python pandas数据分组对象

python pandas数据分组对象

0 背景

在标准的数据探索过程中,我们通常按照特定的维度或标准来分割数据集。这种分割帮助我们理解数据在这些维度上的集中趋势。通过对数据按照这些粒度进行聚合分析,我们可以更好地洞察数据模式,从而协助我们解决特定场景下的问题。上一篇我们已经完成了分组对象的创建和一些操作,这个分组对象包含了数据的分组详情。那, 接下来的步骤我们是对这个分组对象进行各种操作,并获取相关信息。这是为了为最终的数据聚合和统计分析打下坚实的基础。本文内容将涉及python pandas数据分组对象的一些操作。

1 使用

创建数据并获取分组对象

import numpy as np
import pandas as pd
data = {
    'A': ['aa''ab''bc''bc''ab''bc', ],
    'B': [223585, ],
    'C': np.random.randn(6),
    'D': np.random.randn(6),
    'E': np.random.randint(1106)
}
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()有几个重要的参数:

  1. normalize (布尔值,默认为 False):
    • 如果设置为 True,结果将显示值的相对频率而非计数,即每个唯一值的比例。
  2. sort (布尔值,默认为 True):
    • 如果为 True,结果将按计数值降序排列。
    • 如果为 False,结果将按出现的值的顺序排列。
  3. ascending (布尔值,默认为 False):
    • sortTrue 时,这个参数控制排序的顺序。
    • 如果为 True,结果将按计数值升序排列。
  4. bins (整数或者一维数组,默认为 None):
    • 仅当列是数字类型时适用。
    • 如果提供了整数,则它定义了值的分箱数量,类似于直方图的箱数。
    • 如果提供了一维数组,则数组中的数字定义了箱边界。
  5. dropna (布尔值,默认为 True):
    • 如果为 True,则在计数时忽略 NaN 值。
    • 如果为 False,则包含 NaN 值在内的计数。

2 总结

groupby 函数可以简单的理解为为拆开数据、应用数据和合并数据。本文主要介绍python Pandas提供的分组对象的一些方法,能够让我们灵活方便地对数据分组对象进行单独的操作,达到更好的数据分析的效果。

欢迎关注我的微信公众号


原文始发于微信公众号(其之):python pandas数据分组对象

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/204773.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!