有效的方法减去前一行用 Pandas 分组分隔的行

Efficient way subtract a row with previous row seperated by group with Pandas

objective是减去一行(N)与前一行(N-1)以组分隔。

给定一个 df

   years nchar  nval
0   2019     a     1
1   2019     b     1
2   2019     c     1
3   2020     a     1
4   2020     s     4

让我们将年份2019分成一组,我们将其表示为df_2019

对于df_2019,我们分配常量10

然后,仅针对索引0,我们进行以下操作并分配给新列'B`

df_2019.loc[df_2019.index[0], 'B']= 10 - df_2019['nval'].values[0]

而另一个索引

df_2019.loc[df_2019.index[N], 'B'] = df_2019['B'].values[N-1] - df_2019['nval'].values[N]

这样,就会产生下面的结果table

   years      nchar nval C   D    B
 
1   2019          a    1          9
2   2019          b    1          8
3   2019          c    1          7

                          

对于组2020,同样的计算适用。然而,唯一的区别是,常量值是 7,它取自 B 列的最后一个索引。

为了满足这个要求,使用额外的可能组生成了以下代码。

import pandas as pd
year=[2019,2019,2019,2020,2020,2020,2020,2022,2022,2022]
nval=[1,1,1,1,4,1,4,5,6,7]
nchar=['a','b','c','a','s','c','a','b','c','g']

df=pd.DataFrame(zip(year,nchar,nval),columns=['years','nchar','nval'])
print(df)
year_ls=[2019,2020,2022]
nspacing_total=2

nspacing_between_df=4
all_df=[]
default_val=10
for idx,dyear in enumerate(year_ls):
  df_=df[df['years']==dyear].reset_index(drop=True)
  t=pd.DataFrame([[''] * 3]*len(df_), columns=["C", "D", "B"])
  df_=pd.concat([df_,t],axis=1)
  Total = df_['nval'].sum()
  df_=pd.DataFrame([[''] * len(df.columns)]*1, columns=df.columns).append(df_).reset_index(drop=True)

  if idx ==0:
    df_.loc[df_.index[0], 'B']=default_val
    

  if idx !=0:
    
    pre_df=all_df[idx-1]
    pre_val=pre_df['B'].values[-1]

    nposi=1

    pre_years=pre_df['years'].values[nposi]
    df_.loc[df_.index[0], 'nchar']=f'From {pre_years}'
    df_.loc[df_.index[0], 'B']=pre_val

  for ndexd in range(df_.shape[0]-1):
    df_.loc[df_.index[ndexd+1], 'B']=df_['B'].values[ndexd]-df_['nval'].values[ndexd+1]
    
  df_=df_.append(pd.DataFrame([[''] * len(df.columns)]*nspacing_total, columns=df.columns)).reset_index(drop=True)
  df_.loc[df_.index[-1], 'nval']=Total
  df_.loc[df_.index[-1], 'nchar']='Total'
  df_.loc[df_.index[-1], 'B']=df_['B'].values[0]-df_['nval'].values[-1]

  all_df.append(df_)

但是,我想知道是否可以使用 pandas groupby 或其他方法进一步简化此提案。我非常感谢任何提示。

最终,我想表达如下table,将导出到excel

   years      nchar nval C   D    B
0                           10     
1   2019          a    1          9
2   2019          b    1          8
3   2019          c    1          7
4                                  
5             Total    3          7
6                                  
7                                  
8                                  
9                                  
10        From 2019               7
11  2020          a    1          6
12  2020          s    4          2
13  2020          c    1          1
14  2020          a    4         -3
15                                 
16            Total   10         -3
17                                 
18                                 
19                                 
20                                 
21        From 2020              -3
22  2022          b    5         -8
23  2022          c    6        -14
24  2022          g    7        -21
25                                 
26            Total   18        -21
27                                 
28                                 
29                                 
30                                 

生成上述内容的代码table

# Optional to represent the table above
all_ap_df=[]
for a_df in all_df:
  df=a_df.append(pd.DataFrame([[''] * len(df.columns)]*nspacing_between_df, columns=df.columns)).reset_index(drop=True)
  all_ap_df.append(df)
df=pd.concat(all_ap_df,axis=0).reset_index(drop=True)


df.loc[df_.index[0], 'D']=df['B'].values[0]
df.loc[df_.index[0], 'B']=''
df = df.fillna('')

我觉得这个其实很简单。使用 groupby + cumsum:

df['B'] = 10 - df['nval'].cumsum()

输出:

>>> df
   years nchar  nval  B
0   2019     a     1  9
1   2019     b     1  8
2   2019     c     1  7
3   2020     a     1  6
4   2020     s     4  2

在您的案例链中 groupby

df['new'] = df.groupby('years')['nval'].cumsum().rsub(10)
Out[8]: 
0    9
1    8
2    7
3    9
4    5
Name: nval, dtype: int64