问题描述
我一直试图解决一个问题几个小时并坚持下去。 以下是问题大纲:
import numpy as np
import pandas as pd
df = pd.DataFrame({'orderid': [10315, 10318, 10321, 10473, 10621, 10253, 10541, 10645],
          'customerid': ['ISLAT', 'ISLAT', 'ISLAT', 'ISLAT', 'ISLAT', 'HANAR', 'HANAR', 'HANAR'],
          'orderdate': ['1996-09-26', '1996-10-01', '1996-10-03', '1997-03-13', '1997-08-05', '1996-07-10', '1997-05-19', '1997-08-26']})
df
   orderid customerid   orderdate
0    10315      ISLAT  1996-09-26
1    10318      ISLAT  1996-10-01
2    10321      ISLAT  1996-10-03
3    10473      ISLAT  1997-03-13
4    10621      ISLAT  1997-08-05
5    10253      HANAR  1996-07-10
6    10541      HANAR  1997-05-19
7    10645      HANAR  1997-08-26
我想选择在 5 天内多次订购商品的所有客户。
例如,这里只有客户在 5 天内订购了并且他已经完成了两次。
我想获得以下格式的输出:
所需输出
customerid  initial_order_id    initial_order_date  nextorderid nextorderdate   daysbetween
ISLAT       10315               1996-09-26          10318       1996-10-01      5
ISLAT       10318               1996-10-01          10321       1996-10-03      2
1楼
首先,为了能够计算天数差异,将orderdate列转换为datetime :
df.orderdate = pd.to_datetime(df.orderdate)
然后定义如下函数:
def fn(grp):
    return grp[(grp.orderdate.shift(-1) - grp.orderdate) / np.timedelta64(1, 'D') <= 5]
最后应用它:
df.sort_values(['customerid', 'orderdate']).groupby('customerid').apply(fn)
2楼
    您可以使用sort_values和diff创建列“daysbetween”。 
    获得以下顺序后,您可以join df 与 df join一次groupby每个 customerid 并shift所有数据。 
    最后, query满足 'daysbetween_next ' 中的天数的地方:
df['daysbetween'] = df.sort_values(['customerid', 'orderdate'])['orderdate'].diff().dt.days
df_final = df.join(df.groupby('customerid').shift(-1), 
                   lsuffix='_initial', rsuffix='_next')\
             .drop('daysbetween_initial', axis=1)\
             .query('daysbetween_next <= 5 and daysbetween_next >=0')
3楼
    这有点棘手,因为在 5 天窗口内可以有任意数量的购买对。 
    这是利用merge_asof一个很好的用例,它允许对数据帧与其自身进行近似但不精确的匹配。
输入数据
import pandas as pd
df = pd.DataFrame({'orderid': [10315, 10318, 10321, 10473, 10621, 10253, 10541, 10645],
          'customerid': ['ISLAT', 'ISLAT', 'ISLAT', 'ISLAT', 'ISLAT', 'HANAR', 'HANAR', 'HANAR'],
          'orderdate': ['1996-09-26', '1996-10-01', '1996-10-03', '1997-03-13', '1997-08-05', '1996-07-10', '1997-05-19', '1997-08-26']})
定义一个计算购买对的函数,给定客户的数据。
def compute_purchase_pairs(df):
    # Approximate self join on the date, but not exact.
    df_combined = pd.merge_asof(df,df, left_index=True, right_index=True,
                                suffixes=('_first', '_second') , allow_exact_matches=False)
    # Compute difference
    df_combined['timedelta'] = df_combined['orderdate_first'] - df_combined['orderdate_second']
    return df_combined
进行预处理并计算对
# Convert to datetime
df['orderdate'] = pd.to_datetime(df['orderdate'])
# Sort dataframe from last buy to newest (groupby will not change this order)
df2 = df.sort_values(by='orderdate', ascending=False)
# Create an index for joining
df2 = df.set_index('orderdate', drop=False)
# Compute puchases pairs for each customer
df_differences = df2.groupby('customerid').apply(compute_purchase_pairs)
# Show only the ones we care about
result = df_differences[df_differences['timedelta'].dt.days<=5]
result.reset_index(drop=True)
结果
   orderid_first customerid_first orderdate_first  orderid_second  \
0          10318            ISLAT      1996-10-01         10315.0   
1          10321            ISLAT      1996-10-03         10318.0   
  customerid_second orderdate_second timedelta  
0             ISLAT       1996-09-26    5 days  
1             ISLAT       1996-10-01    2 days  
4楼
这很简单。 让我们写下当时的需求,并尝试以此为基础。
首先,我猜客户有一个唯一的 id,因为它没有被指定。 我们将使用该 ID 来识别客户。
其次,我认为客户是在 5 天前还是后 5 天购买都没有关系。
我的解决方案是使用一个简单的过滤器。 请注意,此解决方案也可以在 SQL 数据库中实现。
作为一个条件,我们要求用户是相同的。 我们可以通过以下方式实现:
new_df = df[df["ID"] == df["ID"].shift(1)]
我们创建一个新的 DataFrame,即 new_df,所有行都使得第 x 行与第 x - 1 行(即前一行)具有相同的用户 ID。
现在,让我们通过在前一段代码中添加条件来搜索 5 天内的购买
new_df = df[df["ID"] == df["ID"].shift(1) & (df["Date"] - df["Date"].shift(1)) <= 5]
这应该可以完成工作。 我现在不能测试它写,所以可能需要一些修复。 我会尽快尝试测试