我在Python中有以下数据框(按产品商店和周组合排序多个行(已排序))。
product store week visit prob
123 321 1 0 0.003
123 321 2 0 0.234
123 321 3 1 0
123 321 4 0 0.198
123 301 1 0 0.290
123 301 2 2 0
123 301 3 0 0.989
123 301 4 4 0.788
我想根据访问列中前面的零个数找到累计概率。例如:对于每个产品商店周组合,我会发现第一次出现的访问次数> 0。然后计算前面的零的数量。然后乘以 prob 列中的所有行,直到我达到值> 0以及该产品商店组合的最后一周。像下面的东西。访问> 0 cum_prob可以留空或为0。
product store week visit prob cum_prob
123 321 1 0 0.003 0.000702
123 321 2 0 0.234 0.000702
123 321 3 1 0
123 321 4 0 0.198 0.198
123 301 1 0 0.290 0.290
123 301 2 2 0
123 301 3 0 0.989 0.989
123 301 4 4 0.788
我如何在Python中实现这一目标?在SAS中我可以使用数组和一些循环。
答案 0 :(得分:2)
我将创建一个工作数据集d1
并为其指定一些新列。
iszero
跟踪prob
为零的位置。我稍后会乘以此专栏novist
跟踪visit
不为零的位置。我稍后会乘以这个并用它来帮助创建群组filled_prob
填写1
,其中prob
为零。这有助于我的prod
功能以后运行良好。d1 = df.assign(
iszero=df.prob.eq(0),
novisit=df.visit.ne(0),
filled_prob=np.where(df.prob.eq(0), 1, df.prob)
)
d1
我将使用我刚刚创建的一个列来创建分组列
d1['visit_group'] = d1.groupby(['product', 'store']).novisit.cumsum()
d1
最后,添加'cum_prob'
以及我在上面制作的列。
d1['cum_prob'] = d1.groupby(
['product', 'store', 'visit_group']
).filled_prob.transform('prod') * (~d1.iszero) * (~d1.novisit)
d1
您可以根据自己的需要对其进行切片
d1.loc[:, df.columns.tolist() + ['cum_prob']]
所有
d1 = df.assign(
iszero=df.prob.eq(0),
novisit=df.visit.ne(0),
filled_prob=np.where(df.prob.eq(0), 1, df.prob)
)
d1['visit_group'] = d1.groupby(['product', 'store']).novisit.cumsum()
d1['cum_prob'] = d1.groupby(
['product', 'store', 'visit_group']
).filled_prob.transform('prod') * (~d1.iszero) * (~d1.novisit)
d1.loc[:, df.columns.tolist() + ['cum_prob']]
对评论的回应:
周跳过是否会改变计算方式。相反,我们可以像这样预先过滤df
def skip_weeks(x):
"""check if difference in week from one row
to the next is always 1. If not, then we skipped a week"""
return x.week.diff().dropna().eq(1).all()
# I'll use this to map and filter in a bit
no_skips = df.groupby(['product', 'store']).apply(skip_weeks)
# produces
# product store
# 123 301 True
# 321 True
# dtype: bool
# simple series of tuples
# could've done `df[['product', 'store']].apply(tuple, 1)`
# but this is quicker
s = pd.Series(list(zip(df['product'].tolist(), df.store.tolist())), df.index)
# filter, this is what we then use rest of algorithm on
# remember to assign it to a variable like `df = df.loc[s.map(no_skips)]`
df.loc[s.map(no_skips)]
答案 1 :(得分:1)
这是一个解决方案,它将每周分配到一个组中,然后根据该组找到累积总和。
首先要做的是将s.ne(0)
转为0/1。然后是第一个差异,它将为组中的第一行创建-1/1。然后对此采用绝对值的累积和来创建组。然后我们可以简单地使用transform
并获取每个组的产品。
df['group'] = df.groupby(['product', 'store'])['visit']\
.transform(lambda s: s.ne(0).diff().abs().cumsum().fillna(0))
df['cum_prod'] = df.groupby(['product', 'store', 'group'])['prob']\
.transform(lambda s: s.prod())
请参阅下面输出中的group列。你要做的一件事是让所有非零访问都有0概率,最后一行不会这样做。
product store week visit prob group cum_prod
0 123 321 1 0 0.003 0 0.000702
1 123 321 2 0 0.234 0 0.000702
2 123 321 3 1 0.000 1 0.000000
3 123 321 4 0 0.198 2 0.198000
4 123 301 1 0 0.290 0 0.290000
5 123 301 2 2 0.000 1 0.000000
6 123 301 3 0 0.989 2 0.989000
7 123 301 4 4 0.788 3 0.788000