目录
1.单类分组
A.groupby('性别')
首先,我们有一个变量A,数据类型是DataFrame,想要按照【性别】进行分组,得到的结果是一个Groupby对象,还没有进行任何的运算。
describe()
描述组内数据的基本统计量
A.groupby("性别").describe().unstack()
-
只有数字类型的列数据才会计算统计
-
示例里面数字类型的数据有两列 【班级】和【身高】
但是,我们并不需要统计班级的均值等信息,只需要【身高】,所以做一下小的改动:
A.groupby("性别")["身高"].describe().unstack()
unstack() 索引重排
2.多类分组
A.groupby( [“班级”,“性别”])
单独用groupby,我们得到的还是一个 Groupby 对象。
mean() 组内均值计算
DataFrame的很多函数可以直接运用到Groupby对象上。
A.groupby( ["班级","性别"]).agg([np.sum, np.mean, np.std]) # 一次计算了三个
我们还可以一次运用多个函数计算
agg() 分组多个运算
# 对sales进行操作,按4列进行分组,并求 [ 'item_id' ]列的频数
group = sales.groupby(['state_id', 'store_id', 'cat_id', 'dept_id'], as_index=False)['item_id'].count()
as_index=False,保持原来的数据索引结果不变,表示分组的四列[‘state_id’, ‘store_id’, ‘cat_id’, ‘dept_id’]不做索引,而是作为dataframe的列输出。
对于数据的分组和分组运算主要是指groupby函数的应用,具体函数的规则如下:
df.groupby([df[属性],df[属性]) (指分类的属性,数据的限定定语,可以有多个 ).mean()(对于数据的计算方式——函数名称)
分组计算后重命名
对分组计算进行for循环
分组后调用自定义函数
# 聚合后重命名,ak_dict是'filled_ntf'列经过CapacityAys.weibull_para计算后的结果
# 'filled_ntf_mean'是'filled_ntf'列经过mean()计算后的结果
ws_ym_mean_ak = temp.groupby(by=['year', 'month'], as_index=False)\
.agg(ak_dict=('filled_ntf', CapacityAys.weibull_para), filled_ntf_mean=('filled_ntf', 'mean'))
for date, df in temp0.groupby(['year', 'month']): # date=('year', 'month')
date # 'year', month数据,通过date[0]和date[1]获取
df#分组后的数据
# as_index=True 就是要把['year', 'month']合并起来作为索引,调用自定义类函数CapacityAys().weibull_para
ak_value = temp.groupby(['year', 'month'], as_index=True)['filled_风速_mean_ntf'].agg(CapacityAys().weibull_para)
3.时间分组
时间序列可以直接作为index,或者有一列是时间序列,差别不是很大。 这里仅仅演示,某一列为时间序列。
为 A 新增一列【生日】,由于分隔符 “/” 的问题,我们查看列属性,【生日】的属性并不是日期类型
(1) 按照【生日】的【年份】进行分组,看看有多少人是同龄?
A["生日"] = pd.to_datetime(A["生日"],format ="%Y/%m/%d") # 转化为时间格式
A.groupby(A["生日"].apply(lambda x:x.year)).count() # 按照【生日】的【年份】分组
(2) 同一年作为一个小组,小组内生日靠前的那一位作为小队长:
A.sort_values("生日", inplace=True) # 按时间排序
A.groupby(A["生日"].apply(lambda x:x.year),as_index=False).first()
as_index=False # 保持原来的数据索引结果不变
first() 保留第一个数据
Tail(n=1) 保留最后n个数据
再进一步:
(3) 想要找到哪个月只有一个人过生日
A.groupby(A["生日"].apply(lambda x:x.month),as_index=False) # 到这里是按月分组
A.groupby(A["生日"].apply(lambda x:x.month),as_index=False).filter(lambda x: len(x)==1)
- filter() 对分组进行过滤,保留满足()条件的分组
- 用 first(),tail()截取每组前后几个数据
- 用 apply()对每组进行(自定义)函数运算
- 用 filter()选取满足特定条件的分组
4. groupby之后对不同列运用聚合函数
4.1 方法1
这种方法会导致多级索引
df.groupby('group').agg({'a':['sum', 'max'],
'b':'mean',
'c':'sum',
'd': lambda x: x.max() - x.min()})
a b c d
sum max mean sum <lambda>
group
0 0.864569 0.446069 0.466054 0.969921 0.341399
1 1.478872 0.843026 0.687672 1.754877 0.672401
4.2 方法2
def f(x):
d = {}
d['a_sum'] = x['a'].sum()
d['a_max'] = x['a'].max()
d['b_mean'] = x['b'].mean()
d['c_d_prodsum'] = (x['c'] * x['d']).sum()
return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum'])
df.groupby('group').apply(f)
a_sum a_max b_mean c_d_prodsum
group
0 0.864569 0.446069 0.466054 0.173711
1 1.478872 0.843026 0.687672 0.630494
这种方法很好。在groupby之后的每个子DF,可以运用聚合函数;
这里自己构建完成的Series
列索引是平铺的,比较直观;
** 也可以这样简写:**
df.groupby('group') \
.apply(lambda x: pd.Series({
'a_sum' : x['a'].sum(),
'a_max' : x['a'].max(),
'b_mean' : x['b'].mean(),
'c_d_prodsum' : (x['c'] * x['d']).sum()
})
)
a_sum a_max b_mean c_d_prodsum
group
0 0.530559 0.374540 0.553354 0.488525
1 1.433558 0.832443 0.460206 0.053313
4.3 方法3
In [26]: f = {'A':['sum','mean'], 'B':['prod']}
In [27]: df.groupby('GRP').agg(f)
Out[27]:
A B
sum mean prod
GRP
0 0.719580 0.359790 0.102004
1 0.454824 0.227412 0.034060
这其实是一种变形,把agg变成一个map单独传入
结果对于列来说,仍然是二级索引;
4.4 方法4
df.groupby('group').agg(
a_sum=('a', 'sum'),
a_mean=('a', 'mean'),
b_mean=('b', 'mean'),
c_sum=('c', 'sum'),
d_range=('d', lambda x: x.max() - x.min())
)
a_sum a_mean b_mean c_sum d_range
group
0 0.947337 0.473668 0.871939 0.838150 0.320543
1 0.604149 0.302074 0.656902 0.542985 0.057681
这种方法更加直观,我比较喜欢;
有个变种可以这样写:
4.5 最后,回顾一下单个列的写法
In [84]: animals.groupby("kind").height.agg(
....: min_height='min',
....: max_height='max',
....: )
....:
Out[84]:
min_height max_height
kind
cat 9.1 9.5
dog 6.0 34.0
或者这样:
# 聚合单列
grouped_single = df.groupby('Team').agg({'Age': ['mean', 'min', 'max']})
grouped_single.columns = ['age_mean', 'age_min', 'age_max']
grouped_single = grouped_single.reset_index()
# 聚合多列
grouped_multiple = df.groupby(['Team', 'Pos']).agg({'Age': ['mean', 'min', 'max']})
grouped_multiple.columns = ['age_mean', 'age_min', 'age_max']
grouped_multiple = grouped_multiple.reset_index()
5. 其它应用案例
data_all_profile.groupby('is_turn').size()
Out[3]:
is_turn
0 6470
1 38194
3 78764
dtype: int64
data_all_profile.groupby('is_turn').size().reset_index(name='count')
Out[4]:
is_turn count
0 0 6470
1 1 38194
2 3 78764
5.2 案例2
data_count_by_bin_1027 = data_1027_unique['extreme_wind_bin'].value_counts()
valid_bins_1027 = data_count_by_bin_1027[data_count_by_bin_1027 >= 3].index
data_1027_unique.groupby('extreme_wind_bin')['V50-TI_category'].apply(lambda x: (x == 'high').mean()).loc[
valid_bins_1027]
这段代码的含义可以分为以下几个部分进行解释:
-
data_1027_unique.groupby('extreme_wind_bin')
:- 这个部分是对
data_1027_unique
数据框按照extreme_wind_bin
列进行分组。每个组对应一个extreme_wind_bin
(极端风速区间)。
- 这个部分是对
-
['V50-TI_category']
:- 这是选取每个分组中的
V50-TI_category
列,它包含了"high"和"low"两个类别,表示每个数据点是否属于"高湍流强度"类别。
- 这是选取每个分组中的
-
apply(lambda x: (x == 'high').mean())
:apply()
方法对每个分组应用一个自定义的函数。这里的lambda x: (x == 'high').mean()
是一个匿名函数:- 它检查每个分组中的
V50-TI_category
列中值是否为 “high”(x == 'high'
)。 - 这会生成一个布尔数组,其中 “high” 为
True
,非 “high” 为False
。 mean()
方法计算True
值的比例(True
被视为 1,False
被视为 0),也就是计算每个风速区间中 “high” 的比例。
- 它检查每个分组中的
-
.loc[valid_bins_1027]
:- 这个部分是基于
valid_bins_1027
对结果进行筛选,只保留valid_bins_1027
中的风速区间。valid_bins_1027
是之前筛选出的符合特定条件(如数据量大于等于100)的有效区间。
- 这个部分是基于
总结:
这段代码的作用是:
- 对
data_1027_unique
数据进行按extreme_wind_bin
列分组。 - 计算每个风速区间中
V50-TI_category
列为 “high” 的比例。 - 最终,返回符合
valid_bins_1027
中的风速区间的结果。
5.2.1 举例
下面用一个简单的例子来解释这段代码 apply(lambda x: (x == 'high').mean())
。
假设有一个数据框 df
,其中一列表示某个分组(例如 extreme_wind_bin
),另一列是 V50-TI_category
,表示是否属于“high”类别。
import pandas as pd
# 创建一个简单的示例数据框
data = {
'extreme_wind_bin': ['bin1', 'bin1', 'bin1', 'bin2', 'bin2', 'bin3', 'bin3', 'bin3', 'bin3'],
'V50-TI_category': ['high', 'low', 'high', 'low', 'low', 'high', 'high', 'low', 'high']
}
df = pd.DataFrame(data)
print(df)
输出结果:
extreme_wind_bin V50-TI_category
0 bin1 high
1 bin1 low
2 bin1 high
3 bin2 low
4 bin2 low
5 bin3 high
6 bin3 high
7 bin3 low
8 bin3 high
分组后应用 apply(lambda x: (x == 'high').mean())
result = df.groupby('extreme_wind_bin')['V50-TI_category'].apply(lambda x: (x == 'high').mean())
print(result)
解释:
-
分组操作:
- 根据
extreme_wind_bin
列将数据分为 3 组:bin1
,bin2
, 和bin3
。 bin1
包含三行,bin2
包含两行,bin3
包含四行。
- 根据
-
lambda x: (x == 'high').mean()
:- 对每个分组的
V50-TI_category
列应用这个lambda
函数。它的意思是:检查每个分组中V50-TI_category
是否等于 “high”,并计算 “high” 出现的比例。
逐个分组解释:
-
对
bin1
:['high', 'low', 'high']
转换为[True, False, True]
,True 表示 “high”。- 计算平均值:
(1 + 0 + 1) / 3 = 0.6667
,所以bin1
的 “high” 比例是 66.67%。
-
对
bin2
:['low', 'low']
转换为[False, False]
,都不是 “high”。- 计算平均值:
(0 + 0) / 2 = 0.0
,所以bin2
的 “high” 比例是 0%。
-
对
bin3
:['high', 'high', 'low', 'high']
转换为[True, True, False, True]
。- 计算平均值:
(1 + 1 + 0 + 1) / 4 = 0.75
,所以bin3
的 “high” 比例是 75%。
- 对每个分组的
-
结果:
extreme_wind_bin
bin1 0.666667
bin2 0.000000
bin3 0.750000
Name: V50-TI_category, dtype: float64
总结
apply(lambda x: (x == 'high').mean())
:
- 将
V50-TI_category
列中的 “high” 转换为布尔值True
,其它值为False
。 - 通过
mean()
计算分组内 “high” 出现的比例,即True
的平均值。
在这个例子中,bin1
的 “high” 出现比例是 66.67%,bin2
是 0%,bin3
是 75%。
参考链接:
[1]链接1 ;