2016-12-26 5 views
2

У меня есть следующий фреймворк данных на 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

Я создам рабочий набор данных d1 и назначу ему несколько новых столбцов.

  • iszero tracks, где prob is zero. Я буду умножаться на эту колонку позже
  • 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)] 
+0

Очень ценный, не могли бы вы также объяснить второй последний шаг. Transform part – Mukul

+0

[transform] (http://pandas.pydata.org/pandas-docs/stable/groupby.html#transformation) возвращает объект с тем же индексом, который передается значениям 'groupby' для репликации внутри групп. – piRSquared

+0

Мне действительно не нравится этот ответ. Очень запутанный и трудно следовать. –

1

Вот это решение, которое будет назначать каждую неделю в группе, а затем найти накопленную сумму на основе этой группы.

Первое, что нужно сделать, это повернуть визиты в 0/1 с s.ne(0). Тогда первое различие, которое создаст -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()) 

См. Колонку группы на выходе ниже. Единственное, что вам нужно сделать, это сделать все ненулевые посещения равными 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 
+0

'df.loc [7, 'cum_prod']' должно быть нулевым. Я верю – piRSquared

Смежные вопросы