【问题标题】:pandas groupby return data on original MultiIndexpandas groupby 返回原始 MultiIndex 上的数据
【发布时间】:2019-09-29 15:15:29
【问题描述】:

请看下面我的例子,我怎样才能从 groupby 中返回原始 MultiIndex 的所有 3 个级别的数据?

在此示例中:我想按品牌查看总数。我现在使用 map 应用了一种解决方法(见下文,这显示了我希望直接从 groupby 获得的输出)。

brands = ['Tesla','Tesla','Tesla','Peugeot', 'Peugeot', 'Citroen', 'Opel', 'Opel', 'Peugeot', 'Citroen', 'Opel']
years = [2018, 2017,2016, 2018, 2017, 2017, 2018, 2017,2016, 2016,2016]
owners = ['Tesla','Tesla','Tesla','PSA', 'PSA', 'PSA', 'PSA', 'PSA','PSA', 'PSA', 'PSA']
index = pd.MultiIndex.from_arrays([owners, years, brands], names=['owner', 'year', 'brand'])
data = np.random.randint(low=100, high=1000, size=len(index), dtype=int)
weight = np.random.randint(low=1, high=10, size=len(index), dtype=int)
df = pd.DataFrame({'data': data, 'weight': weight},index=index)
df.loc[('PSA', 2017, 'Opel'), 'data'] = np.nan
df.loc[('PSA', 2016, 'Opel'), 'data'] = np.nan
df.loc[('PSA', 2016, 'Citroen'), 'data'] = np.nan
df.loc[('Tesla', 2016, 'Tesla'), 'data'] = np.nan

出来:

                        data    weight
owner   year    brand       
PSA     2016    Citroen NaN     5
                Opel    NaN     5
                Peugeot 250.0   2
        2017    Citroen 469.0   4
                Opel    NaN     5
                Peugeot 768.0   5
        2018    Opel    237.0   6
                Peugeot 663.0   4
Tesla   2016    Tesla   NaN     3
        2017    Tesla   695.0   6
        2018    Tesla   371.0   5

我尝试过使用索引和“级别”以及列和“按”。 我尝试过使用“as_index = False”.sum() 以及“group_keys()”= False 和 .apply(sum)。但我无法在 groupby 输出中恢复品牌列:

grouped = df.groupby(level=['owner', 'year'], group_keys=False) #type: <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
grouped.apply(sum)

出来:

        data    weight  group_data
owner   year            
PSA     2016    250.0   12.0    750.0
        2017    1237.0  14.0    3711.0
        2018    900.0   10.0    1800.0
Tesla   2016    0.0     3.0     0.0
        2017    695.0   6.0     695.0
        2018    371.0   5.0     371.0

类似的:

grouped = df.groupby(by=['owner', 'year'], group_keys=False) #type: <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
grouped.apply(sum)

或:

grouped = df.groupby(by=['owner', 'year'], as_index=False, group_keys=False) #type: <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
grouped.sum()

解决方法:

grouped = df.groupby(level=['owner', 'year'], group_keys=False) #type: <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
df_owner_year = grouped.apply(sum)
s_data = df_owner_year['data']
df['group_data'] = df.index.map(s_data)
df

出来:

                        data    weight  group_data
owner   year    brand           
PSA     2016    Citroen NaN     5   250.0
                Opel    NaN     5   250.0
                Peugeot 250.0   2   250.0
        2017    Citroen 469.0   4   1237.0
                Opel    NaN     5   1237.0
                Peugeot 768.0   5   1237.0
        2018    Opel    237.0   6   900.0
                Peugeot 663.0   4   900.0
Tesla   2016    Tesla   NaN     3   0.0
        2017    Tesla   695.0   6   695.0
        2018    Tesla   371.0   5   371.0

【问题讨论】:

  • 我不确定我是否理解,但是您的解决方法的结果与您在开始时定义的不一样吗?索引的目的不是唯一标识您要查找的记录/字段吗?如果是这样,那么按整个索引分组不会导致任何变化,因为您是按个人分组。
  • @KenHBS 谢谢。我按索引的第 0 级和第 1 级分组,并尝试在保留第 2 级的情况下将组总数返回到 df 中。

