У меня есть такой кадр данных:

person  action_type        time
A           4           2014-11-10
A           4           2014-11-15
A           3           2014-11-16
A           1           2014-11-18
A           4           2014-11-19
B           4           2014-11-13
B           2           2014-11-15
B           4           2014-11-19

Поэтому я хочу добавить новый столбец с именем 'action_4', который представляет число действия action_type, равное 4, для человека за последние 7 дней (не включая себя). Результат как это:

person  action_type        time      action_4
A           4           2014-11-10      0
A           4           2014-11-15      1
A           3           2014-11-16      2
A           1           2014-11-18      1
A           4           2014-11-19      1
B           4           2014-11-13      0
B           2           2014-11-15      1
B           4           2014-11-19      1

Поскольку моя форма данных - 21649900 * 3, избегайте использования for ... in ... пожалуйста.

2
JoeKevin 24 Апр 2017 в 16:13

2 ответа

Лучший ответ

Вот мой подход.

Я думаю, что проверка интервала на основе времени (например, 7 дней) всегда очень дорога, поэтому лучше полагаться на количество наблюдений. (на самом деле, в последней версии панд они представили "учитывающее время" вращение, но у меня нет никакого опыта с этим ...)

Таким образом, мой подход состоит в том, чтобы для каждого человека установить ежедневную частоту, а затем просто подсчитать количество действий_4, произошедших за последние 7 дней, исключая сегодняшний. Я добавил комментарии к коду, которые должны прояснить его, но не стесняйтесь просить большего объяснения.

import pandas as pd
from io import StringIO

inp_str = u"""
person action_type time
A 4 2014-11-10
A 4 2014-11-15
A 3 2014-11-16
A 1 2014-11-18
A 4 2014-11-19
B 4 2014-11-13
B 2 2014-11-15
B 4 2014-11-19
"""

or_df = pd.read_csv(StringIO(inp_str), sep = " ").set_index('time')
or_df.index = pd.to_datetime(or_df.index)

# Find first and last date for each person
min_dates = or_df.groupby('person').apply(lambda x: x.index[0])
max_dates = or_df.groupby('person').apply(lambda x: x.index[-1])

# Resample each person to daily frequency so that 1 obs = 1 day
transf_df = pd.concat([el.reindex(pd.date_range(min_dates[pp], max_dates[pp], freq = 'D')) for pp, el in  or_df.groupby('person')])
# Forward fill person
transf_df.loc[:, 'person'] = transf_df['person'].ffill()
# Set a null value for action_type (possibly integer so you preserve the column type)
transf_df = transf_df.fillna({'action_type' : -1})

# For each person count the number of action 4, exluding today
result = transf_df.groupby('person').transform(lambda x: x.rolling(7, 1).apply(lambda y: len(y[y==4])).shift(1).fillna(0))
result.columns = ['action_4']

# Bring back to original index
pd.concat([transf_df, result], axis = 1).set_index('person', append = True).loc[or_df.set_index('person', append = True).index, :]

Это дает ожидаемый результат:

                   action_type  action_4
time       person                       
2014-11-10 A               4.0       0.0
2014-11-15 A               4.0       1.0
2014-11-16 A               3.0       2.0
2014-11-18 A               1.0       1.0
2014-11-19 A               4.0       1.0
2014-11-13 B               4.0       0.0
2014-11-15 B               2.0       1.0
2014-11-19 B               4.0       1.0
1
FLab 24 Апр 2017 в 15:37

Я не принимаю во внимание ваш столбец action_type, но он может помочь вам найти правильный ответ:

df2 = pd.DataFrame(None, columns=["person", "action_type", "time", "action_4"])

df["action_4"] = 0
for index, table in df.groupby('person'):
    table["action_4"] = table.time.apply(lambda x: table[(table.time > (x -
            datetime.timedelta(days=7))) & (table.time < x)].shape[0])
    df2 = pd.concat([df2, table])
1
Virginie 24 Апр 2017 в 14:51
43589076