在数据帧上分组后,正片和负片会相互抵消,因此如何成对过滤正片和负片?

2024-06-10 03:26:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个数据帧,如下所示。我在数据帧的3列上分组:

  1. Cat1(Cat1的值可以是例如:A、B、C)
  2. Cat2(Cat2的值可以是例如:N,M,O)
  3. AbsOfTranAmt-AbsOfTranAmtTranAmt的绝对值

在对Cat1Cat2AbsOfTranAmt进行分组后,下面的数据帧中有3个组。 我需要返回在TranAmt中加起来不为零的记录(这是美元金额列)。删除取消的记录

Cat1 Cat2 AbsOfTranAmt TranAmt
  A   N     10           10
  A   N     10          -10
  A   N     10           10
  A   N     20           20
  A   N     20          -20
  A   N     30          -30
  A   N     30           30
  A   N     30          -30
  A   N     30          -30
  A   N     30          -30

输出数据帧应为:

Cat1 Cat2 AbsOfTranAmt TranAmt
  A   N     10           10
  A   N     30          -30
  A   N     30          -30
  A   N     30          -30

这是另一个示例数据:

Cat1  Cat2 AbsOfTranAmt TranAmt  
 A  N   25  25  
 A  N   25  25  
 A  N   25  25  
 A  P   25  -25  
 A  P   25  -25  
 A  P   25  25  
 A  P   25  -25  
 A  P   25  25  
 A  P   25  25  
 A  O   25  25  
 A  N   25  -25  

Tags: 数据示例pandas记录金额cat1cat2tranamt
2条回答

我试图避免过多的迭代,但最后需要一些迭代,尽管可能有更好的方法来实现这一点(df包含初始示例数据):

summary = df.groupby(['Cat1','Cat2','TranAmt']).count().reset_index()

summary['Count'] = summary.apply(lambda row: row['AbsofTranAmt'] if row['TranAmt'] > 0 else row['AbsofTranAmt'] * -1,axis=1)

summary['AbsofTranAmt'] = summary.apply(lambda row: row['TranAmt'] if row['TranAmt'] > 0 else row['TranAmt'] * -1,axis=1)

summary = summary.groupby(['Cat1','Cat2','AbsofTranAmt']).sum().reset_index()

summary['TranAmt'] = summary.apply(lambda row: row['AbsofTranAmt'] if row['Count'] > 0 else row['AbsofTranAmt'] * -1,axis=1)

print(summary)

这会给你

  Cat1 Cat2  AbsofTranAmt  TranAmt  Count
0    A    N            10       10      1
2    A    N            30      -30     -3

然后,您可以使用它重新创建生成的数据帧

results = []

for r in summary.to_dict('records'):
    count = abs(r.pop('Count'))
    results += [r]*count

result_df = pd.DataFrame(results)

print(result_df)

给你:

 AbsofTranAmt Cat1 Cat2  TranAmt
0            10    A    N       10
1            30    A    N      -30
2            30    A    N      -30
3            30    A    N      -30

其思想是“计算”每三个[Cat1、Cat2、abs(TranAmt)]三元组的交易数量。正交易增加计数器,负交易减少计数器

假设数据存储在名为df的数据帧中:

df = pd.DataFrame({'Cat1':['A']*10,
                   'Cat2':['N']*10,
                   'AbsOfTranAmt':[10,10,10,20,20,30,30,30,30,30],
                   'TranAmt':[10,-10,10,20,-20,-30,30,-30,-30,-30]})

解决方案1:使用字典

定义返回数字符号的函数非常有用:

def sign(x):
    return (x>0) - (x<0)

“盘点”交易:

counts = {}
for _,trans in df.iterrows():
    key = (trans['Cat1'],trans['Cat2'],abs(trans['TranAmt']))
    counts[key] = counts.get(key,0) + sign(trans['TranAmt'])

将结果强制转换回原始格式:

# get list of unmatched transactions
unmatched = []
for k,v in counts.items():
    unmatched += [(k[0],k[1],sign(v)*k[2])]*abs(v)

# cast to DataFrame and create AbsOfTranAmt
df_unmatched = pd.DataFrame(unmatched,columns=['Cat1','Cat2','TranAmt'])
df_unmatched['AbsOfTranAmt'] = df_unmatched['TranAmt'].abs()

注意,我故意选择不使用df['AbsOfTranAmt']中的值。此列对我来说似乎是多余的,因为它不包含df['TranAmt']之外的任何信息

解决方案2:使用groupby

我们再次需要一个符号函数,这次是一个系列:

def sign(series):
    return (series > 0).astype(int) - (series < 0).astype(int)

“盘点”交易:

df['count'] = sign(df['TranAmt'])
counts = df.groupby(['Cat1','Cat2','AbsOfTranAmt'])['count'].sum()

将结果强制转换回原始格式:

# get dataframe of unmatched transactions
df_unmatched = []
for _,count in counts.reset_index().iterrows():
    df_unmatched += [count]*abs(count['count'])

df_unmatched = pd.concat(df_unmatched,axis=1).T

# bring back to original format
df_unmatched['TranAmt'] = df_unmatched['AbsOfTranAmt']*sign(df_unmatched['count'])
df_unmatched.drop('count',axis=1,inplace=True)

编辑1:添加了遵循相同逻辑的groupby解决方案

编辑2:更改了一些列名

相关问题 更多 >