Pandas 中的聚合
- 2024-12-04 08:56:00
- admin 原创
- 230
问题描述:
如何使用 Pandas 进行聚合?
聚合后没有 DataFrame!发生了什么?
我如何才能聚合主要的字符串列(到
list
s,tuple
s,strings with separator
)?我如何才能汇总计数?
如何创建由聚合值填充的新列?
我见过这些反复出现的问题,询问 pandas 聚合功能的各种方面。目前,有关聚合及其各种用例的大部分信息分散在数十篇措辞不当、无法搜索的帖子中。这里的目的是整理一些更重要的要点,以供后人参考。
本问答旨在成为一系列实用用户指南的下一部分:
我如何旋转数据框?
Pandas concat 函数中的“levels”、“keys”和“names”参数有什么用?
如何对每列都有一个系列的 DataFrame 进行操作?
Pandas 合并 101
请注意,这篇文章并非旨在替代有关聚合和groupby 的文档,因此也请阅读该文档!
解决方案 1:
问题 1
如何使用 Pandas 进行聚合?
扩展聚合文档。
聚合函数用于减少返回对象的维度。这意味着输出 Series/DataFrame 的行数与原始行数相比更少或相同。
一些常见的聚合函数如下表所示:
功能 描述
mean() 计算组的平均值
sum() 计算组值之和
size() 计算组大小
count() 计算组的数量
std() 组的标准差
var() 计算组的方差
sem() 组平均值的标准误差
describe() 生成描述统计数据
first() 计算组内第一个值
last() 计算组内最后一个值
nth() 取第 n 个值,如果 n 是列表,则取子集
min() 计算组值的最小值
max() 计算组值的最大值
np.random.seed(123)
df = pd.DataFrame({'A' : ['foo', 'foo', 'bar', 'foo', 'bar', 'foo'],
'B' : ['one', 'two', 'three','two', 'two', 'one'],
'C' : np.random.randint(5, size=6),
'D' : np.random.randint(5, size=6),
'E' : np.random.randint(5, size=6)})
print (df)
A B C D E
0 foo one 2 3 0
1 foo two 4 1 0
2 bar three 2 1 1
3 foo two 1 0 3
4 bar two 3 1 4
5 foo one 2 1 0
通过过滤列和Cython 实现的函数进行聚合:
df1 = df.groupby(['A', 'B'], as_index=False)['C'].sum()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
聚合函数用于所有未在groupby
函数中指定的列,以下是A, B
列:
df2 = df.groupby(['A', 'B'], as_index=False).sum()
print (df2)
A B C D E
0 bar three 2 1 1
1 bar two 3 1 4
2 foo one 4 4 0
3 foo two 5 1 3
您还可以在函数后面的列表中仅指定一些用于聚合的列groupby
:
df3 = df.groupby(['A', 'B'], as_index=False)['C','D'].sum()
print (df3)
A B C D
0 bar three 2 1
1 bar two 3 1
2 foo one 4 4
3 foo two 5 1
使用函数得到相同的结果DataFrameGroupBy.agg
:
df1 = df.groupby(['A', 'B'], as_index=False)['C'].agg('sum')
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
df2 = df.groupby(['A', 'B'], as_index=False).agg('sum')
print (df2)
A B C D E
0 bar three 2 1 1
1 bar two 3 1 4
2 foo one 4 4 0
3 foo two 5 1 3
对于应用于一列的多个函数,请使用 s 列表tuple
- 新列的名称和聚合函数:
df4 = (df.groupby(['A', 'B'])['C']
.agg([('average','mean'),('total','sum')])
.reset_index())
print (df4)
A B average total
0 bar three 2.0 2
1 bar two 3.0 3
2 foo one 2.0 4
3 foo two 2.5 5
如果想要传递多个函数,可以传递list
s tuple
:
df5 = (df.groupby(['A', 'B'])
.agg([('average','mean'),('total','sum')]))
print (df5)
C D E
average total average total average total
A B
bar three 2.0 2 1.0 1 1.0 1
two 3.0 3 1.0 1 4.0 4
foo one 2.0 4 2.0 4 0.0 0
two 2.5 5 0.5 1 1.5 3
然后进入MultiIndex
列:
print (df5.columns)
MultiIndex(levels=[['C', 'D', 'E'], ['average', 'total']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])
要转换为列,展平MultiIndex
使用:map
`join`
df5.columns = df5.columns.map('_'.join)
df5 = df5.reset_index()
print (df5)
A B C_average C_total D_average D_total E_average E_total
0 bar three 2.0 2 1.0 1 1.0 1
1 bar two 3.0 3 1.0 1 4.0 4
2 foo one 2.0 4 2.0 4 0.0 0
3 foo two 2.5 5 0.5 1 1.5 3
另一个解决方案是传递聚合函数列表,然后展平MultiIndex
并对另一列名称使用str.replace
:
df5 = df.groupby(['A', 'B']).agg(['mean','sum'])
df5.columns = (df5.columns.map('_'.join)
.str.replace('sum','total')
.str.replace('mean','average'))
df5 = df5.reset_index()
print (df5)
A B C_average C_total D_average D_total E_average E_total
0 bar three 2.0 2 1.0 1 1.0 1
1 bar two 3.0 3 1.0 1 4.0 4
2 foo one 2.0 4 2.0 4 0.0 0
3 foo two 2.5 5 0.5 1 1.5 3
如果要使用聚合函数分别指定每一列,请执行以下操作dictionary
:
df6 = (df.groupby(['A', 'B'], as_index=False)
.agg({'C':'sum','D':'mean'})
.rename(columns={'C':'C_total', 'D':'D_average'}))
print (df6)
A B C_total D_average
0 bar three 2 1.0
1 bar two 3 1.0
2 foo one 4 2.0
3 foo two 5 0.5
您也可以传递自定义函数:
def func(x):
return x.iat[0] + x.iat[-1]
df7 = (df.groupby(['A', 'B'], as_index=False)
.agg({'C':'sum','D': func})
.rename(columns={'C':'C_total', 'D':'D_sum_first_and_last'}))
print (df7)
A B C_total D_sum_first_and_last
0 bar three 2 2
1 bar two 3 2
2 foo one 4 4
3 foo two 5 1
问题 2
聚合后没有 DataFrame!发生了什么?
按两列或多列进行聚合:
df1 = df.groupby(['A', 'B'])['C'].sum()
print (df1)
A B
bar three 2
two 3
foo one 4
two 5
Name: C, dtype: int32
首先检查Pandas 对象的Index
和:type
print (df1.index)
MultiIndex(levels=[['bar', 'foo'], ['one', 'three', 'two']],
labels=[[0, 0, 1, 1], [1, 2, 0, 2]],
names=['A', 'B'])
print (type(df1))
<class 'pandas.core.series.Series'>
对于如何获取MultiIndex Series
列,有两种解决方案:
添加参数
as_index=False
df1 = df.groupby(['A', 'B'], as_index=False)['C'].sum()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
使用
Series.reset_index
:
df1 = df.groupby(['A', 'B'])['C'].sum().reset_index()
print (df1)
A B C
0 bar three 2
1 bar two 3
2 foo one 4
3 foo two 5
如果按一列分组:
df2 = df.groupby('A')['C'].sum()
print (df2)
A
bar 5
foo 9
Name: C, dtype: int32
...Series
获得Index
:
print (df2.index)
Index(['bar', 'foo'], dtype='object', name='A')
print (type(df2))
<class 'pandas.core.series.Series'>
解决方案与以下相同MultiIndex Series
:
df2 = df.groupby('A', as_index=False)['C'].sum()
print (df2)
A C
0 bar 5
1 foo 9
df2 = df.groupby('A')['C'].sum().reset_index()
print (df2)
A C
0 bar 5
1 foo 9
问题 3
我如何才能聚合主要的字符串列(到list
s,tuple
s,strings with separator
)?
df = pd.DataFrame({'A' : ['a', 'c', 'b', 'b', 'a', 'c', 'b'],
'B' : ['one', 'two', 'three','two', 'two', 'one', 'three'],
'C' : ['three', 'one', 'two', 'two', 'three','two', 'one'],
'D' : [1,2,3,2,3,1,2]})
print (df)
A B C D
0 a one three 1
1 c two one 2
2 b three two 3
3 b two two 2
4 a two three 3
5 c one two 1
6 b three one 2
除了使用聚合函数之外,还可以传递list
、来转换列tuple
:set
df1 = df.groupby('A')['B'].agg(list).reset_index()
print (df1)
A B
0 a [one, two]
1 b [three, two, three]
2 c [two, one]
另一种方法是使用GroupBy.apply
:
df1 = df.groupby('A')['B'].apply(list).reset_index()
print (df1)
A B
0 a [one, two]
1 b [three, two, three]
2 c [two, one]
要转换为带有分隔符的字符串,.join
仅当它是字符串列时才使用:
df2 = df.groupby('A')['B'].agg(','.join).reset_index()
print (df2)
A B
0 a one,two
1 b three,two,three
2 c two,one
如果它是数字列,则使用 lambda 函数astype
将其转换为string
s:
df3 = (df.groupby('A')['D']
.agg(lambda x: ','.join(x.astype(str)))
.reset_index())
print (df3)
A D
0 a 1,3
1 b 3,2,2
2 c 2,1
另一个解决方案是先转换为字符串groupby
:
df3 = (df.assign(D = df['D'].astype(str))
.groupby('A')['D']
.agg(','.join).reset_index())
print (df3)
A D
0 a 1,3
1 b 3,2,2
2 c 2,1
要转换所有列,请不要在 后传递列列表groupby
。没有任何列D
,因为会自动排除“干扰”列。这意味着所有数字列都被排除。
df4 = df.groupby('A').agg(','.join).reset_index()
print (df4)
A B C
0 a one,two three,three
1 b three,two,three two,two,one
2 c two,one one,two
因此需要将所有列转换为字符串,然后获取所有列:
df5 = (df.groupby('A')
.agg(lambda x: ','.join(x.astype(str)))
.reset_index())
print (df5)
A B C D
0 a one,two three,three 1,3
1 b three,two,three two,two,one 3,2,2
2 c two,one one,two 2,1
问题 4
我如何才能汇总计数?
df = pd.DataFrame({'A' : ['a', 'c', 'b', 'b', 'a', 'c', 'b'],
'B' : ['one', 'two', 'three','two', 'two', 'one', 'three'],
'C' : ['three', np.nan, np.nan, 'two', 'three','two', 'one'],
'D' : [np.nan,2,3,2,3,np.nan,2]})
print (df)
A B C D
0 a one three NaN
1 c two NaN 2.0
2 b three NaN 3.0
3 b two two 2.0
4 a two three 3.0
5 c one two NaN
6 b three one 2.0
各组GroupBy.size
功能:size
df1 = df.groupby('A').size().reset_index(name='COUNT')
print (df1)
A COUNT
0 a 2
1 b 3
2 c 2
函数GroupBy.count
排除缺失值:
df2 = df.groupby('A')['C'].count().reset_index(name='COUNT')
print (df2)
A COUNT
0 a 2
1 b 2
2 c 1
此函数应用于计算多列非缺失值:
df3 = df.groupby('A').count().add_suffix('_COUNT').reset_index()
print (df3)
A B_COUNT C_COUNT D_COUNT
0 a 2 2 1
1 b 3 2 3
2 c 2 1 1
相关函数是Series.value_counts
。它返回包含按降序排列的唯一值计数的对象的大小,因此第一个元素是最常出现的元素。它NaN
默认排除 s 个值。
df4 = (df['A'].value_counts()
.rename_axis('A')
.reset_index(name='COUNT'))
print (df4)
A COUNT
0 b 3
1 a 2
2 c 2
如果想要与使用函数groupby
+相同的输出size
,请添加Series.sort_index
:
df5 = (df['A'].value_counts()
.sort_index()
.rename_axis('A')
.reset_index(name='COUNT'))
print (df5)
A COUNT
0 a 2
1 b 3
2 c 2
问题 5
如何创建由聚合值填充的新列?
方法GroupBy.transform
返回一个与被分组的对象索引相同(大小相同)的对象。
有关更多信息,请参阅Pandas 文档。
np.random.seed(123)
df = pd.DataFrame({'A' : ['foo', 'foo', 'bar', 'foo', 'bar', 'foo'],
'B' : ['one', 'two', 'three','two', 'two', 'one'],
'C' : np.random.randint(5, size=6),
'D' : np.random.randint(5, size=6)})
print (df)
A B C D
0 foo one 2 3
1 foo two 4 1
2 bar three 2 1
3 foo two 1 0
4 bar two 3 1
5 foo one 2 1
df['C1'] = df.groupby('A')['C'].transform('sum')
df['C2'] = df.groupby(['A','B'])['C'].transform('sum')
df[['C3','D3']] = df.groupby('A')['C','D'].transform('sum')
df[['C4','D4']] = df.groupby(['A','B'])['C','D'].transform('sum')
print (df)
A B C D C1 C2 C3 D3 C4 D4
0 foo one 2 3 9 4 9 5 4 4
1 foo two 4 1 9 5 9 5 5 1
2 bar three 2 1 5 2 5 2 2 1
3 foo two 1 0 9 5 9 5 5 1
4 bar two 3 1 5 3 5 2 3 1
5 foo one 2 1 9 4 9 5 4 4
解决方案 2:
如果您有 R 或 SQL 背景,这里有三个示例,它们将教您以您熟悉的方式进行聚合所需的一切:
首先创建一个 Pandas 数据框
import pandas as pd
df = pd.DataFrame({'key1' : ['a','a','a','b','a'],
'key2' : ['c','c','d','d','e'],
'value1' : [1,2,2,3,3],
'value2' : [9,8,7,6,5]})
df.head(5)
我们创建的表格如下所示:
键1 | 键2 | 值1 | 值2 |
---|---|---|---|
一个 | 丙 | 1 | 9 |
一个 | 丙 | 2 | 8 |
一个 | d | 2 | 7 |
b | d | 3 | 6 |
一个 | 埃 | 3 | 5 |
使用类似于 SQL 的行缩减进行聚合
Group By
1.1 如果 Pandas 版本>=0.25
运行检查你的 Pandas 版本print(pd.__version__)
。如果你的Pandas 版本是 0.25 或更高版本,则以下代码将起作用:
df_agg = df.groupby(['key1','key2']).agg(mean_of_value_1=('value1', 'mean'),
sum_of_value_2=('value2', 'sum'),
count_of_value1=('value1','size')
).reset_index()
df_agg.head(5)
生成的数据表将如下所示:
键1 | 键2 | 平均值1 | 值之和2 | 值计数1 |
---|---|---|---|---|
一个 | 丙 | 1.5 | 17 | 2 |
一个 | d | 2.0 | 7 | 1 |
一个 | 埃 | 3.0 | 5 | 1 |
b | d | 3.0 | 6 | 1 |
与此等价的SQL语句是:
SELECT
key1
,key2
,AVG(value1) AS mean_of_value_1
,SUM(value2) AS sum_of_value_2
,COUNT(*) AS count_of_value1
FROM
df
GROUP BY
key1
,key2
1.2 如果 Pandas 版本<0.25
如果您的 Pandas 版本早于 0.25,则运行上述代码将出现以下错误:
TypeError:aggregate()缺少 1 个必需的位置参数:'arg'
value1
现在,为了对和进行聚合value2
,您将运行以下代码:
df_agg = df.groupby(['key1','key2'],as_index=False).agg({'value1':['mean','count'],'value2':'sum'})
df_agg.columns = ['_'.join(col).strip() for col in df_agg.columns.values]
df_agg.head(5)
结果表将如下所示:
键1 | 键2 | 值1_平均值 | 值1_计数 | 值2_总和 |
---|---|---|---|---|
一个 | 丙 | 1.5 | 2 | 17 |
一个 | d | 2.0 | 1 | 7 |
一个 | 埃 | 3.0 | 1 | 5 |
b | d | 3.0 | 1 | 6 |
需要使用以下代码单独重命名列:
df_agg.rename(columns={"value1_mean" : "mean_of_value1",
"value1_count" : "count_of_value1",
"value2_sum" : "sum_of_value2"
}, inplace=True)
2. 创建不减少行数的列 ( EXCEL - SUMIF, COUNTIF
)
如果您想要执行 SUMIF、COUNTIF 等操作,就像在 Excel 中行数不减少的情况一样,那么您需要执行此操作。
df['Total_of_value1_by_key1'] = df.groupby('key1')['value1'].transform('sum')
df.head(5)
生成的数据框将如下所示,其行数与原始数据框相同:
键1 | 键2 | 值1 | 值2 | 按键 1 的值总计 |
---|---|---|---|---|
一个 | 丙 | 1 | 9 | 8 |
一个 | 丙 | 2 | 8 | 8 |
一个 | d | 2 | 7 | 8 |
b | d | 3 | 6 | 3 |
一个 | 埃 | 3 | 5 | 8 |
3.创建 RANK 列ROW_NUMBER() OVER (PARTITION BY ORDER BY)
最后,在某些情况下您可能想要创建一个与 SQL等同的排名列。ROW_NUMBER() OVER (PARTITION BY key1 ORDER BY value1 DESC, value2 ASC)
以下是具体操作方法。
df['RN'] = df.sort_values(['value1','value2'], ascending=[False,True]) \n .groupby(['key1']) \n .cumcount() + 1
df.head(5)
``注意:我们通过在每行末尾添加来使代码变为多行。
最终的数据框如下所示:
键1 | 键2 | 值1 | 值2 | RN |
---|---|---|---|---|
一个 | 丙 | 1 | 9 | 4 |
一个 | 丙 | 2 | 8 | 3 |
一个 | d | 2 | 7 | 2 |
b | d | 3 | 6 | 1 |
一个 | 埃 | 3 | 5 | 1 |
在上述所有示例中,最终的数据表将具有表结构,并且不会具有您在其他语法中可能获得的数据透视结构。
其他聚合运算符:
mean()
计算组的平均值
sum()
计算组值的总和
size()
计算组大小
count()
计算组数
std()
组别标准差
var()
计算组的方差
sem()
组平均值的标准误差
describe()
生成描述性统计数据
first()
计算组内第一个值
last()
计算组最后一个值
nth()
取第 n 个值,如果 n 是列表,则取其子集
min()
计算组值的最小值
max()
计算组值的最大值