【问题标题】:Excel SUMIF equivalent in PandasPandas 中的 Excel SUMIF 等效项
【发布时间】:2022-03-12 10:53:02
【问题描述】:
import pandas as pd 
import numpy as np

df = pd.DataFrame([['A', 201901, 10, 201801, 201801], 
                   ['B', 201902, 11, 201801, 201802], 
                   ['B', 201903, 13, 201801, 201803],
                   ['B', 201905, 18, 201801, 201805],
                   ['A', 201906, 80, 201801, 201806],
                   ['A', 202001, 10, 201901, 201901], 
                   ['A', 202002, 11, 201901, 201902], 
                   ['A', 202003, 13, 201901, 201903],
                   ['A', 202004, 18, 201901, 201904],
                   ['B', 202005, 80, 201901, 201905],
                   ['A', 202006, 80, 201901, 201906],
                   ['B', 201901, 10, 201801, 201801], 
                   ['A', 201902, 11, 201801, 201802], 
                   ['A', 201903, 13, 201801, 201803],
                   ['A', 201905, 18, 201801, 201805],
                   ['B', 201906, 80, 201801, 201806],
                   ['B', 202001, 10, 201901, 201901], 
                   ['B', 202002, 11, 201901, 201902], 
                   ['B', 202003, 13, 201901, 201903],
                   ['B', 202004, 18, 201901, 201904],
                   ['A', 202005 ,80, 201901, 201905],
                   ['B', 202006 ,80, 201901, 201906]],
                  columns = ['Store','yearweek','sales','Start_PY','PY'])
df

从上面的df (请注意缺少第 201904 周),我想在每行添加一个列 'Sales_PY',其中包含每家商店前一年的销售额总和。 像这样的:

Store yearweek sales Start_PY PY sales_PY
A 201901 100 201801 201801 NaN
B 201902 11 201801 201802 NaN
B 201903 13 201801 201803 NaN
B 201905 18 201801 201805 NaN
A 201906 800 201801 201806 NaN
A 202001 100 201901 201901 100.0
A 202002 110 201901 201902 210.0
A 202003 130 201901 201903 340.0
A 202004 180 201901 201904 340.0
B 202005 80 201901 201905 52.0
A 202006 800 201901 201906 1320.0
B 201901 10 201801 201801 NaN
A 201902 110 201801 201802 NaN
A 201903 130 201801 201803 NaN
A 201905 180 201801 201805 NaN
B 201906 80 201801 201806 NaN
B 202001 10 201901 201901 10.0
B 202002 11 201901 201902 21.0
B 202003 13 201901 201903 34.0
B 202004 18 201901 201904 34.0
A 202005 800 201901 201905 520.0
B 202006 80 201901 201906 132.0

而且我认为 Pandas 中的 Excel 中必须有一个 SUMIF 等效项。

即最后一行的销售额 PY 将是销售额的总和 WHERE store == 'B' AND yearweek >= 201901 AND yearweek

因为我无法确保我的 df 将按商店/周排列,而且我的 df 有时会缺少几周,所以我不喜欢使用 shift() 和/或 cumsum() 函数。