标签: python-3.x pandas


【解决方案1】:

我确信在某些情况下 MultiIndex 很有价值,但我通常只想尽快摆脱它,所以我会从 df = df.reset_index() 开始。

然后您可以轻松地按brand 分组,例如:

>>> df.groupby('brand').agg({'weight': sum, 'data': sum})
#          weight    data
# brand                  
# Citroen      10   784.0
# Opel         13   193.0
# Peugeot      14  1663.0
# Tesla        18   507.0

或按所有者和年份分组:

>>> df.groupby(['owner', 'year']).agg({'weight': sum, 'data': sum})
              weight    data
# owner year                
# PSA   2016      17   879.0
#       2017       8  1264.0
#       2018      12   497.0
# Tesla 2016       8     0.0
#       2017       4   151.0
#       2018       6   356.0

【讨论】:

  • 谢谢。我的例子不够清楚,但我需要这里的多索引,因为我想用 3 个键创建新的唯一行。
【解决方案2】:

您可以使用 groupby 来完成此操作。

df = df.sort_index()
print(df)

                     data  weight
owner year brand                 
PSA   2016 Citroen    NaN       4
           Opel       NaN       7
           Peugeot  880.0       1
      2017 Citroen  164.0       2
           Opel       NaN       5
           Peugeot  607.0       8
      2018 Opel     809.0       1
           Peugeot  317.0       8
Tesla 2016 Tesla      NaN       1
      2017 Tesla    384.0       9
      2018 Tesla    550.0       9

Groupby 所有者和年份以及使您的新列等于该值。

df['new'] = df.groupby(['owner', 'year'])['data'].sum()
print(df)

                    data  weight     new
owner year brand                         
PSA   2016 Citroen    NaN       4   880.0
           Opel       NaN       7   880.0
           Peugeot  880.0       1   880.0
      2017 Citroen  164.0       2   771.0
           Opel       NaN       5   771.0
           Peugeot  607.0       8   771.0
      2018 Opel     809.0       1  1126.0
           Peugeot  317.0       8  1126.0
Tesla 2016 Tesla      NaN       1     0.0
      2017 Tesla    384.0       9   384.0
      2018 Tesla    550.0       9   550.0

编辑

还有一个问题被问到为什么当按列 df['new'] 分组时返回 NaN 但当分组在索引中时返回正确的值。我在 SO 上提出了这个问题,一个很好的答案是 @Jezrael 的 here。

【讨论】:

  • 谢谢。我没有意识到 sum() 通过 (grouped.groups -> "MultiIndex(levels=[['PSA', 'Tesla'], [2016, 2017, 2018], ['Citroen'] , 'Opel', 'Peugeot', 'Tesla']],.. 通过 df['new'] = 立即将分组和求和的系列结果分配给原始 df,您可以避免丢失索引。它是就像我理解的即时地图一样。
  • 对不起,但这在其他情况下似乎对我不起作用。非常简单:df = pd.DataFrame({'group':['a','a','b','b'], 'data':[5,10,100,30]},columns=['group ', 'data']) df['new'] = df.groupby('group')['data'].sum() print(df) -> 新列仅包含 NaN。没有分配给新系列的 groupby 结果看起来很好......添加 groupby_result = df.groupby('group')['data'].sum() df['new'] = df['group'].map (groupby_result) 确实给了我正确的新列。你能解释一下吗?
  • 分组在索引级别时,行如上填充,但在列级别时,它们不会。我没有充分的理由,我将在 SO 中发布一个问题。
  • 谢谢!,我已接受您的回答,为避免混淆,最好(请)在答案中添加对新问题的引用或结果?
猜你喜欢
  • 1970-01-01
  • 2018-11-25
  • 2017-04-06
  • 2018-10-28
  • 1970-01-01
  • 2021-05-18
  • 2021-04-04
  • 2023-03-25
  • 2015-06-12
相关资源
最近更新 更多