Панды - накопленная сумма последовательных - PullRequest
0 голосов
/ 20 мая 2018

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

Name_A ¦  date1 ¦ 1

Name_A ¦  date2 ¦ 0 

Name_A ¦  date3 ¦ 1

Name_A ¦  date4 ¦ 1

Name_A ¦  date5 ¦ 1

Name_B ¦  date6 ¦ 1

Name_B ¦  date7 ¦ 1

Name_B ¦  date8 ¦ 0

Name_B ¦  date9 ¦ 1

И я хотел бы получить это:

Name_A ¦ date1 ¦ 1  

Name_A ¦ date2 ¦ 0  

Name_A ¦ date3 ¦ 1  

Name_A ¦ date4 ¦ 2 

Name_A ¦ date5 ¦ 3

Name_B ¦ date6 ¦ 1

Name_B ¦ date7 ¦ 2

Name_B ¦ date8 ¦ 0

Name_B ¦ date9 ¦ 1 

По сути, я хочу получить кумулятивную сумму последовательных 1 с.Если имя меняется или появляется 0, он должен начать отсчет снова с 0.

Есть идеи / предложения?Спасибо.

Ответы [ 3 ]

0 голосов
/ 20 мая 2018

Вот векторизованное решение, не требующее явных циклов:

df = pd.DataFrame.from_dict({'name': list('AAAAABBBB'), 'bit': (1,0,1,1,1,1,1,0,1)})
>>> df
   bit name
0    1    A
1    0    A
2    1    A
3    1    A
4    1    A
5    1    B
6    1    B
7    0    B
8    1    B
>>> reset = (df['bit'] == 0) | (df['name'] != df['name'].shift(1))
>>> reset, = np.where(np.concatenate([reset, [True]]))
>>> df['count'] = np.arange(reset[-1]) + (df['bit'].values[reset[:-1]]-reset[:-1]).repeat(np.diff(reset))
>>> df
   bit name  count
0    1    A      1
1    0    A      0
2    1    A      1
3    1    A      2
4    1    A      3
5    1    B      1
6    1    B      2
7    0    B      0
8    1    B      1
0 голосов
/ 20 мая 2018

Вот мое собственное взятие:

In [145]: group_ids = df[2].diff().ne(0).cumsum()

In [146]: df["count"] = df[2].groupby([df[0], group_ids]).cumsum()

In [147]: df
Out[147]: 
        0      1  2  count
0  Name_A  date1  1      1
1  Name_A  date2  0      0
2  Name_A  date3  1      1
3  Name_A  date4  1      2
4  Name_A  date5  1      3
5  Name_B  date6  1      1
6  Name_B  date7  1      2
7  Name_B  date8  0      0
8  Name_B  date9  1      1

При этом используется шаблон сравнения-cumsum-groupby для поиска смежных групп, потому что df[2].diff().ne(0) дает нам True, когда значение не совпадает сprevious, и их совокупная сумма дает нам новое число всякий раз, когда начинается новая группа 1 с.

Это будет означать, что у нас есть один и тот же group_id для двоичных значений, пересекающих разные имена, конечно, но так как мы 'сгруппировавшись на и df [0] (имена) и group_ids, все в порядке.

0 голосов
/ 20 мая 2018

Я перестроил ваши данные следующим образом:

import pandas as pd

df = pd.DataFrame(
    {'col1': ['Name_A'] * 5 + ['Name_B'] * 4,
     'col2': ['date{}'.format(x) for x in list(range(1,10,1))],
     'col3': [1,0,1,1,1,1,1,0,1]})

Для предлагаемой вами группы я предпочитаю использовать itertools.groupby вместо pd.groupby, чтобы я мог явно указать два условиячто вы указали (изменение имени и 0 в столбце значений):

from itertools import groupby

groups = []
uniquekeys = []
for k, g in groupby(df.iterrows(), 
                    lambda row: (row[1]['col1'], row[1]['col3'] == 0)):
    groups.append(list(g))
    uniquekeys.append(k)

Теперь, когда существуют правильные группы, остается только выполнить итерацию, а затем вычислить совокупную сумму:

cumsum = pd.concat([pd.Series([y[1]['col3'] for y in x]).cumsum() for x in groups])

df['cumsum'] = list(cumsum)

Результат:

    col1    col2    col3    cumsum
0   Name_A  date1   1       1
1   Name_A  date2   0       0
2   Name_A  date3   1       1
3   Name_A  date4   1       2
4   Name_A  date5   1       3
5   Name_B  date6   1       1
6   Name_B  date7   1       2
7   Name_B  date8   0       0
8   Name_B  date9   1       1

Для справки см. Хорошее объяснение о itertools.groupby здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...