首页 > 解决方案 > Pandas:在不同列上给定一个参考值,对列进行行操作

问题描述

我正在使用如下所示的数据库。对于每种水果(为简洁起见,以下仅是苹果和梨),我们有:1. 年销售额,2. 当前销售额,3. 月销售额和 4. 销售额的标准差。它们的顺序可能会有所不同,但每个水果总是有 4 个值。

dataset = {'apple_yearly_avg': [57],
           'apple_sales': [100],
           'apple_monthly_avg':[80],
           'apple_st_dev': [12],
           'pears_monthly_avg': [33],
           'pears_yearly_avg': [35],
           'pears_sales': [40],
           'pears_st_dev':[8]}

df = pd.DataFrame(dataset).T#tranpose 
df = df.reset_index()#clear index
df.columns = (['Description', 'Value'])#name 2 columns

我想执行两组操作。

对于第一组操作,我们分离出一个水果价格,比如“梨”,然后从当前销售额中减去每个平均销售额。

 df_pear = df[df.loc[:, 'Description'].str.contains('pear')]
 df_pear['temp'] = df_pear['Value'].where(df_pear.Description.str.contains('sales')).bfill()  
 df_pear ['some_op'] = df_pear['Value'] - df_pear['temp'] 

上述工作通过创建一个临时列来保存 pear_sales 为 40,回填它,然后用它来减去值。
问题1:有没有更简洁的方法可以在没有临时数组的情况下执行此操作?此外,我确实得到了一个常见的警告,说我应该使用 '.loc[row_indexer, col_indexer],即使输出仍然有效。

对于第二组操作,我需要将等于“new_purchases”的“5”行添加到数据框的底部,然后用 sales * (1 + std_dev *some_multiplier) 填充 df_pear['some_op']。

df_pear['temp2'] = df_pear['Value'].where(df_pear['Description'].str.contains('st_dev')).bfill()
new_purchases = 5
for i in range(new_purchases):
    df_pear = df_pear.append(df_pear.iloc[-1])#appends 5 copies of the last row

counter = 1
for i in range(len(df_pear)-1, len(df_pear)-new_purchases, -1):#backward loop from the bottom
    df_pear.some_op.iloc[i] = df_pear['temp'].iloc[0] * (1 + df_pear['temp2'].iloc[i] * counter)
    counter += 1

在此处输入图像描述 这个“向后”循环实现了它,但是我再次担心可读性,因为创建了另一个临时列,然后索引相当难看?

谢谢你。

标签: pythonpandas

解决方案


我认为,有一种更清洁的方法可以一次性完成这两项任务:

  1. 添加 2 列FruitDescr ,在第一个“_”处拆分Description的结果:

    df[['Fruit', 'Descr']] = df['Description'].str.split('_', n=1, expand=True)
    

    要查看结果,您现在可以打印df

  2. 定义以下函数来“重新格式化”当前组:

    def reformat(grp):
        wrk = grp.set_index('Descr')
        sal = wrk.at['sales', 'Value']
        dev = wrk.at['st_dev', 'Value']
        avg = wrk.at['yearly_avg', 'Value']
        # Subtract (yearly) average
        wrk['some_op'] = wrk.Value - avg
        # New rows
        wrk2 = pd.DataFrame([wrk.loc['st_dev']] * 5).assign(
            some_op=[ sal * (1 + dev * i) for i in range(5, 0, -1) ])
        return pd.concat([wrk, wrk2])  # Old and new rows
    
  3. 将此函数应用于按Fruit分组的每个组,删除Fruit 列并将结果保存回df

    df = df.groupby('Fruit').apply(reformat)\
        .reset_index(drop=True).drop(columns='Fruit')
    

现在,当你print(df),结果是:

          Description  Value  some_op
0    apple_yearly_avg     57        0
1         apple_sales    100       43
2   apple_monthly_avg     80       23
3        apple_st_dev     12      -45
4        apple_st_dev     12     6100
5        apple_st_dev     12     4900
6        apple_st_dev     12     3700
7        apple_st_dev     12     2500
8        apple_st_dev     12     1300
9   pears_monthly_avg     33       -2
10        pears_sales     40        5
11   pears_yearly_avg     35        0
12       pears_st_dev      8      -27
13       pears_st_dev      8     1640
14       pears_st_dev      8     1320
15       pears_st_dev      8     1000
16       pears_st_dev      8      680
17       pears_st_dev      8      360

编辑

我怀疑描述是否也应该从“st_dev”行复制到新行。如果您想要那里的其他内容,请在创建wrk2 后将其设置为重新格式化功能。


推荐阅读