AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79072552
Accepted
orange
orange
Asked: 2024-10-10 10:25:48 +0800 CST2024-10-10 10:25:48 +0800 CST 2024-10-10 10:25:48 +0800 CST

Soma cumulativa com substituições na condição

  • 772

Estou tentando calcular o saldo (o nível) de um inventário ao longo do tempo e ter quantidades de entrada e saída como entrada (e uma categoria para cada tipo de inventário). Normalmente eu calcularia incoming - outgoinge transportaria para o próximo período (soma cumulativa), mas neste caso uma dificuldade adicional é que o saldo pode ser substituído em vários pontos no tempo, o que "redefine" o saldo para esses valores (e entradas/saídas precisam ser adicionadas a essas substituições deste ponto no tempo em diante).

Eu descobri uma maneira de calcular isso compensando o saldo calculado (=cumsum(entrada-saída)) quando há um saldo de substituição (pelo cumsum calculado negativo; ou seja, definindo o inventário como 0 quando há um saldo de substituição), mas isso não funciona quando há várias substituições em momentos diferentes.

Esta é minha abordagem atual que funciona bem para o dataframe fornecido (=apenas uma substituição ( bal) por categoria ( cat)).

>>> df = pd.DataFrame({
...   'cat': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'a', 'b'],
...   'time': [1, 2, 1, 2, 4, 5, 6, 7, 8, 9],
...   'in': [None, 10, None, None, None, 20, 11, 9, 10, None],
...   'out': [10, None, None, 20, 10, 5, None, 30, None, None],
...   'bal': [None, None, None, None, 50, None, None, None, None, None]
                                       ^ at this time, the balance should be set to 50, irrespective of prior `in` and `out`.
... })
>>> 
>>> # cumsum goes by row, so order matters
>>> df = df.sort_values(by=['time'])
>>> df
  cat  time    in   out   bal
0   a     1   NaN  10.0   NaN
2   b     1   NaN   NaN   NaN
1   a     2  10.0   NaN   NaN
3   b     2   NaN  20.0   NaN
4   a     4   NaN  10.0  50.0
5   a     5  20.0   5.0   NaN
6   a     6  11.0   NaN   NaN
7   a     7   9.0  30.0   NaN
8   a     8  10.0   NaN   NaN
9   b     9   NaN   NaN   NaN
>>> 
>>> 
>>> # Calculate the balance as if 'bal' (the override) wasn't there (cumsum(in - out))
>>> df['inout'] = df['in'].fillna(0) - df['out'].fillna(0)
>>> df['cumsum'] = df[['cat', 'inout']].groupby(['cat']).cumsum()
>>> df
  cat  time    in   out   bal  inout  cumsum
0   a     1   NaN  10.0   NaN  -10.0   -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0
1   a     2  10.0   NaN   NaN   10.0     0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0   <-- we want to override this with the value from 'bal' (50) and continue the calculation
5   a     5  20.0   5.0   NaN   15.0     5.0
6   a     6  11.0   NaN   NaN   11.0    16.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0
8   a     8  10.0   NaN   NaN   10.0     5.0
9   b     9   NaN   NaN   NaN    0.0   -20.0
>>> 
>>> # Find the positions where a balance would override the calculated balance
>>> df['correction'] = -df.loc[pd.notnull(df['bal']), 'cumsum']
>>> df
  cat  time    in   out   bal  inout  cumsum  correction
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN
>>> 
>>> 
>>> # Calculate with the corrected balance
>>> df['inout2'] = df['in'].fillna(0) - df['out'].fillna(0) + df['bal'].fillna(0) + df['correction'].fillna(0)
>>> df['cumsum2'] = df[['cat', 'inout2']].groupby(['cat']).cumsum()
>>> df
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN     0.0      0.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN   -20.0    -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0 (override from 'bal')
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0 <--- 50 (override) +15 (in-out)
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN   -21.0     55.0
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0     65.0
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN     0.0    -20.0
>>> 
>>> 
>>> df[df['cat'] == 'a']
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN   -21.0     55.0
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0     65.0
 

Isso parece bom. No índice 4, o cálculo de saldo simples é substituído (era -10, agora é 50, como esperado) e os fluxos de entrada e saída do período subsequente são adicionados, como esperado.

Entretanto, quando introduzo outra substituição, o algoritmo acima quebra.

df = pd.DataFrame({
  'cat': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'a', 'b'],
  'time': [1, 2, 1, 2, 4, 5, 6, 7, 8, 9],
  'in': [None, 10, None, None, None, 20, 11, 9, 10, None],
  'out': [10, None, None, 20, 10, 5, None, 30, None, None],
  'bal': [None, None, None, None, 50, None, None, 30, None, None]
  #                                                ^
})

... same pipeline as before

>>> df
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN     0.0      0.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN   -20.0    -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0  # still ok
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0  30.0  -21.0    -5.0         5.0    14.0     90.0  # expect 30
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0    100.0  # expect 30 + 10 = 40
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN     0.0    -20.0

Gostaria de modificar o algoritmo para manter a simplicidade de uso cumsum(funcional), mas não consigo descobrir como proceder. É quase como se eu precisasse de um cumsum condicional que substitua os valores intermediários quando uma condição for atendida (nesse caso, um valor em bal). No entanto, eu preferiria calcular mais uma coluna de correção (ou consertar a existente) e adicioná-la (mas bati em uma parede, pois provavelmente olhei para ela por muito tempo). Qualquer ajuda é muito apreciada.

python
  • 1 1 respostas
  • 47 Views

1 respostas

  • Voted
  1. Best Answer
    Panda Kim
    2024-10-10T10:55:50+08:002024-10-10T10:55:50+08:00

    Código

    cond = df['bal'].notna()
    df['cumsum2'] = (
        df['in'].fillna(0).sub(df['out'].fillna(0)).mask(cond, df['bal'])
        .groupby([df['cat'], cond.groupby(df['cat']).cumsum()]).cumsum()
    )
    

    df(seu primeiro exemplo)

      cat  time    in   out   bal  cumsum2
    0   a     1   NaN  10.0   NaN    -10.0
    1   a     2  10.0   NaN   NaN      0.0
    2   b     1   NaN   NaN   NaN      0.0
    3   b     2   NaN  20.0   NaN    -20.0
    4   a     4   NaN  10.0  50.0     50.0
    5   a     5  20.0   5.0   NaN     65.0
    6   a     6  11.0   NaN   NaN     76.0
    7   a     7   9.0  30.0   NaN     55.0
    8   a     8  10.0   NaN   NaN     65.0
    9   b     9   NaN   NaN   NaN    -20.0
    

    df(seu segundo exemplo)

      cat  time    in   out   bal  cumsum2
    0   a     1   NaN  10.0   NaN    -10.0
    1   a     2  10.0   NaN   NaN      0.0
    2   b     1   NaN   NaN   NaN      0.0
    3   b     2   NaN  20.0   NaN    -20.0
    4   a     4   NaN  10.0  50.0     50.0
    5   a     5  20.0   5.0   NaN     65.0
    6   a     6  11.0   NaN   NaN     76.0
    7   a     7   9.0  30.0  30.0     30.0
    8   a     8  10.0   NaN   NaN     40.0
    9   b     9   NaN   NaN   NaN    -20.0
    
    • 1

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve