如何计算每行具有动态条件的 Pandas df 元素 (=countif)
How to count Pandas df elements with dynamic condition per row (=countif)
我很想在 Pandas 中做一些相当于 COUNTIF 的事情。我试图用 groupby
来解决这个问题,但我很挣扎,因为我的逻辑分组条件是动态的。
假设我有一份客户名单,以及他们来访的日期。我想根据 2 个逻辑条件识别新客户
- 他们必须是同一个客户(相同
Guest ID
)
- 他们前一天肯定在那里
如果两个条件都满足,他们就是回头客。如果不是,则它们是新客户(因此 newby = 1-...
以识别新客户。
我设法用 for
循环做到了这一点,但显然性能很糟糕,这与 Pandas 的逻辑相悖。
如何将以下代码包装成比循环更智能的代码?
for i in range (0, len(df)):
newby = 1-np.sum((df["Day"] == df.iloc[i]["Day"]-1) & (df["Guest ID"] == df.iloc[i]["Guest ID"]))
这个 post 没有帮助,因为条件是静态的。我想避免引入“虚拟列”,例如转置 df
,因为我会有很多类别(很多客户名称)并且想构建更复杂的逻辑语句。我不想 运行 有很多辅助列结束的风险
我有以下输入
df
Day Guest ID
0 3230 Tom
1 3230 Peter
2 3231 Tom
3 3232 Peter
4 3232 Peter
并期待这个输出
df
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1
请注意,元素 3
和 4
不一定重复 - 假设可能有其他不同的列(例如它们的顺序)。
做:
# ensure the df is sorted by date
df = df.sort_values('Day')
# group by customer and find the diff within each group
df['newby'] = (df.groupby('Guest ID')['Day'].transform('diff').fillna(2) > 1).astype(int)
print(df)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
更新
如果每天允许多次访问,您可以这样做:
# only keep unique visits per day
uniques = df.drop_duplicates()
# ensure the df is sorted by date
uniques = uniques.sort_values('Day')
# group by customer and find the diff within each group
uniques['newby'] = (uniques.groupby('Guest ID')['Day'].transform('diff').fillna(2) > 1).astype(int)
# merge the uniques visits back into the original df
res = df.merge(uniques, on=['Day', 'Guest ID'])
print(res)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1
作为替代方案,如果不进行排序或合并,您可以这样做:
lookup = {(day + 1, guest) for day, guest in df[['Day', 'Guest ID']].value_counts().to_dict()}
df['newby'] = (~pd.MultiIndex.from_arrays([df['Day'], df['Guest ID']]).isin(lookup)).astype(int)
print(df)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1
我很想在 Pandas 中做一些相当于 COUNTIF 的事情。我试图用 groupby
来解决这个问题,但我很挣扎,因为我的逻辑分组条件是动态的。
假设我有一份客户名单,以及他们来访的日期。我想根据 2 个逻辑条件识别新客户
- 他们必须是同一个客户(相同
Guest ID
) - 他们前一天肯定在那里
如果两个条件都满足,他们就是回头客。如果不是,则它们是新客户(因此 newby = 1-...
以识别新客户。
我设法用 for
循环做到了这一点,但显然性能很糟糕,这与 Pandas 的逻辑相悖。
如何将以下代码包装成比循环更智能的代码?
for i in range (0, len(df)):
newby = 1-np.sum((df["Day"] == df.iloc[i]["Day"]-1) & (df["Guest ID"] == df.iloc[i]["Guest ID"]))
这个 post 没有帮助,因为条件是静态的。我想避免引入“虚拟列”,例如转置 df
,因为我会有很多类别(很多客户名称)并且想构建更复杂的逻辑语句。我不想 运行 有很多辅助列结束的风险
我有以下输入
df
Day Guest ID
0 3230 Tom
1 3230 Peter
2 3231 Tom
3 3232 Peter
4 3232 Peter
并期待这个输出
df
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1
请注意,元素 3
和 4
不一定重复 - 假设可能有其他不同的列(例如它们的顺序)。
做:
# ensure the df is sorted by date
df = df.sort_values('Day')
# group by customer and find the diff within each group
df['newby'] = (df.groupby('Guest ID')['Day'].transform('diff').fillna(2) > 1).astype(int)
print(df)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
更新
如果每天允许多次访问,您可以这样做:
# only keep unique visits per day
uniques = df.drop_duplicates()
# ensure the df is sorted by date
uniques = uniques.sort_values('Day')
# group by customer and find the diff within each group
uniques['newby'] = (uniques.groupby('Guest ID')['Day'].transform('diff').fillna(2) > 1).astype(int)
# merge the uniques visits back into the original df
res = df.merge(uniques, on=['Day', 'Guest ID'])
print(res)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1
作为替代方案,如果不进行排序或合并,您可以这样做:
lookup = {(day + 1, guest) for day, guest in df[['Day', 'Guest ID']].value_counts().to_dict()}
df['newby'] = (~pd.MultiIndex.from_arrays([df['Day'], df['Guest ID']]).isin(lookup)).astype(int)
print(df)
输出
Day Guest ID newby
0 3230 Tom 1
1 3230 Peter 1
2 3231 Tom 0
3 3232 Peter 1
4 3232 Peter 1