根据上次出现次数计算前面的零

时间:2016-12-26 10:48:33

标签: python python-3.x pandas

我在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中我可以使用数组和一些循环。

2 个答案:

答案 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

enter image description here

我将使用我刚刚创建的一个列来创建分组列

d1['visit_group'] = d1.groupby(['product', 'store']).novisit.cumsum()
d1

enter image description here

最后,添加'cum_prob'以及我在上面制作的列。

d1['cum_prob'] = d1.groupby(
    ['product', 'store', 'visit_group']
).filled_prob.transform('prod') * (~d1.iszero) * (~d1.novisit)
d1

enter image description here

您可以根据自己的需要对其进行切片

d1.loc[:, df.columns.tolist() + ['cum_prob']]

enter image description here

所有

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