Python基于复杂条件从第二个数据帧中选择行

Python基于复杂条件从第二个数据帧中选择行,python,pandas,numpy,dataframe,Python,Pandas,Numpy,Dataframe,我有两个数据框,一个带有一些采购数据,另一个带有周历,例如 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-20

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

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中的“Purchase_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']

因此,购买id随着每行的增加而递增,产品id和产品名称具有1:1的关系,交易id也会递增,但一个交易中可能有多个购买。

如果数据帧太大,可以使用此技巧

将所有记录与所有记录进行完整的cartisian产品连接:

df_out = pd.merge(df1.assign(key=1),df2.assign(key=1),on='key')
下一步筛选出与本例中的条件不匹配的记录,其中购买的时间不在周开始和周结束之间

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

如果您确实有大数据帧,那么您可以按照PiRSquared的建议使用它

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)
您可以使用从日期中提取周数。如果要继续向上计算周数,需要将“零年”定义为时间序列的开始,并相应地抵消周数:

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中的日期字符串转换为datetime对象
strftime('%Y')
返回年份和
strftime('%U')
返回周(一年中的第一周从第一个星期日开始。如果周应从星期一开始,请改用
'%W'


这样,您就不需要仅为周数维护单独的数据帧。

解析
df1
的日期就足够了,因为您为周使用的定义似乎是标准的。因此,请看一看,暂时忘记
df2
。在您的查询中,数据帧(通常)具有完全不同的形状。您需要在每个数据帧中构造一个键,您可以在该键上执行操作(在df1中尝试每周的开始日)。加上上面的评论,这应该暗示了一个解决方案。这不是标准定义,因为计数将在未来几年继续,所以明年将是第53-104周,以此类推,这就是为什么我想单独加入它,而不是从一个内置的公式计算它。numpy方法看起来非常有用,但是,每个“购买日期”都会复制完整的“周号”输出,也就是说,我有16行代码,而不是上面的输出:购买日期为产品id。。。周号2017-01-01 1 2017-01-01 1 2017-01-01 1 2 2017-01-01 1 3 2017-01-01 2 1 2017-01-01 2 1。。。我的代码看起来与您的示例相匹配,您是否认为这是哪里出了问题?抱歉,这还不清楚。总之,“购买时间”、“产品id”和“成本”行各复制四次,周无输出为[1,1,2,3,1,1,2,3,1,1,2,3,1,1,2,3,1,1,2,1,1,2,3]Sarah。。。您必须向我提供数据和预期输出,以便我进行故障排除。我唯一的猜测是,也许我们正在使用我们的连接创建一个cartisan产品,并且需要在代码中添加一个附加约束,例如product_id。Scott,感谢您迄今为止的评论和帮助。我已经编辑了我的原始帖子,添加了数据帧df1的所有标题。df2与最初规定的相同。预期输出仍然是根据购买日期向df1添加一列,其中包含相应的周号。如果你还需要什么,请告诉我。
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
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))