pyhton-聚合操作 #将t按照用户id进行分组,然后统计所有用户收取的优惠券数目,并初始化一个索引值 t = t . groupby ( 'user_id' ). agg ( 'sum' ). reset_index () #以User_id,Date_received分组, 组内的数据
pyhton-聚合操作
#将t按照用户id进行分组,然后统计所有用户收取的优惠券数目,并初始化一个索引值t = t.groupby('user_id').agg('sum').reset_index()
#以User_id,Date_received分组, 组内的数据都用冒号连接
t2 = t2.groupby(['User_id','Coupon_id'])['Date_received'].agg(lambda x:':'.join(x)).reset_index()
t = dataset3[['User_id']]
#增加一列
t['this_month_user_receive_all_coupon_count'] = 1
t.head()
#重建索引,聚合,分组求和
t = t.groupby('User_id').agg('sum').reset_index()
t.head()
#1.2 对以下字段分别剔除缺失值和0值后进行groupby count
columns = [
'deposit_cur_balabce' #存款当前余额
,'financial_cur_balabce' #理财当前余额
,'loan_cur_balabce' #贷款当前金额
,'own_products_sum' #持有产品数
]
#循环取这几列的数据
for i in columns:
temp = df_crm[[i]+['cust_isn','last_etl_acg_dt']].dropna()
# 不等于0
temp = temp.loc[temp[i]!=0,:]
temp = temp.groupby(['cust_isn','last_etl_acg_dt'],as_index=False).count()
#拼接字段
temp.columns = ['cust_isn','last_etl_acg_dt']+['crm_count_'+i]
fea_crm = pd.merge(fea_crm,temp,on = ['cust_isn','last_etl_acg_dt'],how = 'left')
处理距离数据
显示商品销量和距离的关系t4 = merchant3[(merchant3.date != 'null')&(merchant3.coupon_id != 'null')][['merchant_id','distance']]
#把数据中的null值全部替换为-1
t4.replace('null',-1,inplace=True)
t4.distance = t4.distance.astype('int')
#再把数据中的-1全部替换为NaN
t4.replace(-1,np.nan,inplace=True)
#返回用户离商品的距离最小值
t5 = t4.groupby('merchant_id').agg('min').reset_index()
t5.rename(columns={'distance':'merchant_min_distance'},inplace = True)
#返回用户离商品的距离最大值
t6 = t4.groupby('merchant_id').agg('max').reset_index()
t6.rename(columns={'distance':'merchant_max_distance'},inplace = True)
#print(t6)
#返回距离的平均值
t7 = t4.groupby('merchant_id').agg('mean').reset_index()
t7.rename(columns = {'distance':'merchant_mean_distance'},inplace= True)
#返回距离的中位值
t8 = t4.groupby('merchant_id').agg('median').reset_index()
t8.rename(columns={'distance':'merchant_median_distance'},inplace = True)
聚合求均值
Data Aggregation and Group Operations计算分组摘要统计,如计数、平均值、标准差,或⽤户⾃定
# 义函数。
# 计算分组的概述统计,⽐如数量、平均值或标准差,或是⽤
# 户定义的函数。
# 应⽤组内转换或其他运算,如规格化、线性回归、排名或选
# 取⼦集等。
# 计算透视表或交叉表。
# 执⾏分位数分析以及其它统计分组分析。
# 第10章 数据聚合与分组运算
# 计算分组摘要统计,如计数、平均值、标准差,或⽤户⾃定
# 义函数。
# 计算分组的概述统计,⽐如数量、平均值或标准差,或是⽤
# 户定义的函数。
# 应⽤组内转换或其他运算,如规格化、线性回归、排名或选
# 取⼦集等。
# 计算透视表或交叉表。
# 执⾏分位数分析以及其它统计分组分析。
import numpy as np
import pandas as pd
PREVIOUS_MAX_ROWS = pd.options.display.max_rows
pd.options.display.max_rows = 20
np.random.seed(12345)
import matplotlib.pyplot as plt
plt.rc('figure', figsize=(10, 6))
np.set_printoptions(precision=4, suppress=True)
GroupBy Mechanics
# GroupBy机制
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
key1 key2 data1 data2
0 a one -0.204708 1.393406
1 a two 0.478943 0.092908
2 b one -0.519439 0.281746
3 b two -0.555730 0.769023
4 a one 1.965781 1.246435
grouped = df['data1'].groupby(df['key1'])
grouped.head()
0 -0.204708
1 0.478943
2 -0.519439
3 -0.555730
4 1.965781
Name: data1, dtype: float64
# 调⽤GroupBy的mean⽅法来计算分组平均值:
grouped.mean()
key1
a 0.746672
b -0.537585
Name: data1, dtype: float64
# ⼊多个数组的列表,就会得到不同的结果: 求取平均值
means = df['data1'].groupby([df['key1'], df['key2']]).mean()
means
key1 key2
a one 0.880536
two 0.478943
b one -0.519439
two -0.555730
Name: data1, dtype: float64
透视图
# 这⾥,我通过两个键对数据进⾏了分组,得到的Series具有⼀个
# 层次化索引(由唯⼀的键对组成): 透视图
means.unstack()
states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
years = np.array([2005, 2005, 2006, 2005, 2006])
df['data1'].groupby([states, years]).mean()
California 2005 0.478943
2006 -0.519439
Ohio 2005 -0.380219
2006 1.965781
Name: data1, dtype: float64
df.groupby('key1').mean()
df.groupby(['key1', 'key2']).mean()
data1 data2
key1 key2
a one 0.880536 1.319920
two 0.478943 0.092908
b one -0.519439 0.281746
two -0.555730 0.769023
df.groupby(['key1', 'key2']).size()
key1 key2
a one 2
two 1
b one 1
two 1
dtype:Iterating Over Groups¶
# 对分组进⾏迭代
# GroupBy对象⽀持迭代,可以产⽣⼀组⼆元元组(由分组名和数
# 据块组成)。看下⾯的例⼦:
for name, group in df.groupby('key1'):
print(name)
print(group)
a
key1 key2 data1 data2
0 a one -0.204708 1.393406
1 a two 0.478943 0.092908
4 a one 1.965781 1.246435
b
key1 key2 data1 data2
2 b one -0.519439 0.281746
3 b two -0.555730 0.769023
对于多重键的情况,元组的第⼀个元素将会是由键值组成的元
# 382
# 组:
# 对于多重键的情况,元组的第⼀个元素将会是由键值组成的元
# 382
# 组:
for (k1, k2), group in df.groupby(['key1', 'key2']):
print((k1, k2))
print(group)
('a', 'one')
key1 key2 data1 data2
0 a one -0.204708 1.393406
4 a one 1.965781 1.246435
('a', 'two')
key1 key2 data1 data2
1 a two 0.478943 0.092908
('b', 'one')
key1 key2 data1 data2
2 b one -0.519439 0.281746
('b', 'two')
key1 key2 data1 data2
3 b two -0.55573 0.769023
pieces = dict(list(df.groupby('key1')))
pieces['b']
key1 key2 data1 data2
2 b one -0.519439 0.281746
3 b two -0.555730 0.769023
df.dtypes
grouped = df.groupby(df.dtypes, axis=1)
# 可以如下打印分组:
for dtype, group in grouped:
print(dtype)
print(group)
a
0 -0.204708
1 0.478943
4 1.965781
Name: data1, dtype: float64
b
2 -0.519439
3 -0.555730
Name: data1, dtype: float64
Selecting a Column or Subset of Columns
df.groupby('key1')['data1'] df.groupby('key1')[['data2']]
df['data1'].groupby(df['key1']) df[['data2']].groupby(df['key1'])
选取⼀列或列的⼦集
# 对于由DataFrame产⽣的GroupBy对象,如果⽤⼀个(单个字符
# 串)或⼀组(字符串数组)列名对其进⾏索引,就能实现选取部
# 分列进⾏聚合的⽬的。也就是说:
# 选取⼀列或列的⼦集
# 对于由DataFrame产⽣的GroupBy对象,如果⽤⼀个(单个字符
# 串)或⼀组(字符串数组)列名对其进⾏索引,就能实现选取部
# 分列进⾏聚合的⽬的。也就是说:
df.groupby(['key1', 'key2'])[['data2']].mean()
data2
key1 key2
a one 1.319920
two 0.092908
b one 0.281746
two 0.769023
尤其对于⼤数据集,很可能只需要对部分列进⾏聚合。例如,在
# 前⾯那个数据集中,如果只需计算data2列的平均值并以
# DataFrame形式得到结果,可以这样写:
# 尤其对于⼤数据集,很可能只需要对部分列进⾏聚合。例如,在
# 前⾯那个数据集中,如果只需计算data2列的平均值并以
# DataFrame形式得到结果,可以这样写:
s_grouped = df.groupby(['key1', 'key2'])['data2']
s_grouped
s_grouped.mean()
key1 key2
a one 1.319920
two 0.092908
b one 0.281746
two 0.769023
Name: data2, dtype: float64
Grouping with Dicts and Series
2
# 通过字典或Series进⾏分组
# 除数组以外,分组信息还可以其他形式存在。来看另⼀个示例
# DataFrame:
people = pd.DataFrame(np.random.randn(5, 5),
columns=['a', 'b', 'c', 'd', 'e'],
index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
people.iloc[2:3, [1, 2]] = np.nan # Add a few NA values
people
a b c d e
Joe 0.862580 -0.010032 0.050009 0.670216 0.852965
Steve -0.955869 -0.023493 -2.304234 -0.652469 -1.218302
Wes -1.332610 NaN NaN 0.690002 1.001543
Jim -0.503087 -0.622274 -0.921169 -0.726213 0.222896
Travis 0.051316 -1.157719 0.816707 0.433610 1.010737
# 现在,假设已知列的分组关系,并希望根据分组计算列的和:
mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f' : 'orange'}
# 通过映射分组
by_column = people.groupby(mapping, axis=1)
by_column.sum()
blue red
Joe 0.720225 1.705513
Steve -2.956703 -2.197664
Wes 0.690002 -0.331066
Jim -1.647382 -0.902466
Travis 1.250317 -0.095666
map_series = pd.Series(mapping)
map_series
people.groupby(map_series, axis=1).count()
blue red
Joe 2 3
Steve 2 3
Wes 1 2
Jim 2 3
Travis 2 3
Grouping with Functions
#groupby 统计 计数
date_count = data[['cust_isn','last_etl_acg_dt']].groupby('last_etl_acg_dt',as_index=False).count()
# Series也有同样的功能,它可以被看做⼀个固定⼤⼩的映射:
# 通过函数进⾏分组
# ⽐起使⽤字典或Series,使⽤Python函数是⼀种更原⽣的⽅法定
# 义分组映射。任何被当做分组键的函数都会在各个索引值上被调
# ⽤⼀次,其返回值就会被⽤作分组名称。具体点说,以上⼀⼩节
# 的示例DataFrame为例,其索引值为⼈的名字。你可以计算⼀个
# 字符串⻓度的数组,更简单的⽅法是传⼊len函数:
people.groupby(len).sum()
a b c d e
3 -0.973117 -0.632306 -0.871159 0.634004 2.077404
5 -0.955869 -0.023493 -2.304234 -0.652469 -1.218302
6 0.051316 -1.157719 0.816707 0.433610 1.010737
key_list = ['one', 'one', 'one', 'two', 'two']
people.groupby([len, key_list]).min()
a b c d e
3 one -1.332610 -0.010032 0.050009 0.670216 0.852965
two -0.503087 -0.622274 -0.921169 -0.726213 0.222896
5 one -0.955869 -0.023493 -2.304234 -0.652469 -1.218302
6 two 0.051316 -1.157719 0.816707 0.433610 1.010737
Grouping by Index Levels
根据索引级别分组
# 层次化索引数据集最⽅便的地⽅就在于它能够根据轴索引的⼀个
# 级别进⾏聚合:
# 根据索引级别分组
# 层次化索引数据集最⽅便的地⽅就在于它能够根据轴索引的⼀个
# 级别进⾏聚合:
columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'],
[1, 3, 5, 1, 3]],
names=['cty', 'tenor'])
hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns)
hier_df
cty US JP
tenor 1 3 5 1 3
0 -0.036264 1.095390 0.980928 -0.589488 1.581700
1 -0.528735 0.457002 0.929969 -1.569271 -1.022487
2 -0.402827 0.220487 -0.193401 0.669158 -1.648985
3 -2.252797 -1.166832 0.353607 0.702110 -0.274569
# 要根据级别分组,使⽤level关键字传递级别序号或名字:
hier_df.groupby(level='cty', axis=1).count()
cty JP US
0 2 3
1 2 3
2 2 3
3 2 3
聚合操作与apply应用
Data Aggregation# 10.2 数据聚合
# 聚合指的是任何能够从数组产⽣标量值的数据转换过程。之前的
# 例⼦已经⽤过⼀些,⽐如mean、count、min以及sum等。你可
# 能想知道在GroupBy对象上调⽤mean()时究竟发⽣了什么。许多
# 常⻅的聚合运算(如表10-1所示)都有进⾏优化。然⽽,除了这
# 些⽅法,你还可以使⽤其它的。
# 例如,quantile可以计算Series或DataFrame389
# 列的样本分位数。
df
grouped = df.groupby('key1')
grouped['data1'].quantile(0.9)
key1
a 1.668413
b -0.523068
Name: data1, dtype: float64
如果要使⽤你⾃⼰的聚合函数,只需将其传⼊aggregate或agg⽅
# 法即可: 使用自己的聚合函数
# 如果要使⽤你⾃⼰的聚合函数,只需将其传⼊aggregate或agg⽅
# 法即可: 使用自己的聚合函数
def peak_to_peak(arr):
return arr.max() - arr.min()
grouped.agg(peak_to_peak)
grouped.describe()
Column-Wise and Multiple Function Application
⾯向列的多函数应⽤
# 回到前⾯⼩费的例⼦。使⽤read_csv导⼊数据之后,我们添加了
# ⼀个⼩费百分⽐的列tip_pct:
# ⾯向列的多函数应⽤
# 回到前⾯⼩费的例⼦。使⽤read_csv导⼊数据之后,我们添加了
# ⼀个⼩费百分⽐的列tip_pct:
tips = pd.read_csv('examples/tips.csv')
# Add tip percentage of total bill
tips['tip_pct'] = tips['tip'] / tips['total_bill']
tips[:6]
total_bill tip smoker day time size tip_pct
0 16.99 1.01 No Sun Dinner 2 0.059447
1 10.34 1.66 No Sun Dinner 3 0.160542
2 21.01 3.50 No Sun Dinner 3 0.166587
3 23.68 3.31 No Sun Dinner 2 0.139780
4 24.59 3.61 No Sun Dinner 4 0.146808
5 25.29 4.71 No Sun Dinner 4 0.186240
# 你已经看到,对Series或DataFrame列的聚合运算其实就是使⽤
# aggregate(使⽤⾃定义函数)或调⽤诸如mean、std之类的⽅
# 法。然⽽,你可能希望对不同的列使⽤不同的聚合函数,或⼀次
# 应⽤多个函数。其实这也好办,我将通过⼀些示例来进⾏讲解。
# ⾸先,我根据天和smoker对tips进⾏分组:
grouped = tips.groupby(['day', 'smoker'])
grouped_pct = grouped['tip_pct']
grouped_pct.agg('mean')
day smoker
Fri No 0.151650
Yes 0.174783
Sat No 0.158048
Yes 0.147906
Sun No 0.160113
Yes 0.187250
Thur No 0.160298
Yes 0.163863
Name: tip_pct, dtype: float64
# 如果传⼊⼀组函数或函数名,得到的DataFrame的列就会以相应
# 的函数命名:
grouped_pct.agg(['mean', 'std', peak_to_peak])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-41-9758eaedd62a> in <module>()
1 # 如果传⼊⼀组函数或函数名,得到的DataFrame的列就会以相应
2 # 的函数命名:
----> 3 grouped_pct.agg(['mean', 'std', peak_to_peak])
NameError: name 'peak_to_peak' is not defined
你并⾮⼀定要接受GroupBy⾃动给出的那些列名,特别是lambda
# 函数,它们的名称是'<lambda>',这样的辨识度就很低了(通过
# 函数的name属性看看就知道了)。因此,如果传⼊的是⼀个由
# (name,function)元组组成的列表,则各元组的第⼀个元素就会被
# ⽤作DataFrame的列名(可以将这种⼆元元组列表看做⼀个有序
# 映射):
# 你并⾮⼀定要接受GroupBy⾃动给出的那些列名,特别是lambda
# 函数,它们的名称是'<lambda>',这样的辨识度就很低了(通过
# 函数的name属性看看就知道了)。因此,如果传⼊的是⼀个由
# (name,function)元组组成的列表,则各元组的第⼀个元素就会被
# ⽤作DataFrame的列名(可以将这种⼆元元组列表看做⼀个有序
# 映射):
grouped_pct.agg([('foo', 'mean'), ('bar', np.std)])
foo bar
day smoker
Fri No 0.151650 0.028123
Yes 0.174783 0.051293
Sat No 0.158048 0.039767
Yes 0.147906 0.061375
Sun No 0.160113 0.042347
Yes 0.187250 0.154134
Thur No 0.160298 0.038774
Yes 0.163863 0.039389
对于DataFrame,你还有更多选择,你可以定义⼀组应⽤于全部
# 列的⼀组函数,或不同的列应⽤不同的函数。假设我们想要对
# tip_pct和total_bill列计算三个统计信息:
# 对于DataFrame,你还有更多选择,你可以定义⼀组应⽤于全部
# 列的⼀组函数,或不同的列应⽤不同的函数。假设我们想要对
# tip_pct和total_bill列计算三个统计信息:
functions = ['count', 'mean', 'max']
result = grouped['tip_pct', 'total_bill'].agg(functions)
result
tip_pct total_bill
count mean max count mean max
day smoker
Fri No 4 0.151650 0.187735 4 18.420000 22.75
Yes 15 0.174783 0.263480 15 16.813333 40.17
Sat No 45 0.158048 0.291990 45 19.661778 48.33
Yes 42 0.147906 0.325733 42 21.276667 50.81
Sun No 57 0.160113 0.252672 57 20.506667 48.17
Yes 19 0.187250 0.710345 19 24.120000 45.35
Thur No 45 0.160298 0.266312 45 17.113111 41.19
Yes 17 0.163863 0.241255 17 19.190588 43.11
result['tip_pct']
ftuples = [('Durchschnitt', 'mean'), ('Abweichung', np.var)]
grouped['tip_pct', 'total_bill'].agg(ftuples)
现在,假设你想要对⼀个列或不同的列应⽤不同的函数。具体的
# 办法是向agg传⼊⼀个从列名映射到函数的字典:
# 现在,假设你想要对⼀个列或不同的列应⽤不同的函数。具体的
# 办法是向agg传⼊⼀个从列名映射到函数的字典:
grouped.agg({'tip' : np.max, 'size' : 'sum'})
grouped.agg({'tip_pct' : ['min', 'max', 'mean', 'std'],
'size' : 'sum'})
tip_pct size
min max mean std sum
day smoker
Fri No 0.120385 0.187735 0.151650 0.028123 9
Yes 0.103555 0.263480 0.174783 0.051293 31
Sat No 0.056797 0.291990 0.158048 0.039767 115
Yes 0.035638 0.325733 0.147906 0.061375 104
Sun No 0.059447 0.252672 0.160113 0.042347 167
Yes 0.065660 0.710345 0.187250 0.154134 49
Thur No 0.072961 0.266312 0.160298 0.038774 112
Yes 0.090014 0.241255 0.163863 0.039389 40
Returning Aggregated Data Without Row Indexes
以“没有⾏索引”的形式返回聚合数据
# 到⽬前为⽌,所有示例中的聚合数据都有由唯⼀的分组键组成的
# 索引(可能还是层次化的)。由于并不总是需要如此,所以你可
# 以向groupby传⼊as_index=False以禁⽤该功能:
# 以“没有⾏索引”的形式返回聚合数据
# 到⽬前为⽌,所有示例中的聚合数据都有由唯⼀的分组键组成的
# 索引(可能还是层次化的)。由于并不总是需要如此,所以你可
# 以向groupby传⼊as_index=False以禁⽤该功能:
tips.groupby(['day', 'smoker'], as_index=False).mean()
day smoker total_bill tip size tip_pct
0 Fri No 18.420000 2.812500 2.250000 0.151650
1 Fri Yes 16.813333 2.714000 2.066667 0.174783
2 Sat No 19.661778 3.102889 2.555556 0.158048
3 Sat Yes 21.276667 2.875476 2.476190 0.147906
4 Sun No 20.506667 3.167895 2.929825 0.160113
5 Sun Yes 24.120000 3.516842 2.578947 0.187250
6 Thur No 17.113111 2.673778 2.488889 0.160298
7 Thur Yes 19.190588 3.030000 2.352941 0.163863
Apply: General split-apply-combine
10.3 apply:⼀般性的“拆分-应⽤-合并”
# 最通⽤的GroupBy⽅法是apply,本节剩余部分将重点讲解它。
# 如图10-2所示,apply会将待处理的对象拆分成多个⽚段,然后
# 对各⽚段调⽤传⼊的函数,最后尝试将各⽚段组合到⼀起。
# 10.3 apply:⼀般性的“拆分-应⽤-合并”
# 最通⽤的GroupBy⽅法是apply,本节剩余部分将重点讲解它。
# 如图10-2所示,apply会将待处理的对象拆分成多个⽚段,然后
# 对各⽚段调⽤传⼊的函数,最后尝试将各⽚段组合到⼀起。
def top(df, n=5, column='tip_pct'):
return df.sort_values(by=column)[-n:]
top(tips, n=6)
total_bill tip smoker day time size tip_pct
109 14.31 4.00 Yes Sat Dinner 2 0.279525
183 23.17 6.50 Yes Sun Dinner 4 0.280535
232 11.61 3.39 No Sat Dinner 2 0.291990
67 3.07 1.00 Yes Sat Dinner 1 0.325733
178 9.60 4.00 Yes Sun Dinner 2 0.416667
172 7.25 5.15 Yes Sun Dinner 2 0.710345
现在,如果对smoker分组并⽤该函数调⽤apply,就会得到:
# 现在,如果对smoker分组并⽤该函数调⽤apply,就会得到:
tips.groupby('smoker').apply(top)
total_bill tip smoker day time size tip_pct
smoker
No 88 24.71 5.85 No Thur Lunch 2 0.236746
185 20.69 5.00 No Sun Dinner 5 0.241663
51 10.29 2.60 No Sun Dinner 2 0.252672
149 7.51 2.00 No Thur Lunch 2 0.266312
232 11.61 3.39 No Sat Dinner 2 0.291990
Yes 109 14.31 4.00 Yes Sat Dinner 2 0.279525
183 23.17 6.50 Yes Sun Dinner 4 0.280535
67 3.07 1.00 Yes Sat Dinner 1 0.325733
178 9.60 4.00 Yes Sun Dinner 2 0.416667
172 7.25 5.15 Yes Sun Dinner 2 0.710345
最终结果就有了⼀个层次化索引,其内层索引值来⾃
# 原DataFrame。
# 如果传给apply的函数能够接受其他参数或关键字,则可以将这
# 些内容放在函数名后⾯⼀并传⼊:
# 最终结果就有了⼀个层次化索引,其内层索引值来⾃
# 原DataFrame。
# 如果传给apply的函数能够接受其他参数或关键字,则可以将这
# 些内容放在函数名后⾯⼀并传⼊:
tips.groupby(['smoker', 'day']).apply(top, n=1, column='total_bill')
total_bill tip smoker day time size tip_pct
smoker day
No Fri 94 22.75 3.25 No Fri Dinner 2 0.142857
Sat 212 48.33 9.00 No Sat Dinner 4 0.186220
Sun 156 48.17 5.00 No Sun Dinner 6 0.103799
Thur 142 41.19 5.00 No Thur Lunch 5 0.121389
Yes Fri 95 40.17 4.73 Yes Fri Dinner 4 0.117750
Sat 170 50.81 10.00 Yes Sat Dinner 3 0.196812
Sun 182 45.35 3.50 Yes Sun Dinner 3 0.077178
Thur 197 43.11 5.00 Yes Thur Lunch 4 0.115982
可能你已经想起来了,之前我在GroupBy对象上调⽤过
# describe:
# 可能你已经想起来了,之前我在GroupBy对象上调⽤过
# describe:
result = tips.groupby('smoker')['tip_pct'].describe()
result
result.unstack('smoker')
smoker
count No 151.000000
Yes 93.000000
mean No 0.159328
Yes 0.163196
std No 0.039910
Yes 0.085119
min No 0.056797
Yes 0.035638
25% No 0.136906
Yes 0.106771
50% No 0.155625
Yes 0.153846
75% No 0.185014
Yes 0.195059
max No 0.291990
Yes 0.710345
dtype: float64
f = lambda x: x.describe() grouped.apply(f)
Suppressing the Group Keys
禁⽌分组键
# 从上⾯的例⼦中可以看出,分组键会跟原始对象的索引共同构成
# 400
# 结果对象中的层次化索引。将group_keys=False传⼊groupby即
# 可禁⽌该效果:
# 禁⽌分组键
# 从上⾯的例⼦中可以看出,分组键会跟原始对象的索引共同构成
# 400
# 结果对象中的层次化索引。将group_keys=False传⼊groupby即
# 可禁⽌该效果:
tips.groupby('smoker', group_keys=False).apply(top)
total_bill tip smoker day time size tip_pct
88 24.71 5.85 No Thur Lunch 2 0.236746
185 20.69 5.00 No Sun Dinner 5 0.241663
51 10.29 2.60 No Sun Dinner 2 0.252672
149 7.51 2.00 No Thur Lunch 2 0.266312
232 11.61 3.39 No Sat Dinner 2 0.291990
109 14.31 4.00 Yes Sat Dinner 2 0.279525
183 23.17 6.50 Yes Sun Dinner 4 0.280535
67 3.07 1.00 Yes Sat Dinner 1 0.325733
178 9.60 4.00 Yes Sun Dinner 2 0.416667
172 7.25 5.15 Yes Sun Dinner 2 0.710345
Quantile and Bucket Analysis
分位数和桶分析
# 我曾在第8章中讲过,pandas有⼀些能根据指定⾯元或样本分位
# 数将数据拆分成多块的⼯具(⽐如cut和qcut)。将这些函数跟
# groupby结合起来,就能⾮常轻松地实现对数据集的桶
# (bucket)或分位数(quantile)分析了。以下⾯这个简单的随
# 机数据集为例,我们利⽤cut将其装⼊⻓度相等的桶中:
# 分位数和桶分析
# 我曾在第8章中讲过,pandas有⼀些能根据指定⾯元或样本分位
# 数将数据拆分成多块的⼯具(⽐如cut和qcut)。将这些函数跟
# groupby结合起来,就能⾮常轻松地实现对数据集的桶
# (bucket)或分位数(quantile)分析了。以下⾯这个简单的随
# 机数据集为例,我们利⽤cut将其装⼊⻓度相等的桶中:
frame = pd.DataFrame({'data1': np.random.randn(1000),
'data2': np.random.randn(1000)})
quartiles = pd.cut(frame.data1, 4)
quartiles[:10]
0 (-1.23, 0.489]
1 (-1.23, 0.489]
2 (-1.23, 0.489]
3 (-1.23, 0.489]
4 (-1.23, 0.489]
5 (-1.23, 0.489]
6 (-2.956, -1.23]
7 (0.489, 2.208]
8 (0.489, 2.208]
9 (-1.23, 0.489]
Name: data1, dtype: category
Categories (4, interval[float64]): [(-2.956, -1.23] < (-1.23, 0.489] < (0.489, 2.208] < (2.208, 3.928]]
由cut返回的Categorical对象可直接传递到groupby。因此,我们
# 可以像下⾯这样对data2列做⼀些统计计算:
# 由cut返回的Categorical对象可直接传递到groupby。因此,我们
# 可以像下⾯这样对data2列做⼀些统计计算:
def get_stats(group):
return {'min': group.min(), 'max': group.max(),
'count': group.count(), 'mean': group.mean()}
grouped = frame.data2.groupby(quartiles)
grouped.apply(get_stats).unstack()
count max mean min
data1
(-2.956, -1.23] 92.0 1.670835 -0.038501 -3.399312
(-1.23, 0.489] 602.0 3.260383 -0.019113 -2.989741
(0.489, 2.208] 294.0 2.954439 0.066179 -3.745356
(2.208, 3.928] 12.0 1.765640 -0.048003 -1.929776
这些都是⻓度相等的桶。要根据样本分位数得到⼤⼩相等的桶,
# 使⽤qcut即可。传⼊labels=False即可只获取分位数的编号:
# 这些都是⻓度相等的桶。要根据样本分位数得到⼤⼩相等的桶,
# 使⽤qcut即可。传⼊labels=False即可只获取分位数的编号:
# Return quantile numbers
grouping = pd.qcut(frame.data1, 10, labels=False)
grouped = frame.data2.groupby(grouping)
grouped.apply(get_stats).unstack()
count max mean min
data1
0 100.0 1.670835 -0.010807 -3.399312
1 100.0 2.628441 -0.033573 -2.153545
2 100.0 2.527939 -0.149095 -2.925113
3 100.0 3.260383 0.072874 -2.315555
4 100.0 2.074345 -0.116594 -2.047939
5 100.0 2.184810 0.074563 -2.989741
6 100.0 2.089154 -0.033600 -2.084231
7 100.0 2.954439 -0.014457 -3.056990
8 100.0 2.735527 0.096532 -3.745356
9 100.0 2.377020 0.152483 -3.428254
Example: Filling Missing Values with Group-Specific Values
示例:⽤特定于分组的值填充缺失值
# 对于缺失数据的清理⼯作,有时你会⽤dropna将其替换掉,⽽有
# 时则可能会希望⽤⼀个固定值或由数据集本身所衍⽣出来的值去
# 填充NA值。这时就得使⽤fillna这个⼯具了。在下⾯这个例⼦
# 中,我⽤平均值去填充NA值:
# 示例:⽤特定于分组的值填充缺失值
# 对于缺失数据的清理⼯作,有时你会⽤dropna将其替换掉,⽽有
# 时则可能会希望⽤⼀个固定值或由数据集本身所衍⽣出来的值去
# 填充NA值。这时就得使⽤fillna这个⼯具了。在下⾯这个例⼦
# 中,我⽤平均值去填充NA值:
s = pd.Series(np.random.randn(6))
s[::2] = np.nan
s
s.fillna(s.mean())
0 0.433256
1 1.464753
2 0.433256
3 -0.194930
4 0.433256
5 0.029944
dtype: float64
假设你需要对不同的分组填充不同的值。⼀种⽅法是将数据分
# 组,并使⽤apply和⼀个能够对各数据块调⽤fillna的函数即可。
# 下⾯是⼀些有关美国⼏个州的示例数据,这些州⼜被分为东部和
# ⻄部:
# 假设你需要对不同的分组填充不同的值。⼀种⽅法是将数据分
# 组,并使⽤apply和⼀个能够对各数据块调⽤fillna的函数即可。
# 下⾯是⼀些有关美国⼏个州的示例数据,这些州⼜被分为东部和
# ⻄部:
states = ['Ohio', 'New York', 'Vermont', 'Florida',
'Oregon', 'Nevada', 'California', 'Idaho']
group_key = ['East'] * 4 + ['West'] * 4
data = pd.Series(np.random.randn(8), index=states)
data
Ohio -1.199705
New York 0.500468
Vermont -0.084207
Florida -0.145003
Oregon -0.382199
Nevada 0.588151
California 2.653656
Idaho -1.101910
dtype: float64
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
data
data.groupby(group_key).mean()
我们可以⽤分组平均值去填充NA值:
# 我们可以⽤分组平均值去填充NA值:
fill_mean = lambda g: g.fillna(g.mean())
data.groupby(group_key).apply(fill_mean)
Ohio -1.199705
New York 0.500468
Vermont -0.084207
Florida -0.145003
Oregon -0.382199
Nevada 0.588151
California 2.653656
Idaho -1.101910
dtype: float64
fill_values = {'East': 0.5, 'West': -1}
fill_func = lambda g: g.fillna(fill_values[g.name])
data.groupby(group_key).apply(fill_func)
Example: Random Sampling and Permutation
示例:随机采样和排列
# 假设你想要从⼀个⼤数据集中随机抽取(进⾏替换或不替换)样
# 本以进⾏蒙特卡罗模拟(Monte Carlo simulation)或其他分析⼯
# 作。“抽取”的⽅式有很多,这⾥使⽤的⽅法是对Series使⽤
# sample⽅法:
# 示例:随机采样和排列
# 假设你想要从⼀个⼤数据集中随机抽取(进⾏替换或不替换)样
# 本以进⾏蒙特卡罗模拟(Monte Carlo simulation)或其他分析⼯
# 作。“抽取”的⽅式有很多,这⾥使⽤的⽅法是对Series使⽤
# sample⽅法:
# Hearts, Spades, Clubs, Diamonds
suits = ['H', 'S', 'C', 'D']
card_val = (list(range(1, 11)) + [10] * 3) * 4
base_names = ['A'] + list(range(2, 11)) + ['J', 'K', 'Q']
cards = []
for suit in ['H', 'S', 'C', 'D']:
cards.extend(str(num) + suit for num in base_names)
deck = pd.Series(card_val, index=cards)
现在我有了⼀个⻓度为52的Series,其索引包括牌名,值则是21
# 点或其他游戏中⽤于计分的点数(为了简单起⻅,我当A的点数
# 为1):
# 现在我有了⼀个⻓度为52的Series,其索引包括牌名,值则是21
# 点或其他游戏中⽤于计分的点数(为了简单起⻅,我当A的点数
# 为1):
deck[:13]
AH 1
2H 2
3H 3
4H 4
5H 5
6H 6
7H 7
8H 8
9H 9
10H 10
JH 10
KH 10
QH 10
dtype: int64
def draw(deck, n=5):
return deck.sample(n)
draw(deck)
get_suit = lambda card: card[-1] # last letter is suit
deck.groupby(get_suit).apply(draw, n=2)
deck.groupby(get_suit, group_keys=False).apply(draw, n=2)
Example: Group Weighted Average and Correlation
示例:分组加权平均数和相关系数
# 根据groupby的“拆分-应⽤-合并”范式,可以进⾏DataFrame
# 的列与列之间或两个Series之间的运算(⽐如分组加权平均)。
# 以下⾯这个数据集为例,它含有分组键、值以及⼀些权重值:
# 示例:分组加权平均数和相关系数
# 根据groupby的“拆分-应⽤-合并”范式,可以进⾏DataFrame
# 的列与列之间或两个Series之间的运算(⽐如分组加权平均)。
# 以下⾯这个数据集为例,它含有分组键、值以及⼀些权重值:
df = pd.DataFrame({'category': ['a', 'a', 'a', 'a',
'b', 'b', 'b', 'b'],
'data': np.random.randn(8),
'weights': np.random.rand(8)})
df
category data weights
0 a -0.608443 0.738078
1 a -0.340831 0.224993
2 a 0.197070 0.129218
3 a 1.129965 0.641297
4 b -1.078691 0.234486
5 b 0.261523 0.507843
6 b 0.473563 0.380462
7 b -0.939801 0.802412
# 然后可以利⽤category计算分组加权平均数:
grouped = df.groupby('category')
get_wavg = lambda g: np.average(g['data'], weights=g['weights'])
grouped.apply(get_wavg)
category
a 0.129411
b -0.360513
dtype: float64
close_px = pd.read_csv('examples/stock_px_2.csv', parse_dates=True,
index_col=0)
close_px.info()
close_px[-4:]
来做⼀个⽐较有趣的任务:计算⼀个由⽇收益率(通过百分数变
# 化计算)与SPX之间的年度相关系数组成的DataFrame。下⾯是
# ⼀个实现办法,我们先创建⼀个函数,⽤它计算每列和SPX列的
# 成对相关系数:
# 来做⼀个⽐较有趣的任务:计算⼀个由⽇收益率(通过百分数变
# 化计算)与SPX之间的年度相关系数组成的DataFrame。下⾯是
# ⼀个实现办法,我们先创建⼀个函数,⽤它计算每列和SPX列的
# 成对相关系数:
spx_corr = lambda x: x.corrwith(x['SPX'])
# 接下来,我们使⽤pct_change计算close_px的百分⽐变化:
rets = close_px.pct_change().dropna()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-62-11e2071548d0> in <module>()
1 # 接下来,我们使⽤pct_change计算close_px的百分⽐变化:
----> 2 rets = close_px.pct_change().dropna()
NameError: name 'close_px' is not defined
get_year = lambda x: x.year
by_year = rets.groupby(get_year)
by_year.apply(spx_corr)
# 当然,你还可以计算列与列之间的相关系数。这⾥,我们计算
# Apple和Microsoft的年相关系数:
by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))
# 当然,你还可以计算列与列之间的相关系数。这⾥,我们计算
# Apple和Microsoft的年相关系数:
by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-63-3caa37fa4193> in <module>()
1 # 当然,你还可以计算列与列之间的相关系数。这⾥,我们计算
2 # Apple和Microsoft的年相关系数:
----> 3 by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))
NameError: name 'by_year' is not defined
Example: Group-Wise Linear Regression
示例:组级别的线性回归
# 顺着上⼀个例⼦继续,你可以⽤groupby执⾏更为复杂的分组统
# 计分析,只要函数返回的是pandas对象或标量值即可。例如,
# 我可以定义下⾯这个regress函数(利⽤statsmodels计量经济学
# 库)对各数据块执⾏普通最⼩⼆乘法(Ordinary Least
# Squares, OLS)回归:
# 示例:组级别的线性回归
# 顺着上⼀个例⼦继续,你可以⽤groupby执⾏更为复杂的分组统
# 计分析,只要函数返回的是pandas对象或标量值即可。例如,
# 我可以定义下⾯这个regress函数(利⽤statsmodels计量经济学
# 库)对各数据块执⾏普通最⼩⼆乘法(Ordinary Least
# Squares, OLS)回归:
import statsmodels.api as sm
def regress(data, yvar, xvars):
Y = data[yvar]
X = data[xvars]
X['intercept'] = 1.
result = sm.OLS(Y, X).fit()
return result.params
by_year.apply(regress, 'AAPL', ['SPX'])
by_year.apply(regress, 'AAPL', ['SPX'])