【问题标题】:Pandas Equivalent for SQL window function and rows rangePandas 等效于 SQL 窗口函数和行范围
【发布时间】:2021-05-03 16:35:45
【问题描述】:

考虑最小的例子

customer   day  purchase
Joe        1       5
Joe        1      10
Joe        2       5
Joe        2       5       
Joe        4      10
Joe        7       5

在 BigQuery 中,可以执行类似的操作来获取客户在过去 2 天内每天花费的金额:

SELECT customer, day
, sum(purchase) OVER (PARTITION BY customer ORDER BY day ASC RANGE between 2 preceding and 1 preceding)
FROM table

熊猫中的等价物是什么?即预期结果

customer   day  purchase    amount_last_2d
Joe        1       5             null  -- spent days [-,-]
Joe        1      10             null  -- spent days [-,-]
Joe        2       5               15  -- spent days [-,1]
Joe        2       5               15  -- spent days [-,1]
Joe        4      10               10  -- spent days [2,3]
Joe        7       5                0  -- spent days [5,6]

【问题讨论】:

  • 你能发布预期的输出吗?
  • 我认为您应该在输入和输出中添加一个 day=3 的行,以便为可能的解决方案提供更好的单元测试。我担心您当前的示例将允许解决方案可以复制您的输出,但不能复制您想要的逻辑。
  • 这是要点之一 - RANGE 函数对日期值进行操作,而不是行号(例如,前两天的总和,而不是前 2 行的总和)
  • 这就是为什么你应该提供一个更完整的例子。我认为根据您当前的输入,您可以吸引似乎适用于这种情况的答案,但不适用于其他情况。这就是为什么有一个简单的示例很重要,该示例可以区分实现您所需逻辑的答案,以及那些接近但仅适用于该特定示例的答案。
  • 我明白你的观点 ALollz,修改了示例,以便澄清目的。

标签: pandas google-bigquery range window-functions


【解决方案1】:

尝试groupbyshift 然后reindex 返回

df['new'] = df.groupby(['customer','day']).purchase.sum().shift().reindex(pd.MultiIndex.from_frame(df[['customer','day']])).values
df
Out[259]: 
  customer  day  purchase   new
0      Joe    1         5   NaN
1      Joe    1        10   NaN
2      Joe    2        10  15.0
3      Joe    2         5  15.0
4      Joe    4        10  15.0

更新

s = df.groupby(['customer','day']).apply(lambda x : df.loc[df.customer.isin(x['customer'].tolist()) & (df.day.isin(x['day']-1)|df.day.isin(x['day']-2)),'purchase'].sum())
df['new'] = s.reindex(pd.MultiIndex.from_frame(df[['customer','day']])).values
df
Out[271]: 
  customer  day  purchase  new
0      Joe    1         5    0
1      Joe    1        10    0
2      Joe    2         5   15
3      Joe    2         5   15
4      Joe    4        10   10
5      Joe    7         5    0

【讨论】:

  • 感谢 Beny,这是一个好主意,但我认为这可以检索之前记录的天数,但不能检索任意滞后时间窗口的总和。我修改了示例以阐明目的。
【解决方案2】:

不确定这是否是正确的方法,因为只提供一个客户,所以这是有限的;如果有不同的客户,我会使用merge 而不是map;另请注意,还有一个隐含的假设,即日期已经按升序排列:

根据customerday的groupby组合得到购买总和,并创建day和总和的映射:

sum_purchase = (df.groupby(["customer", "day"])
                 .purchase
                 .sum()
                 .shift()
                 .droplevel(0))

同样,对于多个客户,我不会删除 customer 索引,而是使用下面的合并:

获取天与天之间的差异的映射:

diff_2_days = (df.drop_duplicates("day")[["day"]]
                 .set_index("day", drop=False)
                 .diff()
                 .day)

通过将上述值映射到 day 列来创建新列,然后使用np.where 获取 diff 小于或等于 2 的列:

(
    df.assign(
        diff_2_days = df.day.map(diff_2_days),
        sum_purchase = df.day.map(sum_purchase),
        final=lambda df: np.where(df.diff_2_days.le(2), 
                                  df.sum_purchase, 
                                  np.nan))
      .drop(columns=["sum_purchase", "diff_2_days"])
)


    customer    day     purchase    final
0       Joe     1             5     NaN
1       Joe     1            10     NaN
2       Joe     2             5     15.0
3       Joe     2             5     15.0
4       Joe     4            10     10.0
5       Joe     7             5     NaN

在 postgres 中运行您的代码以了解范围的作用以及它与行的不同之处;很有见地。我认为对于 Windows 函数,SQL 也很容易做到这一点。

所以,让我知道这到底是怎么回事,我很乐意重新调整它。

【讨论】:

    猜你喜欢
    • 2017-05-25
    • 2023-03-09
    • 2014-07-28
    • 2021-10-02
    • 2022-08-09
    • 2019-03-24
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    相关资源
    最近更新 更多