编辑推荐: |
来源csdn
,主要讲解了GroupBy机制,groups中的迭代,Dicts与Series分组,应用函数进行分组,根据索引等级分组,数据聚合等结合案例详细讲解。 |
|
import numpy as
np
import pandas as pd |
GroupBy机制
上图显示了一个分组背后的具体操作,当操作一个数据集按照某个key进行分组时,数据集首先会按组进行分割,然后再对每一组应用函数,最后返回分组后的结果。
df=pd.DataFrame({
'key1':['a','a','b','b','a'],
'key2':['one','two','one','two','one'],
'data1':np.random.randn(5),
'data2':np.random.randn(5)
})
df.loc[:,'data1'].groupby(df.loc[:,'key1']).mean()
#将data1列按照key1进行分组,再求均值
|
当需要按照多个key进行分组时,给groupby()传递一个列表即可,得到的结果是具有层级index的Series:
mean=df.loc[:,'data1'].groupby([df.loc[:,'key1'],df.loc[:,'key2']]).mean()
mean.unstack() |
当对整个数据集进行分组时,可以直接给groupby()传递key的值,此时不可再用loc()与iloc()方法,因为groupby()生成的是一个groupby对象,而不是DataFrame:
df.groupby(['key1','key2'])['data2'].mean() |
另一个应用于groupby()之后的方法为size():
df.groupby(['key1','key2']).size() |
groups中的迭代
GroupBy对象是一个可迭代对象:
for key,data
in df.groupby('key1'):
print(key)
print(data) |
根据Dicts与Series分组
建立映射关系如dict或series,将列先进行映射再进行分组。
people=pd.DataFrame(
np.random.randn(5,5),
index=['Joe','Steve','Wes','Jim','Travis'],
columns=['a','b','c','d','e']
)
people.iloc[2,[1,2]]=None
mapping={
'a':'red',
'b':'red',
'c':'blue',
'd':'blue',
'e':'red',
'f':'orange'
}
people.groupby(mapping,axis=1).mean()
|
map_series=pd.Series(mapping)
people.groupby(map_series,axis=1).mean() |
应用函数进行分组
比如对上述数据集,如需要对名字长度进行分组,则可以给groupby()传递一个函数作为参数:
people.groupby(len,axis=0).mean() |
根据索引等级分组
对于带层级索引的数据集,可以根据某一层级的索引名称进行分组,指定参数level=即可:
hier_df=pd.DataFrame(
np.random.randn(4,5),
columns=[['US','US','US','JP','JP'],
[1,3,5,1,3]]
)
hier_df.columns.names=['city','tensor']
hier_df.groupby(level='city',axis=1).count()
|
数据聚合
tips=pd.read_csv('examples/tips.csv')
tips.loc[:,'tip_pct']=tips.loc[:,'tip']/tips.loc[:,'total_bill']
tips.sample(3)
|
grouped=tips.groupby(['day','smoker'])
grouped['tip_pct'].mean() |
可以对groupby对象的agg()方法传递一个函数名列表,会返回一个groupby对象应用函数后生成的DataFrame:
grouped.agg(['min','max','mean']) |
返回无行索引的聚合数据
使用参数as_index=参数来返回一个无行索引的数据:
tips.groupby(['day','smoker'],as_index=False).mean() |
可见行索引被转换成了列值。
Apply
GroupBy()方法最普遍的目的就是在数据集上应用函数以筛选出想要的数据。比如用户自己写了一个top()函数,它会返回DataFrame指定列中值最大的n条记录:
def top(df,n=5,column='tip_pct'):
return df.sort_values(by=column).iloc[-n:,:] |
tips.groupby('smoker').apply(top,n=3)
#筛选3条tip_pct最大的数据 |
可以看到以’smoker’分组方法筛选出来的数据条目中还包含了由分组产生的’smoker’行索引,可以使用参数group_keys=来消除分组索引:
tips.groupby ('smoker',group_keys =False).apply(top,n=3)
#筛选3条tip_pct最大的数据 |
分位数与桶分析
frame=pd.DataFrame({
'data1':np.random.randn(1000),
'data2':np.random.randn(1000)
})
quartiles= pd.cut(frame.loc[:,'data1'],4) #以data1生成四个区间
|
def get_stats(group):
return {
'min':group.min(),
'max':group.max(),
'mean':group.mean()
}
|
grouped= frame.loc[:,'data2' ].groupby(quartiles)
#以data1的区间来对data2进行分组
grouped.apply(get_stats).unstack() |
示例
随机采样与改序
假设需要生成一组随机采样(有放回或无放回)来做蒙特卡洛分析,或者是用做机器学习项目中的数据子集,需要对原数据集进行随机采样或者改序。
#生成一副牌组Series
suits=['H','S','C','D']
base_name=['A']+list(range(2,11))+['J','Q','K']
cards=[]
for suit in suits:
cards.extend(str(num)+suit for num in base_name)
card_val=(list(range(1,14)))*4
deck=pd.Series(card_val,index=cards)
|
#抽牌函数
def draw(deck,n=5):
return deck.sample(n)
|
4H 4
6C 6
5C 5
AH 1
7C 7
dtype: int64
假设现在需要从每个花色中随机抽出两张牌,可以先按花色来对牌组进行分组再应用抽牌函数:
get_suit=lambda
card:card[-1] #card的最后一位表示花色,这里没太懂
deck.groupby (get_suit,group_keys=False).apply (draw,n=2) |
6C 6
10C 10
KD 13
QD 12
9H 9
JH 11
5S 5
6S 6
dtype: int64
分组的加权均值与相关系数
如果数据是带权重的,使用apply()方法可以很方便的计算加权平均值:
frame=pd.DataFrame({
'category':['a','a','b','b'],
'data':np.random.randn(4),
'weights':np.random.rand(4)
})
frame |
get_wavg=lambda
grouped:np.average(grouped.loc[:,'data'],weights=grouped.loc[:,'weights'])
frame.groupby('category').apply(get_wavg) |
在下一个例子前,先了解一下pct_change()方法:
obj=pd.Series(np.arange(1,5))
obj.pct_change() |
接下来我们导入一个真实数据集,进行相关性分析:
close_px=pd.read_csv ('examples/stock_px_2.csv', parse_dates=True,index_col=0)
close_px.info() |
使用pct_change()来计算日回报率:
eturns=close_px.pct_change().dropna()
returns.head() |
我们把数据按年进行聚合,再使用apply()方法显示以SPX为参照的各公司收益相关性:
get_year=lambda
timestamp:timestamp.year #此数据集的行索引为时间对象,无loc()方法
spx_cor=lambda x:x.corrwith(x.loc[:,'SPX']) |
returns.groupby(get_year).apply(spx_cor) |
还可以得到指定两家公司的年收益相关性:
AAPL_cor_MSFT=lambda
x:x.loc[:,'AAPL'].corr(x.loc[:,'MSFT'])
returns.groupby(get_year).apply(AAPL_cor_MSFT) |
2003 0.480868
2004 0.259024
2005 0.300093
2006 0.161735
2007 0.417738
2008 0.611901
2009 0.432738
2010 0.571946
2011 0.581987
dtype: float64
透视表与交叉表
pivot_table()方法与groupby()方法能得到相似的结果,不过pivot_table()方法能提供更强大的功能。
tips=pd.read_csv('examples/tips.csv')
tips.pivot_table(index=['day','smoker']) |
以上结果同样可以通过tips.groupby(['day','smoker'])得到。如果现在我们只想生成tip和size列的透视表,以time和day来分组:
tips.pivot_table(['tip','size'],index=['time','day'],columns='smoker') |
交叉表
以两个Series或list组成一个表
pd.crossta([tips.loc[:,'time'], tips.loc[:,'day']], tips.loc[:,'smoker'], margins=True)
#margins显示总额 |
|