一、groupby
类似excel的数据透视表,一般是按照行进行分组,使用方法如下。
df.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True,
squeeze=False, observed=False, **kwargs)
分组得到的直接结果是一个DataFrameGroupBy对象。
df = pd.DataFrame({\'A\':[\'zhao\',\'li\',\'wang\',\'li\',\'zhao\'],
\'B\':[\'one\',\'one\',\'two\',\'three\',\'two\'],
\'C\':np.arange(1,6),
\'D\':np.arange(6,11)})
print(df)
print(df.groupby(\'A\'))
print(type(df.groupby(\'A\')))
# A B C D
# 0 zhao one 1 6
# 1 li one 2 7
# 2 wang two 3 8
# 3 li three 4 9
# 4 zhao two 5 10
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000000001E6C550>
# <class \'pandas.core.groupby.generic.DataFrameGroupBy\'>
分组后的直接结果是一个可迭代对象,可迭代对象中的每一个元素都是一个元组,元组的第一个值为分组的名称,第二个值为DataFrame。可通过for或转换为list、元组查看每一个元素。
for n,p in df.groupby(\'A\'): print(type(p)) print(n) print(p) print(\'-------------------------\') # <class \'pandas.core.frame.DataFrame\'> # li # A B C D # 1 li one 2 7 # 3 li three 4 9 # ------------------------- # <class \'pandas.core.frame.DataFrame\'> # wang # A B C D # 2 wang two 3 8 # ------------------------- # <class \'pandas.core.frame.DataFrame\'> # zhao # A B C D # 0 zhao one 1 6 # 4 zhao two 5 10 # -------------------------
通过get_group(\'分组名称\')获取某一个分组的内容
groups是一个字典,字典的键为各分组名称,值为列表包含分组所在的索引行,可通过groups[\'分组名称\']查看某一个分组所在的行
print(df.groupby(\'A\').get_group(\'zhao\')) #获取分组后的zhao组 # A B C D # 0 zhao one 1 6 # 4 zhao two 5 10 print(df.groupby([\'A\',\'B\']).groups) print(df.groupby([\'A\',\'B\']).groups[(\'li\', \'one\')]) # {(\'li\', \'one\'): Int64Index([1], dtype=\'int64\'), (\'li\', \'three\'): Int64Index([3], dtype=\'int64\'), (\'wang\', \'two\'): Int64Index([2], dtype=\'int64\'), (\'zhao\', \'one\'): Int64Index([0], dtype=\'int64\'), (\'zhao\', \'two\'): Int64Index([4], dtype=\'int64\')} # Int64Index([1], dtype=\'int64\')
size( )统计每个分组的长度
print(df.groupby(\'A\').size()) print(type(df.groupby(\'A\').size())) # A # li 2 # wang 1 # zhao 2 # dtype: int64 # <class \'pandas.core.series.Series\'>
分组可对单列或者多列进行,如果对多列进行分组,需要写在一个列表内。
df = pd.DataFrame({\'A\':[\'zhao\',\'li\',\'wang\',\'li\',\'zhao\'],
\'B\':[\'one\',\'one\',\'two\',\'three\',\'two\'],
\'C\':np.arange(1,6),
\'D\':np.arange(6,11)})
print(df.groupby(\'A\').sum()) #以A列分组,对其他元素为数值的列进行求和,忽略非数值元素的列
print(\'---------------------\')
print(df.groupby([\'A\',\'B\']).sum()) #以A列和B列分组,对其他列求和,忽略非数值元素的列
print(\'---------------------\')
print(df.groupby(\'A\')[\'D\'].sum()) #以A列分组,再对D列求和
C D
# A
# li 6 16
# wang 3 8
# zhao 6 16
# ---------------------
# C D
# A B
# li one 2 7
# three 4 9
# wang two 3 8
# zhao one 1 6
# two 5 10
# ---------------------
# A
# li 16
# wang 8
# zhao 16
# Name: D, dtype: int32
按照index分组,将index相同的分为一组,分组依据level=0
df = pd.DataFrame({\'data1\':[1,2,3,4],\'data2\':[3,4,5,6],\'A\':[5,6,7,8],\'B\':[7,8,9,0]},index=[1,2,3,1])
print(df) #groupby(level=0)表示将索引相同的行分为一组
print(df.groupby(level=0).first()) #分组后组内第一个值
print(df.groupby(level=0).last()) #分组后组内最后一个值
print(df.groupby(level=0).max()) #分组后组内最大值
print(df.groupby(level=0).min()) #分组后组内最小值
print(df.groupby(level=0).sum()) #分组后组内元素的和
print(df.groupby(level=0).mean()) #分组后组内元素的平均值
print(df.groupby(level=0).median())#分组后组内元素的中位数
print(df.groupby(level=0).count()) #分组后组内元素的个数
print(df.groupby(level=0).std()) #分组后组内元素的方差
print(df.groupby(level=0).prod()) #分组后组内元素的乘积
print(df.groupby(level=0).describe())#分组后组内元素的count、mean、std、min、25%、50%、75%。max
按照index长度分组
df = pd.DataFrame({\'A\':[1,2,3,4],\'B\':[3,4,5,6],\'D\':[5,6,7,8],\'D\':[7,8,9,0]},index=[\'a\',\'ab\',\'cd\',\'e\'])
print(df)
print(df.groupby(len).sum())
# A B D
# a 1 3 7
# ab 2 4 8
# cd 3 5 9
# e 4 6 0
# A B D
# 1 5 9 7
# 2 5 9 17
按照数据类型进行分组,df.dtypes可获得每个数据列的数据类型,数据类型是对列而言,因此按数据类型分组需指明axis=1
df = pd.DataFrame({\'data1\':[1,2],\'data2\':[3,4],\'A\':[\'a\',\'b\'],\'B\':[\'c\',\'d\']})
print(df)
print(df.groupby(df.dtypes,axis=1).sum())
for n,p in df.groupby(df.dtypes,axis=1):
print(n)
print(p)
print(\'-------\')
# data1 data2 A B
# 0 1 3 a c
# 1 2 4 b d
# int64 object
# 0 4 ac
# 1 6 bd
# int64
# data1 data2
# 0 1 3
# 1 2 4
# -------
# object
# A B
# 0 a c
# 1 b d
# -------
按照字典分组,需定义一个字典,键为列名称,值为对应的分组名称,按照列分组需要指明axis=1
例如下面例子中的map,定义data1列和A列属于分组key1,data2列数组分组key2,B列属于分组key3
df = pd.DataFrame({\'data1\':[1,2],\'data2\':[3,4],\'A\':[\'a\',\'b\'],\'B\':[\'c\',\'d\']})
map = {\'data1\':\'key1\',\'data2\':\'key2\',\'A\':\'key1\',\'B\':\'key3\'}
for i,p in df.groupby(map,axis=1):
print(i)
print(p)
print(\'----------\')
# key1
# data1 A
# 0 1 a
# 1 2 b
# ----------
# key2
# data2
# 0 3
# 1 4
# ----------
# key3
# B
# 0 c
# 1 d
# ----------
多函数计算agg(函数1,函数2)
对分组后的每个组既进行第一个函数的计算,又进行第二个函数的计算。
df = pd.DataFrame({\'a\':[1,2,3,1],\'b\':[\'a\',\'b\',\'a\',\'b\'],\'c\':np.arange(3,7),\'d\':np.arange(2,6)})
print(df)
print(df.groupby(\'a\').agg([\'mean\',\'sum\'])) #b列为非数值,忽略
print(df.groupby(\'b\').agg([np.sum,np.mean]))
# a b c d
# 0 1 a 3 2
# 1 2 b 4 3
# 2 3 a 5 4
# 3 1 b 6 5
# c d
# mean sum mean sum
# a
# 1 4.5 9 3.5 7
# 2 4.0 4 3.0 3
# 3 5.0 5 4.0 4
# a c d
# sum mean sum mean sum mean
# b
# a 4 2.0 8 4 6 3
# b 3 1.5 10 5 8 4
多函数计算后的默认column为函数名称,也可以通过字典自定义column。
df = pd.DataFrame({\'a\':[1,2,3,1],\'b\':[\'a\',\'b\',\'a\',\'b\'],\'c\':np.arange(3,7),\'d\':np.arange(2,6)})
print(df.groupby(\'b\')[\'c\'].agg({\'result_sum\':np.sum,\'result_mean\':np.mean}))
# result_sum result_mean
# b
# a 8 4
# b 10 5
二、transform
上述groupby如果通过行分组再进行求和、均值等,会出现结果与原对象的行数不一致的情况,而通过transform得到的结果与原对象的结果行数一致。
df = pd.DataFrame({\'A\':[\'zhao\',\'li\',\'wang\',\'li\',\'zhao\'],
\'B\':[\'one\',\'one\',\'two\',\'two\',\'three\'],
\'C\':np.arange(1,6),
\'D\':np.arange(5,10)})
print(df)
print(df.groupby(\'B\').mean()) #只包含one、two、three三行
print(df.groupby(\'B\').transform(np.mean)) #结果为5行,B列内容相同的行的结果相同
# A B C D
# 0 zhao one 1 5
# 1 li one 2 6
# 2 wang two 3 7
# 3 li two 4 8
# 4 zhao three 5 9
# C D
# B
# one 1.5 5.5
# three 5.0 9.0
# two 3.5 7.5
# C D
# 0 1.5 5.5
# 1 1.5 5.5
# 2 3.5 7.5
# 3 3.5 7.5
# 4 5.0 9.0
三、applay
上述groupby分组后都是使用python定义好的sum、mean、max等进行统计计算,apply可以自定义统计方法。
def func(df,n): return *** df.groupby(\'*\').apply(func,n) #自定义函数func,第一个参数为pandas对象,并返回值 #分组后使用apply函数,将自定义函数的名称作为第一个参数,第二个参数传递给自定义函数的第二个参数
df = pd.DataFrame({\'A\':[\'zhao\',\'li\',\'wang\',\'li\',\'zhao\',\'zhao\'],
\'B\':[\'one\',\'one\',\'two\',\'two\',\'three\',\'two\'],
\'C\':np.arange(1,7),
\'D\':np.arange(4,10)})
def f(d,n):
return d.sort_index()[:n] #按照索引排序,并返回前n行
print(df)
print(df.groupby(\'A\').apply(f,2)) #按照A分组,对结果按索引排序,并返回每组的前n行
A B C D
# 0 zhao one 1 4
# 1 li one 2 5
# 2 wang two 3 6
# 3 li two 4 7
# 4 zhao three 5 8
# 5 zhao two 6 9
# A B C D
# A
# li 1 li one 2 5
# 3 li two 4 7
# wang 2 wang two 3 6
# zhao 0 zhao one 1 4
# 4 zhao three 5 8
四、透视表pivot_table()、pivot()
pivot_table(self, values=None, index=None, columns=None, aggfunc=\'mean\', fill_value=None, margins=False, dropna=True, margins_name=\'All\')
可类比excel的数据透视表进行理解,使用方法pd.pivot_table( df , ...),或直接使用df.pivot_table( ... )
- values:透视后对哪一列进行计算
- index:按照哪一列进行分组
- columns:透视后除了values,还包含哪些列
- aggfunc:对values进行计算的方法,默认为平均值
- fill_value:对空值使用fill_value指定的值填充,默认为NaN
import numpy as np import pandas as pd date = [\'2019/5/1\',\'2019/5/2\',\'2019/5/3\']*3 df = pd.DataFrame({\'date\':pd.to_datetime(date),\'key\':list(\'abcbacbca\'),\'value\':np.arange(1,10)}) print(df) print(df.pivot_table(index=\'date\',values=\'value\',columns=\'key\',aggfunc=np.sum)) print(df.pivot_table(index=[\'date\',\'key\'],values=\'value\',aggfunc=np.sum)) # date key value # 0 2019-05-01 a 1 # 1 2019-05-02 b 2 # 2 2019-05-03 c 3 # 3 2019-05-01 b 4 # 4 2019-05-02 a 5 # 5 2019-05-03 c 6 # 6 2019-05-01 b 7 # 7 2019-05-02 c 8 # 8 2019-05-03 a 9 # key a b c # date # 2019-05-01 1.0 11.0 NaN # 2019-05-02 5.0 2.0 8.0 # 2019-05-03 9.0 NaN 9.0 # value # date key # 2019-05-01 a 1 # b 11 # 2019-05-02 a 5 # b 2 # c 8 # 2019-05-03 a 9 # c 9
pivot()也是用来生成透视表的,结果为一个二维的表格,结果中可能会存在空值,但是与pivot_table()用法和结果稍有不同。
pivot(data, index=None, columns=None, values=None),使用方法pd.pivot(df,...)或者df.pivot()
index 透视行
columns 透视列
values 对哪一列进行透视
date = [\'2019/5/1\',\'2019/5/2\',\'2019/5/3\']*3 df = pd.DataFrame({\'date\':pd.to_datetime(date),\'key\':list(\'abcbabccd\'),\'value\':np.arange(1,10)}) res = pd.pivot(df,\'date\',\'key\',\'value\') print(res) # key a b c d # date # 2019-05-01 1.0 4.0 7.0 NaN # 2019-05-02 5.0 2.0 8.0 NaN # 2019-05-03 NaN 6.0 3.0 9.0
五、交叉表crossta()
交叉表默认用来统计元素出现的频数,使用方法pd.crosstab(Seris1,Seris2)
crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None,
margins=False, margins_name=\'All\', dropna=True, normalize=False)
- index:行的统计依据
- columns:列的统计依据
- values:在行为index、列为columns的基础上,对values指定的列进行统计,默认为None,表示统计频数
- aggfunc:统计方法,agggunc和values必须同时使用
- normalize:默认为False,值设置为True表示统计频率
- margins:默认为False,值设置为True表示对结果添加一列,对每列的和进行统计,同时添加一行,对每行的和进行统计
- rownames和colnames:默认为None,即显示index和columns的名称
df = pd.DataFrame([[1,2,3,1,3],[2,\'a\',\'a\',2,1],[3,np.nan,\'a\',\'b\',2],[np.nan,\'a\',\'b\',1,2],[4,1,\'c\',\'b\',2]],columns=[\'A\',\'B\',\'C\',\'D\',\'E\']) print(pd.crosstab(df[\'A\'],df[\'B\'])) #表示统计A为1且B为1出现的次数,A为2且B为1出现的次数,A为4且B为1出现的次数…… # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0
可以看到A为np.nan、B为np.nan的两行在统计时都被忽略了。
normalize=True会将上述结果的非0值显示为小数试的百分比
print(pd.crosstab(df[\'A\'],df[\'B\'],normalize=True)) # B 1 2 a # A # 1.0 0.000000 0.333333 0.000000 # 2.0 0.000000 0.000000 0.333333 # 4.0 0.333333 0.000000 0.000000
同时指定values=df[\'E\']和aggfunc=np.sum,表示在A和B对应值得条件下,对E列求和
print(pd.crosstab(df[\'A\'],df[\'B\'],values=df[\'E\'],aggfunc=np.sum)) # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0
margins增加行和列的总数统计
print(pd.crosstab(df[\'A\'],df[\'B\'])) print(pd.crosstab(df[\'A\'],df[\'B\'],margins=True)) # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0 # B 1 2 a All # A # 1.0 0 1 0 1 # 2.0 0 0 1 1 # 4.0 1 0 0 1 # All 1 1 1 3
rownames和colnames自定义结果显示的行和列名称
print(pd.crosstab(df[\'A\'],df[\'B\'],rownames=[\'AAA\'],colnames=[\'BBB\'])) # BBB 1 2 a # AAA # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0