【问题标题】:Python selecting row from second dataframe based on complex criteriaPython根据复杂标准从第二个数据框中选择行
【发布时间】:2017-12-10 02:42:03
【问题描述】:

我有两个数据框,一个带有一些购买数据,一个带有周历,例如

df1:
purchased_at  product_id  cost
01-01-2017    1           £10
01-01-2017    2           £8
09-01-2017    1           £10
18-01-2017    3           £12

df2:
week_no  week_start  week_end
1        31-12-2016  06-01-2017
2        07-01-2017  13-01-2017
3        14-01-2017  20-01-2017

我想使用两者中的数据向 df1 添加一个“week_no”列,该列是根据 df1 中的“purchased_at”日期位于 df2 中的“week_start”和“week_end”日期之间的位置从 df2 中选择的,即

df1:
purchased_at  product_id  cost  week_no
01-01-2017    1           £10   1
01-01-2017    2           £8    1
09-01-2017    1           £10   2
18-01-2017    3           £12   3

我已经搜索过,但我无法找到使用两者之间的比较从第二个数据帧中提取数据的示例,并且我无法正确应用我找到的任何示例,例如

df1.loc[(df1['purchased_at'] < df2['week_end']) & 
        (df1['purchased_at'] > df2['week_start']), df2['week_no']

不成功,出现 ValueError 'can only compare same-labeled Series objects'

任何人都可以帮助解决这个问题,或者如果有更好的方法可以达到相同的结果,我愿意接受建议。

编辑以添加 df1 的更多细节

df1 完整的数据帧头

purchased_at  purchase_id  product_id  product_name  transaction_id  account_number  cost
01-01-2017    1            1           A             1               AA001           £10
01-01-2017    2            2           B             1               AA001           £8
02-01-2017    3            1           A             2               AA008           £10
03-01-2017    4            3           C             3               AB040           £12
...  
09-01-2017   12            1           A             10              AB102           £10
09-01-2017   13            2           B             11              AB102           £8
...
18-01-2017   20            3           C             15              AA001           £12

所以purchase_id随着每一行递增,product_id和product_name是1:1的关系,transaction_id也递增递增,但是一个事务中可以有多次购买。

【问题讨论】:

  • 解析df1 的日期就足够了,因为您使用几周的定义似乎是标准。所以,看看this,暂时忘记df2
  • 在您的查询中,数据框(通常)将具有完全不同的形状。您需要在每个数据框中构造一个键,您可以在该键上join on(尝试 df1 中的一周开始日)。连同上面的评论,这应该暗示一个解决方案
  • 这不是标准定义,因为计数将在未来几年继续,所以明年将是第 53-104 周等等,这就是为什么我想单独加入而不是计算它来自一个内置的公式。

标签: python pandas numpy dataframe


【解决方案1】:

如果你的数据框太大,你可以使用这个技巧。

对所有记录进行完整的笛卡尔积连接:

df_out = pd.merge(df1.assign(key=1),df2.assign(key=1),on='key')

接下来在这种情况下过滤掉那些不符合条件的记录,其中购买_at 不在 week_start 和 week_end 之间

(df_out.query('week_start < purchased_at < week_end')
       .drop(['key','week_start','week_end'], axis=1))

输出:

   purchased_at  product_id cost  week_no 
0    2017-01-01           1  £10        1 
3    2017-01-01           2   £8        1 
7    2017-01-09           1  £10        2 
11   2017-01-18           3  £12        3 

如果您确实有大型数据框,那么您可以使用 PiRSquared 建议的 numpy method

a = df1.purchased_at.values

bh = df2.week_end.values

bl = df2.week_start.values

i, j = np.where((a[:, None] >= bl) & (a[:, None] <= bh))

pd.DataFrame(
    np.column_stack([df1.values[i], df2.values[j]]),
    columns=df1.columns.append(df2.columns)
).drop(['week_start','week_end'],axis=1)

输出:

          purchased_at product_id cost week_no
0  2017-01-01 00:00:00          1  £10       1
1  2017-01-01 00:00:00          2   £8       1
2  2017-01-09 00:00:00          1  £10       2
3  2017-01-18 00:00:00          3  £12       3

【讨论】:

  • numpy 方法看起来非常有用,但是完整的“week_no”输出为每个“购买日期”复制,即,我有 16 行,而不是上面的输出:购买的产品 ID .. .week_no 2017-01-01 1 1 2017-01-01 1 1 2017-01-01 1 2 2017-01-01 1 3 2017-01-01 2 1 2017-01-01 2 1 ... 我的代码看起来为了与您的示例相匹配,您对哪里出了问题有任何想法吗?
  • 抱歉,没有说清楚。总而言之,“purchased_at”、“product_id”和“cost”行分别复制了四次,week_no 输出为 [1,1,2,3,1,1,2,3,1,1,2, 3,1,1,2,3]
  • Sarah... 你将不得不向我提供数据和预期输出,以便我进行故障排除。我唯一的猜测是,也许我们正在使用我们的连接创建一个 cartisan 产品,并且需要在代码中添加一个额外的约束,例如 product_id。
  • Scott,感谢您迄今为止的评论和帮助。我已经编辑了我的原始帖子以添加数据框 df1 的所有标题。 df2 与最初指定的一样。预期的输出仍然是根据 purchase_date 在 df1 中添加一个具有相应 week_no 的列。如果您还有其他需要,请告诉我。
【解决方案2】:

您可以使用time.strftime() 从日期中提取周数。如果您想继续向上计算周数,则需要将“零年”定义为时间序列的开始,并相应地偏移 week_no:

import pandas as pd

data = {'purchased_at': ['01-01-2017', '01-01-2017', '09-01-2017', '18-01-2017'], 'product_id': [1,2,1,3], 'cost':['£10', '£8', '£10', '£12']}

df = pd.DataFrame(data, columns=['purchased_at', 'product_id', 'cost'])

def getWeekNo(date, year0):
    datetime = pd.to_datetime(date, dayfirst=True)
    year = int(datetime.strftime('%Y'))
    weekNo = int(datetime.strftime('%U'))
    return weekNo + 52*(year-year0)

df['week_no'] = df.purchased_at.apply(lambda x: getWeekNo(x, 2017))

在这里,我使用pd.to_dateime() 将日期字符串从 df 转换为日期时间对象。 strftime('%Y') 返回年份和 strftime('%U') 星期(一年的第一周从第一个星期日开始。如果星期应该从星期一开始,请改用 '%W')。

这样,您无需仅为周数维护单独的 DataFrame。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-19
    • 1970-01-01
    • 2018-01-23
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    • 1970-01-01
    相关资源
    最近更新 更多