【问题讨论】:

    标签: python pandas sumifs


    【解决方案1】:

    在 OP 的澄清之后完全替换了答案

    请注意,您编码的 df 与您在表格中打印的 df 不一致。我和桌子上的那个一起去了

    下面不是最优雅的,但考虑到缺少几周等,我想不出更矢量化的操作

    我们基本上实现了非常接近sumif 逻辑的逐行计算。 apply 中的函数应用于每一行 r 对于每一行 r 它选择原始数据帧 df 的相关子集并计算总和

    df['Sales_PY'] = (df.apply(lambda r: df.loc[(df['yearweek'] >= r['Start_PY'])
                                               &(df['yearweek'] <= r['PY']) 
                                               &(df['Store']==r['Store']) ,'sales'].sum(),axis=1)
                    )
    

    输出

        Store      yearweek    sales    Start_PY      PY    Sales_PY
    --  -------  ----------  -------  ----------  ------  ----------
     0  A            201901      100      201801  201801           0
     1  B            201902       11      201801  201802           0
     2  B            201903       13      201801  201803           0
     3  B            201905       18      201801  201805           0
     4  A            201906      800      201801  201806           0
     5  A            202001      100      201901  201901         100
     6  A            202002      110      201901  201902         210
     7  A            202003      130      201901  201903         340
     8  A            202004      180      201901  201904         340
     9  B            202005       80      201901  201905          52
    10  A            202006      800      201901  201906        1320
    11  B            201901       10      201801  201801           0
    12  A            201902      110      201801  201802           0
    13  A            201903      130      201801  201803           0
    14  A            201905      180      201801  201805           0
    15  B            201906       80      201801  201806           0
    16  B            202001       10      201901  201901          10
    17  B            202002       11      201901  201902          21
    18  B            202003       13      201901  201903          34
    19  B            202004       18      201901  201904          34
    20  A            202005      800      201901  201905         520
    21  B            202006       80      201901  201906         132
    

    如果您想要NaNs 而不是没有销售数据的0,您可以在上面的sum 中传递min_count=1 参数:.sum(min_count=1)

    【讨论】:

    • 感谢 Piterbarg!但是,如果缺少几个星期,我怎样才能获得相同的结果呢?即第 6 周不包括在我的 df 中,我仍然想总结第 4-6 周(而不是 3-5)。
    • @BartBeckers 请看我的编辑——这或多或少是你想要的吗?
    • 不完全是,按照这个逻辑,如果例如第 6 行和第 16 行被排除在外,我将得到第 3、4 和 5 周的总和,而我仍然想要第 4 周的总和, 5和6。此外,我的真实df没有(也不会)安排。因此我不喜欢 shift() 函数。
    • @BartBeckers 我必须说,我不太清楚你到底想要达到什么目的。例如,您不需要提前“安排”您的 df,上面的代码将您的 df 按商店分组,按周隐式排序并用 0 填充缺失的周。为了取得进展,我建议您在您的问题中添加一个更具代表性的示例,其中包含缺少行的内容以及预期的输出,以便我们可以从那里获取它
    • 我刚刚编辑了我的问题。请注意,缺少第 201904 周。现在更清楚我想要实现的目标了吗?期待您的回复。
    【解决方案2】:

    您可以将它们按存储分组并将它们向前移动一行,然后再次分组并取累计和。

    import pandas as pd 
    import numpy as np
    
    df = pd.DataFrame([['A', 4, 10, 3, 1], 
                       ['A', 5, 11, 4, 2], 
                       ['A', 6, 13, 5, 3],
                       ['A', 7, 18, 6, 4],
                       ['B', 4 ,80, 3, 1], 
                       ['B', 5, 78, 4, 2], 
                       ['B', 6, 71, 5, 3],
                       ['B', 7, 80, 6, 4]],
                      columns = ['Store','week','sales','week_min_1','week_min_3'])
    
    
    df['sales_last_3_weeks'] = df.groupby('Store')['sales'].shift()
    df['sales_last_3_weeks'] = df.groupby('Store')['sales_last_3_weeks'].cumsum()
    

    【讨论】:

    • 非常感谢您的回复,克里斯。还有没有移位功能的方法,因为当我的数据框排列不正确或我错过了几周时,这会导致问题......此外,如果我每个商店有超过 4 行,那么 sumsum() 函数将求和所有行(不仅是最后 3 行)。期待您的回复。
    【解决方案3】:

    A 店和 B 店的日期似乎是统一的;我们可以使用不等式连接来获取相关行,在合并回原始数据帧之前使用 groupby 对值求和。 pyjanitor 中的 conditional_join 在这里对非 equi 合并很有帮助,我们使用二进制搜索,而不是遍历每一行;根据数据大小,性能可能会有所帮助:

    # pip install pyjanitor
    import janitor
    import pandas as pd
    dates = df.filter(like = 'PY').drop_duplicates()
    left = df.loc[:, :"sales"]
    
    outcome = (
              left.conditional_join(
                  dates,
                  ("yearweek", "Start_PY", ">="),
                  ("yearweek", "PY", "<="),
                  how="right",
              )
              .groupby(["Store", "Start_PY", "PY"])
              .sales.sum()
          )
    
    
    # join back to the original dataframe
    df.merge(
              outcome.rename("Sales_PY"),
              left_on=["Store", "Start_PY", "PY"],
              right_index=True,
              how="left",
          )
    
    
       Store  yearweek  sales  Start_PY      PY  Sales_PY
    0      A    201901    100    201801  201801       NaN
    1      B    201902     11    201801  201802       NaN
    2      B    201903     13    201801  201803       NaN
    3      B    201905     18    201801  201805       NaN
    4      A    201906    800    201801  201806       NaN
    5      A    202001    100    201901  201901     100.0
    6      A    202002    110    201901  201902     210.0
    7      A    202003    130    201901  201903     340.0
    8      A    202004    180    201901  201904     340.0
    9      B    202005     80    201901  201905      52.0
    10     A    202006    800    201901  201906    1320.0
    11     B    201901     10    201801  201801       NaN
    12     A    201902    110    201801  201802       NaN
    13     A    201903    130    201801  201803       NaN
    14     A    201905    180    201801  201805       NaN
    15     B    201906     80    201801  201806       NaN
    16     B    202001     10    201901  201901      10.0
    17     B    202002     11    201901  201902      21.0
    18     B    202003     13    201901  201903      34.0
    19     B    202004     18    201901  201904      34.0
    20     A    202005    800    201901  201905     520.0
    21     B    202006     80    201901  201906     132.0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多