循环通过过滤的数据框以查看值是不是在列表列中

Posted

技术标签:

【中文标题】循环通过过滤的数据框以查看值是不是在列表列中【英文标题】:Looping through a filtered dataframe to see if a value is in a list column循环通过过滤的数据框以查看值是否在列表列中 【发布时间】:2022-01-17 18:02:59 【问题描述】:

抱歉标题含糊,我不完全确定如何更准确地用词。我有一个这样的数据框:

    date     customerID saved purchased savedProduct    purchasedProduct
0   2021-01-01  456789    1       0       11223344            [0]
1   2021-01-01  456789    1       0       55667788            [0]
2   2021-01-03  456789    0       1          0       [11223344, 28373827]

这是用这个创建的:

d = 'date': ['2021-01-01', '2021-01-01', '2021-01-03'], 'customerID': ['456789', '456789', '456789'], 'saved':[1, 1, 0], 'purchased': [0, 0, 1], 'savedProduct': [11223344, 55667788, 0], 'purchasedProduct': [[0], [0], [11223344, 28373827]]

df = pd.DataFrame(data=d)

其背后的逻辑是每一行都是一个客户记录:他们一次只能保存一个产品(这就是 savedProduct 有一个产品代码的原因)但他们可以购买多个产品,这就是为什么 purchaseProduct 包含一个列表。我想做的是:

通过 customerID,在 savedProduct 中获取唯一的 productID 通过此列中的唯一产品ID,查看它们是否出现在购买的产品中 如果它们出现,请从显示 purchaseProduct 的行中拉出日期列,以便我可以计算 savedProduct 和 purchaseProduct 之间的天数

因此,例如,第 1 行中的产品出现在第 3 行中,因此最好有一种方法在同一行,因此我们可以计算日期之间的差异。

我认为嵌套循环可以完成这项工作,但我无法让它工作(并且必须有更有效的方法..):

    dateDF = pd.DataFrame('customerID': ['0'],
                          'savedDate': ['0'],
                          'purchasedDate': ['0'])
    
    dateDF_t = pd.DataFrame()
    
    sp = []
    for x in df['customerID'].unique():
      customerID = x
      sp = df[df['customerID'] == x]['savedProduct'].unique()
      for i in sp:
        for idx, n in enumerate(df[df['customerID'] == x]['purchasedProduct']):
          if i in n and i != 0:
            print(df[df['customerID'] == x].iloc[idx, 1])
            dateDF_t['customerID'] = df[df['customerID'] == x].iloc[idx, 1]
            dateDF_t['savedDate'] = df[(df['customerID'] == x) & (df['savedProduct'] == i)]['date']
            dateDF_t['purchasedDate'] = df[df['customerID'] == x].iloc[idx, 0]
            dateDF = pd.concat([dateDF, dateDF_t])

但是输出是这样的:

customerID  savedDate   purchasedDate
0   0          0             0
0   NaN      2021-01-01   2021-01-03

有什么方法可以更好地做到这一点,而且为什么 customerID 会产生 NaN?当我有输出(循环中的打印)时,它工作正常

感谢您的帮助!

编辑 - 可能只是使用列表解决了这个问题,但如果有人有更有效的方法,我们仍然会感激不尽!

sp = []
customerIDs = []
savedDates = []
purchasedDates = []
for x in df['customerID'].unique():
  sp = df[df['customerID'] == x]['savedProduct'].unique()
  for i in sp:
    for idx, n in enumerate(df[df['customerID'] == x]['purchasedProduct']):
      if i in n and i != 0:
        customerIDs.append(df[df['customerID'] == x].iloc[idx, 1])
        savedDates.append(df[(df['customerID'] == x) & (df['savedProduct'] == i)]['date'].values[0])
        purchasedDates.append(df[df['customerID'] == x].iloc[idx, 0])
  savedDF = pd.DataFrame('customerID': customerIDs,
             'savedDates': savedDates,
             'purchasedDates': purchasedDates)

输出如下:

customerID  savedDates  purchasedDates
  456789    2021-01-01  2021-01-03
  2727228   2021-02-05  2021-02-09

【问题讨论】:

你能添加一个预期的输出吗? 已编辑谢谢保罗 我添加了一个答案,它比您自己要求的输出稍微复杂一些,但是从我的结果中您当然可以根据自己的喜好对其进行切片和切块。 顺便说一句,也许可以阅读一下这个答案:***.com/questions/16476924/… 作为 Pandas 的初级用户,我发现它非常好 【参考方案1】:

试试:

df=df.explode('purchasedProduct').reset_index(drop=True)
df['purchase_date'] = df.groupby('customerID').apply(
    lambda df: df.apply(
        lambda x: np.nan if x.savedProduct == 0 else df.loc[df.purchasedProduct == x.savedProduct, 'date'], axis=1))

这将首先分解购买产品中包含列表的行,因此它为列表中的每个项目创建一个单独的行。 然后它会添加一个购买日期列,这样您就可以在行级别确定产品是否以及何时购买。

date        customerID  saved   purchased   savedProduct    purchasedProduct    purchase_date
2021-01-01  456789      1       0           11223344        0                   2021-01-03
2021-01-01  456789      1       0           55667788        0                   NaN
2021-01-03  456789      0       1           0               11223344            NaN
2021-01-03  456789      0       1           0               28373827            NaN

当然,您可以过滤 df 以仅包含保存产品的行:

df.loc[df.saved==1]

date        customerID  saved   purchased   savedProduct    purchasedProduct    purchase_date
2021-01-01  456789      1       0           11223344        0                   2021-01-03
2021-01-01  456789      1       0           55667788        0                   NaN

或者只有某些列:

df.loc[df.saved==1, ['customerID', 'savedProduct', 'date',`'purchase_date']]

customerID  savedProduct    date        purchase_date
456789      11223344        2021-01-01  2021-01-03
456789      55667788        2021-01-01  NaN

【讨论】:

以上是关于循环通过过滤的数据框以查看值是不是在列表列中的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 数据框以列中的唯一值作为键,嵌套列表作为值

循环遍历 Pandas 数据框以填充列表(Python)

循环遍历数据框以消除数据中的巨大跳跃的最快方法

循环遍历数据框以更改列值-python [重复]

在循环中创建多个循环的数据框以进行半正弦地理定位

迭代循环并将列表添加到新行或新列中的数据框