python pandas数据分组对象
0 背景
在标准的数据探索过程中,我们通常按照特定的维度或标准来分割数据集。这种分割帮助我们理解数据在这些维度上的集中趋势。通过对数据按照这些粒度进行聚合分析,我们可以更好地洞察数据模式,从而协助我们解决特定场景下的问题。上一篇我们已经完成了分组对象的创建和一些操作,这个分组对象包含了数据的分组详情。那, 接下来的步骤我们是对这个分组对象进行各种操作,并获取相关信息。这是为了为最终的数据聚合和统计分析打下坚实的基础。本文内容将涉及python pandas
数据分组对象的一些操作。
1 使用
创建数据并获取分组对象
import numpy as np
import pandas as pd
data = {
'A': ['aa', 'ab', 'bc', 'bc', 'ab', 'bc', ],
'B': [2, 2, 3, 5, 8, 5, ],
'C': np.random.randn(6),
'D': np.random.randn(6),
'E': np.random.randint(1, 10, 6)
}
df = pd.DataFrame(data)
df
A B C D E
0 aa 2 0.209521 -0.347615 2
1 ab 2 -1.077420 -0.740337 5
2 bc 3 -0.009468 0.723923 7
3 bc 5 0.682054 1.204262 2
4 ab 8 0.782076 1.100714 4
5 bc 5 0.275896 0.780765 8
获取分组对象g
g = df.groupby('A')
选择分组
分组对象的groups
对象会返回一个字典, key是分组的名称, value是每个分组的内容的索引的列表
# 分组对象的groups方法会生成一个字典, key是分组的名称, value是每个分组的内容的索引的列表
g.groups
# {'aa': [0], 'ab': [1, 4], 'bc': [2, 3, 5]}
多层索引的分组的使用:
# 多层索引
g2 = df.groupby([df.A.str[0], 'B'])
# 获取分组的字典,key的多层索引的分组的元组,value是每个分组的内容的索引的列表
g2.groups
# {('a', 2): [0, 1], ('a', 8): [4], ('b', 3): [2], ('b', 5): [3, 5]}
g2.get_group(('b', 5))
# A B C D E
# 3 bc 5 0.682054 1.204262 2
# 5 bc 5 0.275896 0.780765 8
g.indices
返回一个字典,Key为分组的名称,value为本组索引的array格式,可以实现对单分组数据的选取
# grouped.indices返回一个字典,Key为分组的名称,value为本组索引的array格式,可以实现对单分组数据的选取
g.indices
# {'aa': array([0], dtype=int64),
# 'ab': array([1, 4], dtype=int64),
# 'bc': array([2, 3, 5], dtype=int64)}
g.indices['ab']
# array([1, 4], dtype=int64)
迭代分组
我们对分组对象g
进行迭代,可以看到分组的名称和每个组的数据表. 类似于字典的遍历方法for key, value in dict.items()
. 因此我们可以通过这种方式迭代分组对象。
# 迭代分组数据,key是分组的名称,value是分组的数据表,
for key, dt_ in g:
print(key)
print(dt_)
# aa
# A B C D E
# 0 aa 2 0.209521 -0.347615 2
# ab
# A B C D E
# 1 ab 2 -1.077420 -0.740337 5
# 4 ab 8 0.782076 1.100714 4
# bc
# A B C D E
# 2 bc 3 -0.009468 0.723923 7
# 3 bc 5 0.682054 1.204262 2
# 5 bc 5 0.275896 0.780765 8
选择列
在数据分组之后,如果我们需要选择各组中的某一列,我们也可以像DataFrame选择列一样操作:
# 选择单列或多列, 和DataFrame的选择列的方法一样
g.B
g['B']
g[['B', 'C']]
g[['B', 'C']].sum()
应用函数apply()
分组对象使用apply()
调用一个函数,传入的是每组的DataFrame,返回一个经过函数计算后的DataFrame、Series或标量,然后再把数据组合。与DataFrame
的方式有些类似。
将所有元素乘2:
# 应用函数apply, 将所有元素乘2
g.apply(lambda x: x * 2)
# A B C D E
# A
# aa 0 aaaa 4 0.419042 -0.695229 4
# ab 1 abab 4 -2.154840 -1.480674 10
# 4 abab 16 1.564152 2.201427 8
# bc 2 bcbc 6 -0.018935 1.447845 14
# 3 bcbc 10 1.364108 2.408524 4
# 5 bcbc 10 0.551792 1.561530 16
g.apply(lambda x: x * 2).loc[('aa', 0)]
# A aaaa
# B 4
# C 0.419042
# D -0.695229
# E 4
# Name: (aa, 0), dtype: object
将分组后的数据按某一列输出为列表, 实现Hive SQL类似collect_list
函数功能:
# 将分组后的数据按某一列输出为列表, 实现Hive SQL类似collect_list函数功能
c = g.apply(lambda x: x.B.tolist())
c
# A
# aa [2]
# ab [2, 8]
# bc [3, 5, 5]
# dtype: object
获取每组中C列数值最大的两个(类似于:获取每科目中成绩最高的两个分数)
# 获取每组中C列数值最大的两个(类似于:获取每科目中成绩最高的两个分数)
def get_two_max(df_, col):
return df_[col].sort_values(ascending=False).head(2)
g.apply(lambda x: get_two_max(x, 'C'))
设置group_keys
的参数,可以使分组字段不作为索引
# 设置group_keys参数,可以使分组字段不作为索引
df.groupby('A', group_keys=False).apply(get_two_max, "C")
# 0 0.209521
# 4 0.782076
# 1 -1.077420
# 3 0.682054
# 5 0.275896
# Name: C, dtype: float64
管道函数pipe()
分组对象的管道方法与 DataFrame 的管道方法类似。它首先接收一个分组对象,然后将这个对象中同一组的所有数据应用于指定的方法。经过这一过程,最终返回的是经过函数处理并符合特定数据格式的结果。这种方法允许我们对分组数据进行有效的函数操作和转换。
# 最大值与最小值相加
g.pipe(lambda x: x.max() + x.min())
# B C D E
# A
# aa 4 0.419042 -0.695229 4
# ab 10 -0.295344 0.360377 9
# bc 8 0.672586 1.928185 10
def mean_diff(x):
return x.get_group('ab').drop('A', axis=1).mean() - x.get_group('bc').drop('A', axis=1).mean()
# 使用函数
g.pipe(mean_diff)
转换函数transform()
transform()
函数在具有与 agg()
函数相似的功能。然而,不同于 agg()
的是,transform()
的独特之处在于它返回一个与原始数据结构相同的 DataFrame。这个过程涉及将每个数据点的原始值替换为经过统计处理的值。例如,如果我们按组计算学生成绩的平均值,那么 transform()
会生成一个新的 DataFrame,其中每个学生的成绩被替换为其所在组的平均成绩。
g.transform('mean')
# B C D E
# 0 2.000000 0.209521 -0.347615 2.000000
# 1 5.000000 -0.147672 0.180188 4.500000
# 2 4.333333 0.316161 0.902983 5.666667
# 3 4.333333 0.316161 0.902983 5.666667
# 4 5.000000 -0.147672 0.180188 4.500000
# 5 4.333333 0.316161 0.902983 5.666667
# 将分组的数据的位置往上移动一维,通常用于当你需要基于组内的相对位置进行数据操作时,比如在时间序列数据中计算每个组的后一个值。
g.transform(lambda x: x.shift(-1))
# B C D E
# 0 NaN NaN NaN NaN
# 1 8.0 0.782076 1.100714 4.0
# 2 5.0 0.682054 1.204262 2.0
# 3 5.0 0.275896 0.780765 8.0
# 4 NaN NaN NaN NaN
# 5 NaN NaN NaN NaN
# 按照每组标准化
g.transform(lambda x: x / (x.max() - x.min()))
# B C D E
# 0 inf inf -inf inf
# 1 0.333333 -0.579415 -0.402127 5.000000
# 2 1.500000 -0.013691 1.507106 1.166667
# 3 2.500000 0.986309 2.507106 0.333333
# 4 1.333333 0.420585 0.597873 4.000000
# 5 2.500000 0.398969 1.625444 1.333333
# 按组进行筛选, 选出B列中大于3的数据
df[g.transform('mean').B > 3]
# A B C D E
# 1 ab 2 -1.077420 -0.740337 5
# 2 bc 3 -0.009468 0.723923 7
# 3 bc 5 0.682054 1.204262 2
# 4 ab 8 0.782076 1.100714 4
# 5 bc 5 0.275896 0.780765 8
筛选函数filter()
首先, 我们可以用下面的方法, 求一组的平均值
g.mean().mean(1)
g.get_group('aa').drop('A', axis=1).mean(1).mean()
获取每组平均值大于2的:
# 获取每组平均值大于2的
g.filter(lambda x: x.drop('A', axis=1).mean(1).mean() > 2)
# A B C D E
# 1 ab 2 -1.077420 -0.740337 5
# 2 bc 3 -0.009468 0.723923 7
# 3 bc 5 0.682054 1.204262 2
# 4 ab 8 0.782076 1.100714 4
# 5 bc 5 0.275896 0.780765 8
组内B列至少有1个大于5的组:
# 组内B列至少有1个大于5的组
g.filter(lambda x: (x.B > 5).any())
# A B C D E
# 1 ab 2 -1.077420 -0.740337 5
# 4 ab 8 0.782076 1.100714 4
组内B列之和大于10的组:
# 组内B列之和大于10的组
g.filter(lambda x: x.B.sum() > 10)
# A B C D E
# 2 bc 3 -0.009468 0.723923 7
# 3 bc 5 0.682054 1.204262 2
# 5 bc 5 0.275896 0.780765 8
组内所有B列之和大于10的组:
# 组内所有B列之和大于10的组
g.filter(lambda x: (x.B.mean() >= 3).all())
# A B C D E
# 1 ab 2 -1.077420 -0.740337 5
# 2 bc 3 -0.009468 0.723923 7
# 3 bc 5 0.682054 1.204262 2
# 4 ab 8 0.782076 1.100714 4
# 5 bc 5 0.275896 0.780765 8
其他方法
# 组内第一个
g.first()
# 组内最后一个
g.last()
# 分组数
g.ngroups
# 每条数据所在的组
g.ngroup()
# 0 0
# 1 1
# 2 2
# 3 2
# 4 1
# 5 2
# dtype: int64
# 每组显示前n个, 默认是5个
g.head()
# 每组最后n个, 默认是5个
g.tail(1)
# 排序值
g.rank()
# 分组的第一个值
g.nth(1)
# 分组的最后一个值
g.nth(-1)
# 分组的最后两个值
g.nth([-2, -1])
# 第n个非空项
g.nth(0, dropna='all')
g.nth(0, dropna='any')
# 组内位置偏移
g.shift(1)
# 按时间周期偏移, 在时间序列时
g.tshift(1)
# 在 SeriesGroupBy 时可用
# 每组最大的两个
g.B.nlargest(2)
# 每组最小的两个
g.B.nsmallest(2)
# 每组去重数量
g.B.nunique()
# 每组去重值
g.B.unique()
# 每组去重值及数量
g.B.value_counts()
g.B.value_counts(normalize = True)
# 每组值是否单调递增
g.B.is_monotonic_increasing
# 每组值是否单调递减
g.B.is_monotonic_decreasing
# 仅 DataFrameGroupBy 可用
g.corrwith(df2)
其中, 在g.B.value_counts()
时, value_counts()
有几个重要的参数:
-
normalize
(布尔值,默认为False
): -
如果设置为 True
,结果将显示值的相对频率而非计数,即每个唯一值的比例。 -
sort
(布尔值,默认为True
): -
如果为 True
,结果将按计数值降序排列。 -
如果为 False
,结果将按出现的值的顺序排列。 -
ascending
(布尔值,默认为False
): -
当 sort
为True
时,这个参数控制排序的顺序。 -
如果为 True
,结果将按计数值升序排列。 -
bins
(整数或者一维数组,默认为None
): -
仅当列是数字类型时适用。 -
如果提供了整数,则它定义了值的分箱数量,类似于直方图的箱数。 -
如果提供了一维数组,则数组中的数字定义了箱边界。 -
dropna
(布尔值,默认为True
): -
如果为 True
,则在计数时忽略 NaN 值。 -
如果为 False
,则包含 NaN 值在内的计数。
2 总结
groupby
函数可以简单的理解为为拆开数据、应用数据和合并数据。本文主要介绍python Pandas
提供的分组对象的一些方法,能够让我们灵活方便地对数据分组对象进行单独的操作,达到更好的数据分析的效果。
欢迎关注我的微信公众号
原文始发于微信公众号(其之):python pandas数据分组对象
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/204773.html