【问题标题】:Make Pandas groupby act similarly to itertools groupby使 Pandas groupby 的行为类似于 itertools groupby
【发布时间】:2015-12-17 11:03:37
【问题描述】:

假设我有一个这样的列表的 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 键被分成三个独立的组或字段。

现在假设我的 dict 具有等效的 Pandas DataFrame(相同的数据、相同的列表顺序和相同的键):

  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 的 groupby,例如,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 上执行第二个 groupby 以将每个键的子组分开。

【问题讨论】:

  • 有趣的问题。在这种情况下应该返回什么对象呢?我的意思是,groupby 旨在返回具有唯一键的对象,但这在这里是不可能的。你想如何区分重复键(6s的不同组)?
  • 有时键的不间断运行是数据的另一个元素。另一个键的存在表明了一些事情。如:时间间隔或数据读数。一旦您在系列中有不同的间隔,那就是不同的领域。我想要类似键的统一运行——就像 itertools 一样。
  • 有一种方法here 可以提供解决方案。

标签: python pandas group-by


【解决方案1】:

首先,您可以确定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()) 

获取想要的groupby对象

【讨论】:

    【解决方案2】:

    好吧,不要厚脸皮,但为什么不直接在 DataFrame 上使用 Python 的 groupby 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 以您想要的方式运行可能需要太多堆叠的方法,以至于您将来重读时将无法遵循它。

    【讨论】:

    • 回答你的问题,可能比较慢。
    • @TheUnfunCat:在较大的数据帧上执行可能会更慢,但程序员编写的替代方案可能会更慢。
    【解决方案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 groupGrp 上进行分组:

    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
    

    请注意,这个答案here 具有相同的想法(感谢@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]
    

    【讨论】:

    • 该死!我在发布我非常满意的答案后才读到这篇文章……我发誓这是我自己的; P
    • 我想我只是从Pandas 开始,但是我如何使用{1: [0], 2: [1, 2], 3: [3, 4], 4: [5, 6], 5: [7, 8], 6: [9]df 获取键和行?我看到这对是如何匹配的,但我无法将其翻译成我熟悉的切片或 somthic Pythonic...抱歉...
    • df.groupby(['new_group', 'Grp']).Nums.groups.keys() dict_keys([(1, 2), (2, 6), (4, 6), (6, 6), (5, 7), (3, 5)]
    • [df.loc[(df.new_group == k1) & (df.Grp == k2), :] for k1, k2 in df.groupby(['new_group', 'Grp']).Nums.groups.keys()] 请注意,groups 是一个没有一致顺序的字典,因此您可能希望重新使用您的列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多