Сделайте группу Pandas groupby, действуя аналогично группе itertools groupby
Предположим, что у меня есть список Python таких списков:
{'Grp': ['2' , '6' , '6' , '5' , '5' , '6' , '6' , '7' , '7' , '6'],
'Nums': ['6.20', '6.30', '6.80', '6.45', '6.55', '6.35', '6.37', '6.36', '6.78', '6.33']}
Я могу легко сгруппировать числа и групповой ключ, используя itertools.groupby:
from itertools import groupby
for k, l in groupby(zip(di['Grp'], di['Nums']), key=lambda t: t[0]):
print k, [t[1] for t in l]
Печать
2 ['6.20']
6 ['6.30', '6.80'] # one field, key=6
5 ['6.45', '6.55']
6 ['6.35', '6.37'] # second
7 ['6.36', '6.78']
6 ['6.33'] # third
Обратите внимание, что ключ 6
разделен на три отдельные группы или поля.
Теперь предположим, что у меня есть эквивалент Pandas DataFrame для моего dict (те же данные, тот же порядок списка и те же клавиши):
Grp Nums
0 2 6.20
1 6 6.30
2 6 6.80
3 5 6.45
4 5 6.55
5 6 6.35
6 6 6.37
7 7 6.36
8 7 6.78
9 6 6.33
Если я использую Pandas 'groupby, я не вижу, как получить групповую итерацию. Вместо этого Pandas группируется по значению ключа:
for e in df.groupby('Grp'):
print e
Печать
('2', Grp Nums
0 2 6.20)
('5', Grp Nums
3 5 6.45
4 5 6.55)
('6', Grp Nums
1 6 6.30
2 6 6.80 # df['Grp'][1:2] first field
5 6 6.35 # df['Grp'][5:6] second field
6 6 6.37
9 6 6.33) # df['Grp'][9] third field
('7', Grp Nums
7 7 6.36
8 7 6.78)
Примечание: групповые клавиши 6
сгруппированы вместе; а не отдельные группы.
Мой вопрос: существует ли эквивалентный способ использования группы Pandas ', так что 6
, например, будет в трех группах таким же образом, как Python groupby
?
Я пробовал это:
>>> df.reset_index().groupby('Grp')['index'].apply(lambda x: np.array(x))
Grp
2 [0]
5 [3, 4]
6 [1, 2, 5, 6, 9] # I *could* do a second groupby on this...
7 [7, 8]
Name: index, dtype: object
Но он по-прежнему сгруппирован с помощью общего ключа Grp
, и мне нужно будет сделать вторую группу на nd.array
, чтобы разделить подгруппы каждого ключа.
Ответы
Ответ 1
Ну, чтобы не быть нахальным, но почему бы просто не использовать Python groupby
в DataFrame, используя iterrows? Это то, для чего оно предназначено для:
>>> df
Grp Nums
0 2 6.20
1 6 6.30
2 6 6.80
3 5 6.45
4 5 6.55
5 6 6.35
6 6 6.37
7 7 6.36
8 7 6.78
9 6 6.33
>>> from itertools import groupby
>>> for k, l in groupby(df.iterrows(), key=lambda row: row[1]['Grp']):
print k, [t[1]['Nums'] for t in l]
Печать
2 ['6.20']
6 ['6.30', '6.80']
5 ['6.45', '6.55']
6 ['6.35', '6.37']
7 ['6.36', '6.78']
6 ['6.33']
Чтобы попытаться сделать Panda groupby
действовать так, как вы хотите, возможно, запрашивается так много многоуровневых методов, что вы не сможете следовать ему, когда перечитываете в будущем.
Ответ 2
Сначала вы можете определить, какие элементы в столбце Grp
отличаются от предыдущего и получить совокупную сумму для формирования групп, которые вам нужны:
In [9]:
diff_to_previous = df.Grp != df.Grp.shift(1)
diff_to_previous.cumsum()
Out[9]:
0 1
1 2
2 2
3 3
4 3
5 4
6 4
7 5
8 5
9 6
Итак, вы можете сделать
df.groupby(diff_to_previous.cumsum())
чтобы получить желаемый объект группы
Ответ 3
В основном вы хотите создать новый столбец для индексации желаемого порядка группировки, а затем использовать его для группировки. Вы сохраняете номер индекса до тех пор, пока значение в Grp
не изменится.
Для ваших данных вы хотели бы что-то вроде этого:
Grp Nums new_group
0 2 6.20 1
1 6 6.30 2
2 6 6.80 2
3 5 6.45 3
4 5 6.55 3
5 6 6.35 4
6 6 6.37 4
7 7 6.36 5
8 7 6.78 5
9 6 6.33 6
Теперь вы можете группировать как new group
, так и Grp
:
df.groupby(['new_group', 'Grp']).Nums.groups
{(1, 2): [0],
(2, 6): [1, 2],
(3, 5): [3, 4],
(4, 6): [5, 6],
(5, 7): [7, 8],
(6, 6): [9]
Я использовал этот метод для создания нового столбца:
df['new_group'] = None
for n, grp in enumerate(df.Grp):
if n is 0:
df.new_group.iat[0] = 1
elif grp == df.Grp.iat[n - 1]:
df.new_group.iat[n] = df.new_group.iat[n - 1]
else:
df.new_group.iat[n] = df.new_group.iat[n - 1] + 1
Обратите внимание, что этот ответ здесь имеет ту же идею (спасибо @ajcr за ссылку), но в гораздо более сжатом представлении:
>>> df.groupby((df.Grp != df.Grp.shift()).cumsum()).Nums.groups
{1: [0], 2: [1, 2], 3: [3, 4], 4: [5, 6], 5: [7, 8], 6: [9]