2017-07-06 36 views
2

我有两个数据框,一个包含一些购买数据,另一个包含每周日历,例如,Python根据复杂标准从第二个数据框中选择行

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 

我要使用的数据来自两个添加“week_no”列于DF1,这是从DF2基于其中在DF1的“purchased_at”日期落在“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异常“只能比较相同标记的Series对象”

任何人都可以帮助解决这个问题,或者我打开的建议,如果有更好的方式来实现同样的结果。

编辑添加的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也会逐步增加,但是在一个事务中可以有多次购买。

+0

解析'df1'的日期应该足够了,因为您在这几个星期使用的定义似乎是*标准*之一。所以,请看看[this](https://stackoverflow.com/questions/2600775/how-to-get-week-number-in-python),而暂时忘掉'df2'。 –

+0

在您的查询中,数据框将(通常)具有完全不同的形状。您需要在每个数据框中构建一个可以[加入]的密钥(https://pandas.pydata.org/pandas-docs/stable/merging.html)(尝试在df1中开始每周的某一天)。与上面的评论一起,这应该暗示一个解决方案 – Quickbeam2k1

+0

这不是一个标准的定义,因为这个数字在未来几年会持续下去,所以明年将是53-104周等等,这就是为什么我想加入它单独而不是从内置公式计算。 – Sarah

回答

0

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

做了充分cartisian产品的所有记录加入到所有记录:

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

下一页过滤掉不在这种情况下,在purchased_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 
+0

numpy方法看起来非常有用,但是对于'purchase at'日期中的每一个都会复制完整的'week_no'输出,即,而不是上面的输出,我有16行: purchased_at product_id ... week_no 2017-01 -01 1 2017年1月1日1 1 2017年1月1日1 2 2017年1月1日1 3 2017年1月1日2 1 2017年1月1日2 1 ... 我代码看起来符合你的例子,你有什么想法,哪里出了问题? – Sarah

+0

道歉,这不是很清楚。总而言之,'purchased_at','product_id'和'cost'行每个被复制四次,week_no输出为[1,1,2,3,1,1,2,3,1,1,2, 3,1,1,2,3] – Sarah

+0

Sarah ...您将不得不为我提供数据和期望的输出,以便排除故障。我唯一的猜测是,也许我们正在使用我们的连接创建cartisan产品,并且需要在代码中添加一个额外的约束条件,例如product_id。 –

0

你可以只使用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()到datestring从转换df到日期时间对象中。 strftime('%Y')返回一周和strftime('%U')(从第一个星期日开始的一年的第一周。如果星期一应该从星期一开始,改用'%W')。

这样,您不需要为一周数量维护一个单独的DataFrame。

相关问题