如何使用 Pandas 进行聚合?聚合后没有DataFrame!发生了什么?如何主要聚合字符串列(到列表、元组、带分隔符的字符串)?如何汇总计数?如何创建一个由聚合值填充的新列?
我已经看到这些反复出现的问题询问熊猫聚合功能的各个方面。今天,关于聚合及其各种用例的大部分信息都分散在数十个措辞不当、无法搜索的帖子中。这里的目的是为后代整理一些更重要的观点。
本问答旨在成为一系列有用的用户指南的下一部分:
如何旋转数据框,
熊猫连接
如何对每列都有一个系列的 DataFrame 进行操作?
熊猫合并101
请注意,这篇文章并不是要替代 documentation about aggregation 和关于 groupby,所以也请阅读!
问题 1
如何使用 Pandas 进行聚合?
聚合函数是减少返回对象维度的函数。这意味着输出 Series/DataFrame 的行数与原始行数相同或更少。
下表列出了一些常见的聚合函数:
Function Description mean() Compute mean of groups sum() Compute sum of group values size() Compute group sizes count() Compute count of group std() Standard deviation of groups var() Compute variance of groups sem() Standard error of the mean of groups describe() Generates descriptive statistics first() Compute first of group values last() Compute last of group values nth() Take nth value, or a subset if n is a list min() Compute min of group values max() Compute max of group values
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 implemented functions 聚合:
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
对于应用于一列的多个函数,请使用 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
如果要传递多个函数,可以传递 tuple
的 list
:
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
... 用 Index
获得 Series
:
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
如何主要聚合字符串列(到列表、元组、带分隔符的字符串)?
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
如果它是数字列,请使用带有 astype
的 lambda 函数来转换为 string
:
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
,因为 automatic exclusion of 'nuisance' columns。这意味着所有数字列都被排除在外。
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
每个组的 size
的函数 GroupBy.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
的值。
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
返回一个对象,该对象的索引与被分组的对象相同(相同大小)。
有关详细信息,请参阅 the Pandas documentation。
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
如果您来自 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 ac 1 9 ac 2 8 ad 2 7 bd 3 6 ae 3 5
1. 与 SQL Group By 类似的行缩减聚合
1.1 如果 Pandas 版本 >=0.25
通过运行 print(pd.__version__)
检查您的 Pandas 版本。如果您的 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)
生成的数据表将如下所示:
key1 key2 mean_of_value1 sum_of_value2 count_of_value1 ac 1.5 17 2 ad 2.0 7 1 ae 3.0 5 1 bd 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,则运行上述代码会给您以下错误:
类型错误:聚合()缺少 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)
结果表将如下所示:
key1 key2 value1_mean value1_count value2_sum ac 1.5 2 17 ad 2.0 1 7 ae 3.0 1 5 bd 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)
生成的数据框将如下所示,其行数与原始数据框相同:
key1 key2 value1 value2 Total_of_value1_by_key1 ac 1 9 8 ac 2 8 8 ad 2 7 8 bd 3 6 3 ae 3 5 8
3.创建一个RANK列ROW_NUMBER() OVER (PARTITION BY ORDER BY)
最后,在某些情况下,您可能想要创建一个 rank 列,它是 ROW_NUMBER() OVER (PARTITION BY key1 ORDER BY value1 DESC, value2 ASC)
的 SQL 等效。
这是你如何做到的。
df['RN'] = df.sort_values(['value1','value2'], ascending=[False,True]) \
.groupby(['key1']) \
.cumcount() + 1
df.head(5)
注意:我们通过在每行末尾添加 \
来使代码多行。
以下是生成的数据框的样子:
键 1 键 2 值 1 值 2 RN ac 1 9 4 ac 2 8 3 ad 2 7 2 bd 3 6 1 ae 3 5 1
在上面的所有示例中,最终数据表将具有表结构,并且不会具有您可能在其他语法中获得的数据透视结构。
其他聚合运算符:
mean()
计算组的平均值
sum()
计算组值的总和
size()
计算组大小
count()
计算组数
std()
组的标准差
var()
计算组的方差
sem()
组均值的标准误
describe()
生成描述性统计信息
first()
计算组值中的第一个
last()
计算最后一个组值
nth()
如果 n 是列表,则取第 n 个值或子集
min()
计算组值的最小值
max()
计算组值的最大值
df
有一些 nan
时,这是否成立?
不定期副业成功案例分享