Pandas 2.2 中文官方教程和指南(二十·一)(1)

简介: Pandas 2.2 中文官方教程和指南(二十·一)

Pandas 2.2 中文官方教程和指南(二十)

原文:pandas.pydata.org/docs/

按组分组:分割-应用-合并

原文:pandas.pydata.org/docs/user_guide/groupby.html

通过“按组”我们指的是涉及以下一个或多个步骤的过程:

  • 根据某些标准将数据分成组。
  • 应用一个函数到每个组独立地。
  • 合并结果到数据结构中。

在这些中,分割步骤是最直接的。在应用步骤中,我们可能希望执行以下操作之一:

  • 聚合:为每个组计算摘要统计信息(或统计信息)。一些例子:
  • 计算组的总和或均值。

  • 计算组大小/计数。
  • 转换:执行一些组特定的计算并返回一个类似索引的对象。一些例子:
  • 在组内标准化数据(zscore)。

  • 使用从每个组派生的值填充组内的 NAs。
  • 过滤:根据按组计算的结果为 True 或 False 来丢弃一些组。一些例子:
  • 丢弃属于只有少数成员的组的数据。

  • 根据组总和或均值筛选数据。

这些操作中的许多是在 GroupBy 对象上定义的。这些操作类似于聚合 API、窗口 API 和重采样 API 的操作。

可能某个操作不属于这些类别之一,或是它们的某种组合。在这种情况下,可能可以使用 GroupBy 的apply方法来计算操作。该方法将检查应用步骤的结果,并尝试将它们合理地组合成单个结果,如果它不适合上述三个类别之一。

注意

使用内置的 GroupBy 操作将一个操作分成多个步骤,比使用带有用户定义的 Python 函数的apply方法更有效。

GroupBy 这个名字对于那些使用过基于 SQL 的工具(或itertools)的人应该很熟悉,你可以编写类似以下代码:

SELECT  Column1,  Column2,  mean(Column3),  sum(Column4)
FROM  SomeTable
GROUP  BY  Column1,  Column2 

我们的目标是使像这样的操作自然且易于使用 pandas 表达。我们将讨论 GroupBy 功能的每���领域,然后提供一些非平凡的例子/用例。

查看食谱以获取一些高级策略。

将对象分成组

分组的抽象定义是提供标签到组名的映射。要创建一个 GroupBy 对象(稍后会详细介绍 GroupBy 对象),您可以执行以下操作:

In [1]: speeds = pd.DataFrame(
 ...:    [
 ...:        ("bird", "Falconiformes", 389.0),
 ...:        ("bird", "Psittaciformes", 24.0),
 ...:        ("mammal", "Carnivora", 80.2),
 ...:        ("mammal", "Primates", np.nan),
 ...:        ("mammal", "Carnivora", 58),
 ...:    ],
 ...:    index=["falcon", "parrot", "lion", "monkey", "leopard"],
 ...:    columns=("class", "order", "max_speed"),
 ...: )
 ...: 
In [2]: speeds
Out[2]: 
 class           order  max_speed
falcon     bird   Falconiformes      389.0
parrot     bird  Psittaciformes       24.0
lion     mammal       Carnivora       80.2
monkey   mammal        Primates        NaN
leopard  mammal       Carnivora       58.0
In [3]: grouped = speeds.groupby("class")
In [4]: grouped = speeds.groupby(["class", "order"]) 

映射可以以多种不同的方式指定:

  • 一个 Python 函数,用于对每个索引标签进行调用。
  • 与索引长度相同的列表或 NumPy 数组。
  • 一个字典或Series,提供标签 -> 组名的映射。
  • 对于DataFrame对象,需要一个字符串,指示要用于分组的列名或索引级别名称。
  • 上述任何一种事物的列表。

我们将分组对象统称为。例如,考虑以下DataFrame

注意

传递给groupby的字符串可以是列名,也可以是索引级别。如果一个字符串同时匹配列名和索引级别名称,将引发ValueError

In [5]: df = pd.DataFrame(
 ...:    {
 ...:        "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"],
 ...:        "B": ["one", "one", "two", "three", "two", "two", "one", "three"],
 ...:        "C": np.random.randn(8),
 ...:        "D": np.random.randn(8),
 ...:    }
 ...: )
 ...: 
In [6]: df
Out[6]: 
 A      B         C         D
0  foo    one  0.469112 -0.861849
1  bar    one -0.282863 -2.104569
2  foo    two -1.509059 -0.494929
3  bar  three -1.135632  1.071804
4  foo    two  1.212112  0.721555
5  bar    two -0.173215 -0.706771
6  foo    one  0.119209 -1.039575
7  foo  three -1.044236  0.271860 

在 DataFrame 上,通过调用groupby()我们可以获得一个 GroupBy 对象。此方法返回一个pandas.api.typing.DataFrameGroupBy实例。我们可以自然地按照AB列,或两者进行分组:

In [7]: grouped = df.groupby("A")
In [8]: grouped = df.groupby("B")
In [9]: grouped = df.groupby(["A", "B"]) 

注意

df.groupby('A')只是df.groupby(df['A'])的语法糖。

如果我们还在列AB上有一个 MultiIndex,我们可以按照除指定列之外的所有列进行分组:

In [10]: df2 = df.set_index(["A", "B"])
In [11]: grouped = df2.groupby(level=df2.index.names.difference(["B"]))
In [12]: grouped.sum()
Out[12]: 
 C         D
A 
bar -1.591710 -1.739537
foo -0.752861 -1.402938 

上述 GroupBy 将根据其索引(行)拆分 DataFrame。要按列拆分,请先进行转置:

In [13]: def get_letter_type(letter):
 ....:    if letter.lower() in 'aeiou':
 ....:        return 'vowel'
 ....:    else:
 ....:        return 'consonant'
 ....: 
In [14]: grouped = df.T.groupby(get_letter_type) 

pandas Index对象支持重复值。如果在 groupby 操作中使用非唯一索引作为组键,则相同索引值的所有值将被视为一个组,因此聚合函数的输出将仅包含唯一索引值:

In [15]: index = [1, 2, 3, 1, 2, 3]
In [16]: s = pd.Series([1, 2, 3, 10, 20, 30], index=index)
In [17]: s
Out[17]: 
1     1
2     2
3     3
1    10
2    20
3    30
dtype: int64
In [18]: grouped = s.groupby(level=0)
In [19]: grouped.first()
Out[19]: 
1    1
2    2
3    3
dtype: int64
In [20]: grouped.last()
Out[20]: 
1    10
2    20
3    30
dtype: int64
In [21]: grouped.sum()
Out[21]: 
1    11
2    22
3    33
dtype: int64 

注意,直到需要为止才会发生分割。创建 GroupBy 对象仅验证您是否传递了有效的映射。

注意

许多种复杂的数据操作可以用 GroupBy 操作来表达(尽管不能保证是最有效的实现)。您可以通过标签映射函数进行创造性的操作。

GroupBy 排序

默认情况下,在groupby操作期间对组键进行排序。但是,您可以传递sort=False以实现潜在的加速。使用sort=False时,组键之间的顺序遵循键在原始数据框中出现的顺序:

In [22]: df2 = pd.DataFrame({"X": ["B", "B", "A", "A"], "Y": [1, 2, 3, 4]})
In [23]: df2.groupby(["X"]).sum()
Out[23]: 
 Y
X 
A  7
B  3
In [24]: df2.groupby(["X"], sort=False).sum()
Out[24]: 
 Y
X 
B  3
A  7 

注意,groupby将保留每个组内排序的观测顺序。例如,下面通过groupby()创建的组按照它们在原始DataFrame中出现的顺序排列:

In [25]: df3 = pd.DataFrame({"X": ["A", "B", "A", "B"], "Y": [1, 4, 3, 2]})
In [26]: df3.groupby("X").get_group("A")
Out[26]: 
 X  Y
0  A  1
2  A  3
In [27]: df3.groupby(["X"]).get_group(("B",))
Out[27]: 
 X  Y
1  B  4
3  B  2 
GroupBy dropna

默认情况下,在groupby操作期间,将排除NA值作为组键。但是,如果您想要在组键中包含NA值,可以传递dropna=False来实现。

In [28]: df_list = [[1, 2, 3], [1, None, 4], [2, 1, 3], [1, 2, 2]]
In [29]: df_dropna = pd.DataFrame(df_list, columns=["a", "b", "c"])
In [30]: df_dropna
Out[30]: 
 a    b  c
0  1  2.0  3
1  1  NaN  4
2  2  1.0  3
3  1  2.0  2 
# Default ``dropna`` is set to True, which will exclude NaNs in keys
In [31]: df_dropna.groupby(by=["b"], dropna=True).sum()
Out[31]: 
 a  c
b 
1.0  2  3
2.0  2  5
# In order to allow NaN in keys, set ``dropna`` to False
In [32]: df_dropna.groupby(by=["b"], dropna=False).sum()
Out[32]: 
 a  c
b 
1.0  2  3
2.0  2  5
NaN  1  4 

dropna参数的默认设置是True,这意味着NA不包括在组键中。 ### GroupBy 对象属性

groups属性是一个字典,其键是计算出的唯一组,相应的值是属于每个组的轴标签。在上面的示例中,我们有:

In [33]: df.groupby("A").groups
Out[33]: {'bar': [1, 3, 5], 'foo': [0, 2, 4, 6, 7]}
In [34]: df.T.groupby(get_letter_type).groups
Out[34]: {'consonant': ['B', 'C', 'D'], 'vowel': ['A']} 

对 GroupBy 对象调用标准的 Python len函数将返回组的数量,这与groups字典的长度相同:

In [35]: grouped = df.groupby(["A", "B"])
In [36]: grouped.groups
Out[36]: {('bar', 'one'): [1], ('bar', 'three'): [3], ('bar', 'two'): [5], ('foo', 'one'): [0, 6], ('foo', 'three'): [7], ('foo', 'two'): [2, 4]}
In [37]: len(grouped)
Out[37]: 6 

GroupBy将为列名、GroupBy 操作和其他属性提供制表完成:

In [38]: n = 10
In [39]: weight = np.random.normal(166, 20, size=n)
In [40]: height = np.random.normal(60, 10, size=n)
In [41]: time = pd.date_range("1/1/2000", periods=n)
In [42]: gender = np.random.choice(["male", "female"], size=n)
In [43]: df = pd.DataFrame(
 ....:    {"height": height, "weight": weight, "gender": gender}, index=time
 ....: )
 ....: 
In [44]: df
Out[44]: 
 height      weight  gender
2000-01-01  42.849980  157.500553    male
2000-01-02  49.607315  177.340407    male
2000-01-03  56.293531  171.524640    male
2000-01-04  48.421077  144.251986  female
2000-01-05  46.556882  152.526206    male
2000-01-06  68.448851  168.272968  female
2000-01-07  70.757698  136.431469    male
2000-01-08  58.909500  176.499753  female
2000-01-09  76.435631  174.094104  female
2000-01-10  45.306120  177.540920    male
In [45]: gb = df.groupby("gender") 
In [46]: gb.<TAB>  # noqa: E225, E999
gb.agg        gb.boxplot    gb.cummin     gb.describe   gb.filter     gb.get_group  gb.height     gb.last       gb.median     gb.ngroups    gb.plot       gb.rank       gb.std        gb.transform
gb.aggregate  gb.count      gb.cumprod    gb.dtype      gb.first      gb.groups     gb.hist       gb.max        gb.min        gb.nth        gb.prod       gb.resample   gb.sum        gb.var
gb.apply      gb.cummax     gb.cumsum     gb.fillna     gb.gender     gb.head       gb.indices    gb.mean       gb.name       gb.ohlc       gb.quantile   gb.size       gb.tail       gb.weight 
```### 带有多重索引的 GroupBy
使用 层次化索引数据,按层次结构的一个级别进行分组非常自然。
让我们创建一个具有两级 `MultiIndex` 的 Series。
```py
In [47]: arrays = [
 ....:    ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
 ....:    ["one", "two", "one", "two", "one", "two", "one", "two"],
 ....: ]
 ....: 
In [48]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"])
In [49]: s = pd.Series(np.random.randn(8), index=index)
In [50]: s
Out[50]: 
first  second
bar    one      -0.919854
 two      -0.042379
baz    one       1.247642
 two      -0.009920
foo    one       0.290213
 two       0.495767
qux    one       0.362949
 two       1.548106
dtype: float64 

然后,我们可以按 s 中的一个级别进行分组。

In [51]: grouped = s.groupby(level=0)
In [52]: grouped.sum()
Out[52]: 
first
bar   -0.962232
baz    1.237723
foo    0.785980
qux    1.911055
dtype: float64 

如果 MultiIndex 指定了名称,则可以直接将这些名称传递而不是级别号:

In [53]: s.groupby(level="second").sum()
Out[53]: 
second
one    0.980950
two    1.991575
dtype: float64 

支持具有多级别的分组。

In [54]: arrays = [
 ....:    ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
 ....:    ["doo", "doo", "bee", "bee", "bop", "bop", "bop", "bop"],
 ....:    ["one", "two", "one", "two", "one", "two", "one", "two"],
 ....: ]
 ....: 
In [55]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second", "third"])
In [56]: s = pd.Series(np.random.randn(8), index=index)
In [57]: s
Out[57]: 
first  second  third
bar    doo     one     -1.131345
 two     -0.089329
baz    bee     one      0.337863
 two     -0.945867
foo    bop     one     -0.932132
 two      1.956030
qux    bop     one      0.017587
 two     -0.016692
dtype: float64
In [58]: s.groupby(level=["first", "second"]).sum()
Out[58]: 
first  second
bar    doo      -1.220674
baz    bee      -0.608004
foo    bop       1.023898
qux    bop       0.000895
dtype: float64 

索引级别名称可以作为键提供。

In [59]: s.groupby(["first", "second"]).sum()
Out[59]: 
first  second
bar    doo      -1.220674
baz    bee      -0.608004
foo    bop       1.023898
qux    bop       0.000895
dtype: float64 

更多关于 sum 函数和聚合的信息稍后。

使用 Index 级别和列分组的 DataFrame

可以通过列和索引级别的组合对 DataFrame 进行分组。您可以同时指定列名和索引名,或者使用 Grouper

让我们首先创建一个带有 MultiIndex 的 DataFrame:

In [60]: arrays = [
 ....:    ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
 ....:    ["one", "two", "one", "two", "one", "two", "one", "two"],
 ....: ]
 ....: 
In [61]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"])
In [62]: df = pd.DataFrame({"A": [1, 1, 1, 1, 2, 2, 3, 3], "B": np.arange(8)}, index=index)
In [63]: df
Out[63]: 
 A  B
first second 
bar   one     1  0
 two     1  1
baz   one     1  2
 two     1  3
foo   one     2  4
 two     2  5
qux   one     3  6
 two     3  7 

然后我们将 dfsecond 索引级别和 A 列分组。

In [64]: df.groupby([pd.Grouper(level=1), "A"]).sum()
Out[64]: 
 B
second A 
one    1  2
 2  4
 3  6
two    1  4
 2  5
 3  7 

索引级别也可以通过名称指定。

In [65]: df.groupby([pd.Grouper(level="second"), "A"]).sum()
Out[65]: 
 B
second A 
one    1  2
 2  4
 3  6
two    1  4
 2  5
 3  7 

索引级别名称可以直接作为键传递给 groupby

In [66]: df.groupby(["second", "A"]).sum()
Out[66]: 
 B
second A 
one    1  2
 2  4
 3  6
two    1  4
 2  5
 3  7 

在 GroupBy 中的 DataFrame 列选择

从 DataFrame 创建 GroupBy 对象后,您可能希望针对每列执行不同的操作。因此,通过在 GroupBy 对象上使用 [],方式类似于从 DataFrame 获取列的方式,您可以执行以下操作:

In [67]: df = pd.DataFrame(
 ....:    {
 ....:        "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"],
 ....:        "B": ["one", "one", "two", "three", "two", "two", "one", "three"],
 ....:        "C": np.random.randn(8),
 ....:        "D": np.random.randn(8),
 ....:    }
 ....: )
 ....: 
In [68]: df
Out[68]: 
 A      B         C         D
0  foo    one -0.575247  1.346061
1  bar    one  0.254161  1.511763
2  foo    two -1.143704  1.627081
3  bar  three  0.215897 -0.990582
4  foo    two  1.193555 -0.441652
5  bar    two -0.077118  1.211526
6  foo    one -0.408530  0.268520
7  foo  three -0.862495  0.024580
In [69]: grouped = df.groupby(["A"])
In [70]: grouped_C = grouped["C"]
In [71]: grouped_D = grouped["D"] 

这主要是对另一种方式的语法糖,后者要冗长得多:

In [72]: df["C"].groupby(df["A"])
Out[72]: <pandas.core.groupby.generic.SeriesGroupBy object at 0x7ff2cef1c730> 

此外,该方法避免了重新计算从传递的键派生的内部分组信息。

如果您想要对它们进行操作,还可以包括分组列。

In [73]: grouped[["A", "B"]].sum()
Out[73]: 
 A                  B
A 
bar        barbarbar        onethreetwo
foo  foofoofoofoofoo  onetwotwoonethree 
```## 遍历分组
拥有 GroupBy 对象后,通过分组的数据进行迭代是非常自然的,并且功能类似于 [`itertools.groupby()`](https://docs.python.org/3/library/itertools.html#itertools.groupby "(在 Python v3.12 中)"):
```py
In [74]: grouped = df.groupby('A')
In [75]: for name, group in grouped:
 ....:    print(name)
 ....:    print(group)
 ....: 
bar
 A      B         C         D
1  bar    one  0.254161  1.511763
3  bar  three  0.215897 -0.990582
5  bar    two -0.077118  1.211526
foo
 A      B         C         D
0  foo    one -0.575247  1.346061
2  foo    two -1.143704  1.627081
4  foo    two  1.193555 -0.441652
6  foo    one -0.408530  0.268520
7  foo  three -0.862495  0.024580 

在按多个键进行分组的情况下,组名将是一个元组:

In [76]: for name, group in df.groupby(['A', 'B']):
 ....:    print(name)
 ....:    print(group)
 ....: 
('bar', 'one')
 A    B         C         D
1  bar  one  0.254161  1.511763
('bar', 'three')
 A      B         C         D
3  bar  three  0.215897 -0.990582
('bar', 'two')
 A    B         C         D
5  bar  two -0.077118  1.211526
('foo', 'one')
 A    B         C         D
0  foo  one -0.575247  1.346061
6  foo  one -0.408530  0.268520
('foo', 'three')
 A      B         C        D
7  foo  three -0.862495  0.02458
('foo', 'two')
 A    B         C         D
2  foo  two -1.143704  1.627081
4  foo  two  1.193555 -0.441652 

参见 遍历分组。

选择一个组

可以使用 DataFrameGroupBy.get_group() 选择单个组:

In [77]: grouped.get_group("bar")
Out[77]: 
 A      B         C         D
1  bar    one  0.254161  1.511763
3  bar  three  0.215897 -0.990582
5  bar    two -0.077118  1.211526 

或者针对在多列上分组的对象:

In [78]: df.groupby(["A", "B"]).get_group(("bar", "one"))
Out[78]: 
 A    B         C         D
1  bar  one  0.254161  1.511763 

聚合

聚合是一种减少分组对象维度的 GroupBy 操作。聚合的结果是,或者至少被视为,每列在一个组中的标量值。例如,生成一组值中每列的总和。

In [79]: animals = pd.DataFrame(
 ....:    {
 ....:        "kind": ["cat", "dog", "cat", "dog"],
 ....:        "height": [9.1, 6.0, 9.5, 34.0],
 ....:        "weight": [7.9, 7.5, 9.9, 198.0],
 ....:    }
 ....: )
 ....: 
In [80]: animals
Out[80]: 
 kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0
In [81]: animals.groupby("kind").sum()
Out[81]: 
 height  weight
kind 
cat     18.6    17.8
dog     40.0   205.5 

在结果中,默认情况下,组的键出现在索引中。通过传递 as_index=False,可以将它们包含在列中。

In [82]: animals.groupby("kind", as_index=False).sum()
Out[82]: 
 kind  height  weight
0  cat    18.6    17.8
1  dog    40.0   205.5 

内置聚合方法

许多常见的聚合内置到 GroupBy 对象中作为方法。在下面列出的方法中,带有 * 的方法 没有 一个高效的、GroupBy 特定的实现。

方法 描述
any() 计算组中是否有任何真值
all() 计算组中所有值是否都为真值
count() 计算组中非 NA 值的数量
cov() * 计算组的协方差
first() 计算每个组中首次出现的值
idxmax() 计算每个组中最大值的索引
idxmin() 计算每个组中最小值的索引
last() 计算每个组中最后出现的值
max() 计算每个组的最大值
mean() 计算每个组的平均值
median() 计算每个组的中位数
min() 计算每个组的最小值
nunique() 计算每个组中唯一值的数量
prod() 计算每个组中值的乘积
quantile() 计算每个组中值的给定分位数
sem() 计算每个组中值的均值标准误差
size() 计算每个组中值的数量
skew() * 计算每个组中值的偏度
std() 计算每个组中值的标准偏差
sum() 计算每个组中值的总和
var() 计算每个组中值的方差

一些示例:

In [83]: df.groupby("A")[["C", "D"]].max()
Out[83]: 
 C         D
A 
bar  0.254161  1.511763
foo  1.193555  1.627081
In [84]: df.groupby(["A", "B"]).mean()
Out[84]: 
 C         D
A   B 
bar one    0.254161  1.511763
 three  0.215897 -0.990582
 two   -0.077118  1.211526
foo one   -0.491888  0.807291
 three -0.862495  0.024580
 two    0.024925  0.592714 

另一个聚合示例是计算每个组的大小。这包含在 GroupBy 中作为size方法。它返回一个 Series,其索引由组名组成,值是每个组的大小。

In [85]: grouped = df.groupby(["A", "B"])
In [86]: grouped.size()
Out[86]: 
A    B 
bar  one      1
 three    1
 two      1
foo  one      2
 three    1
 two      2
dtype: int64 

虽然DataFrameGroupBy.describe()方法本身不是一个约简函数,但它可以方便地生成关于每个组的摘要统计信息的集合。

In [87]: grouped.describe()
Out[87]: 
 C                      ...         D 
 count      mean       std  ...       50%       75%       max
A   B                                ... 
bar one     1.0  0.254161       NaN  ...  1.511763  1.511763  1.511763
 three   1.0  0.215897       NaN  ... -0.990582 -0.990582 -0.990582
 two     1.0 -0.077118       NaN  ...  1.211526  1.211526  1.211526
foo one     2.0 -0.491888  0.117887  ...  0.807291  1.076676  1.346061
 three   1.0 -0.862495       NaN  ...  0.024580  0.024580  0.024580
 two     2.0  0.024925  1.652692  ...  0.592714  1.109898  1.627081
[6 rows x 16 columns] 

另一个聚合示例是计算每个组的唯一值数量。这类似于DataFrameGroupBy.value_counts()函数,不同之处在于它只计算唯一值的数量。

In [88]: ll = [['foo', 1], ['foo', 2], ['foo', 2], ['bar', 1], ['bar', 1]]
In [89]: df4 = pd.DataFrame(ll, columns=["A", "B"])
In [90]: df4
Out[90]: 
 A  B
0  foo  1
1  foo  2
2  foo  2
3  bar  1
4  bar  1
In [91]: df4.groupby("A")["B"].nunique()
Out[91]: 
A
bar    1
foo    2
Name: B, dtype: int64 

注意

聚合函数不会as_index=True(默认情况下)时返回您正在聚合的组作为命名,分组的列将是返回对象的索引

传递as_index=False 返回您正在聚合的分组作为命名列,而不管它们在输入中是命名索引还是。### aggregate() 方法

注意

aggregate() 方法可以接受许多不同类型的输入。本节详细介绍了使用字符串别名进行各种 GroupBy 方法的其他输入详细信息,请参见下文各节。

任何 pandas 实现的缩减方法都可以作为字符串传递给aggregate()。鼓励用户使用简写形式agg。它将像调用相应的方法一样运行。

In [92]: grouped = df.groupby("A")
In [93]: grouped[["C", "D"]].aggregate("sum")
Out[93]: 
 C         D
A 
bar  0.392940  1.732707
foo -1.796421  2.824590
In [94]: grouped = df.groupby(["A", "B"])
In [95]: grouped.agg("sum")
Out[95]: 
 C         D
A   B 
bar one    0.254161  1.511763
 three  0.215897 -0.990582
 two   -0.077118  1.211526
foo one   -0.983776  1.614581
 three -0.862495  0.024580
 two    0.049851  1.185429 

聚合的结果将使用组名作为新索引。在多个键的情况下,默认情况下结果是一个 MultiIndex。如上所述,这可以通过使用as_index选项进行更改:

In [96]: grouped = df.groupby(["A", "B"], as_index=False)
In [97]: grouped.agg("sum")
Out[97]: 
 A      B         C         D
0  bar    one  0.254161  1.511763
1  bar  three  0.215897 -0.990582
2  bar    two -0.077118  1.211526
3  foo    one -0.983776  1.614581
4  foo  three -0.862495  0.024580
5  foo    two  0.049851  1.185429
In [98]: df.groupby("A", as_index=False)[["C", "D"]].agg("sum")
Out[98]: 
 A         C         D
0  bar  0.392940  1.732707
1  foo -1.796421  2.824590 

请注意,您可以使用DataFrame.reset_index() DataFrame 函数来达到与列名存储在结果MultiIndex中相同的结果,尽管这将多做一次复制。

In [99]: df.groupby(["A", "B"]).agg("sum").reset_index()
Out[99]: 
 A      B         C         D
0  bar    one  0.254161  1.511763
1  bar  three  0.215897 -0.990582
2  bar    two -0.077118  1.211526
3  foo    one -0.983776  1.614581
4  foo  three -0.862495  0.024580
5  foo    two  0.049851  1.185429 
```### 使用自定义函数进行聚合
用户还可以为自定义聚合提供自己的用户定义函数(UDFs)。
警告
使用 UDF 进行聚合时,UDF 不应更改提供的`Series`。有关更多信息,请参见使用用户定义函数(UDF)方法进行突变。
注意
使用 UDF 进行聚合通常不如在 GroupBy 上使用 pandas 内置方法高效。考虑将复杂操作分解为一系列使用内置方法的操作链。
```py
In [100]: animals
Out[100]: 
 kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0
In [101]: animals.groupby("kind")[["height"]].agg(lambda x: set(x))
Out[101]: 
 height
kind 
cat    {9.1, 9.5}
dog   {34.0, 6.0} 

结果的 dtype 将反映聚合函数的 dtype。如果不同组的结果具有不同的 dtype,则将以与DataFrame构造相同的方式确定通用 dtype。

In [102]: animals.groupby("kind")[["height"]].agg(lambda x: x.astype(int).sum())
Out[102]: 
 height
kind 
cat       18
dog       40 
```### 同时应用多个函数
在分组的`Series`上,你可以传递一个函数列表或字典给`SeriesGroupBy.agg()`,输出一个 DataFrame:
```py
In [103]: grouped = df.groupby("A")
In [104]: grouped["C"].agg(["sum", "mean", "std"])
Out[104]: 
 sum      mean       std
A 
bar  0.392940  0.130980  0.181231
foo -1.796421 -0.359284  0.912265 

在分组的DataFrame上,你可以传递一个函数列表给DataFrameGroupBy.agg()以聚合每一列,这将产生一个带有分层列索引的聚合结果:

In [105]: grouped[["C", "D"]].agg(["sum", "mean", "std"])
Out[105]: 
 C                             D 
 sum      mean       std       sum      mean       std
A 
bar  0.392940  0.130980  0.181231  1.732707  0.577569  1.366330
foo -1.796421 -0.359284  0.912265  2.824590  0.564918  0.884785 

结果的聚合将以函数本身命名。如果需要重命名,则可以为Series添加一个链接操作,像这样:

In [106]: (
 .....:    grouped["C"]
 .....:    .agg(["sum", "mean", "std"])
 .....:    .rename(columns={"sum": "foo", "mean": "bar", "std": "baz"})
 .....: )
 .....: 
Out[106]: 
 foo       bar       baz
A 
bar  0.392940  0.130980  0.181231
foo -1.796421 -0.359284  0.912265 

对于分组的DataFrame,您可以以类似的方式重命名:

In [107]: (
 .....:    grouped[["C", "D"]].agg(["sum", "mean", "std"]).rename(
 .....:        columns={"sum": "foo", "mean": "bar", "std": "baz"}
 .....:    )
 .....: )
 .....: 
Out[107]: 
 C                             D 
 foo       bar       baz       foo       bar       baz
A 
bar  0.392940  0.130980  0.181231  1.732707  0.577569  1.366330
foo -1.796421 -0.359284  0.912265  2.824590  0.564918  0.884785 

注意

通常情况下,输出列名应该是唯一的,但 pandas 允许你将相同函数(或两个同名函数)应用于同一列。

In [108]: grouped["C"].agg(["sum", "sum"])
Out[108]: 
 sum       sum
A 
bar  0.392940  0.392940
foo -1.796421 -1.796421 

pandas 也允许你提供多个 lambda 函数。在这种情况下,pandas 会篡改(无名)lambda 函数的名称,对每个后续 lambda 函数追加 _

In [109]: grouped["C"].agg([lambda x: x.max() - x.min(), lambda x: x.median() - x.mean()])
Out[109]: 
 <lambda_0>  <lambda_1>
A 
bar    0.331279    0.084917
foo    2.337259   -0.215962 
```### 命名聚合
为了支持具有对输出列名的控制的特定列聚合,pandas 接受在 `DataFrameGroupBy.agg()` 和 `SeriesGroupBy.agg()` 中的特殊语法,称为“命名聚合”,其中
+   关键字是 *输出* 列名
+   这些值是元组,第一个元素是要选择的列,第二个元素是要应用于该列的聚合函数。pandas 提供了带有字段 `['column', 'aggfunc']` 的 `NamedAgg` 命名元组,以使参数更清晰。通常情况下,聚合可以是可调用对象或字符串别名。
```py
In [110]: animals
Out[110]: 
 kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0
In [111]: animals.groupby("kind").agg(
 .....:    min_height=pd.NamedAgg(column="height", aggfunc="min"),
 .....:    max_height=pd.NamedAgg(column="height", aggfunc="max"),
 .....:    average_weight=pd.NamedAgg(column="weight", aggfunc="mean"),
 .....: )
 .....: 
Out[111]: 
 min_height  max_height  average_weight
kind 
cat          9.1         9.5            8.90
dog          6.0        34.0          102.75 

NamedAgg 就是一个 namedtuple。普通元组也是允许的。

In [112]: animals.groupby("kind").agg(
 .....:    min_height=("height", "min"),
 .....:    max_height=("height", "max"),
 .....:    average_weight=("weight", "mean"),
 .....: )
 .....: 
Out[112]: 
 min_height  max_height  average_weight
kind 
cat          9.1         9.5            8.90
dog          6.0        34.0          102.75 

如果你想要的列名不是有效的 Python 关键字,构造一个字典并解包关键字参数

In [113]: animals.groupby("kind").agg(
 .....:    **{
 .....:        "total weight": pd.NamedAgg(column="weight", aggfunc="sum")
 .....:    }
 .....: )
 .....: 
Out[113]: 
 total weight
kind 
cat           17.8
dog          205.5 

在使用命名聚合时,额外的关键字参数不会传递给聚合函数;只有 (column, aggfunc) 对应的键值对应该作为 **kwargs 传递。如果你的聚合函数需要额外的参数,可以使用 functools.partial() 部分应用它们。

命名聚合对于 Series 分组聚合也是有效的。在这种情况下,没有列选择,所以值只是函数。

In [114]: animals.groupby("kind").height.agg(
 .....:    min_height="min",
 .....:    max_height="max",
 .....: )
 .....: 
Out[114]: 
 min_height  max_height
kind 
cat          9.1         9.5
dog          6.0        34.0 


Pandas 2.2 中文官方教程和指南(二十·一)(2)https://developer.aliyun.com/article/1508817

相关文章
|
11月前
|
SQL 数据采集 数据挖掘
Pandas 教程
10月更文挑战第25天
216 2
|
存储 JSON 数据格式
Pandas 使用教程 CSV - CSV 转 JSON
Pandas 使用教程 CSV - CSV 转 JSON
132 0
|
JSON 数据格式 Python
Pandas 使用教程 JSON
Pandas 使用教程 JSON
146 0
|
SQL 数据采集 JSON
Pandas 使用教程 Series、DataFrame
Pandas 使用教程 Series、DataFrame
231 0
|
数据采集 存储 数据可视化
Pandas高级教程:数据清洗、转换与分析
Pandas是Python的数据分析库,提供Series和DataFrame数据结构及数据分析工具,便于数据清洗、转换和分析。本教程涵盖Pandas在数据清洗(如缺失值、重复值和异常值处理)、转换(数据类型转换和重塑)和分析(如描述性统计、分组聚合和可视化)的应用。通过学习Pandas,用户能更高效地处理和理解数据,为数据分析任务打下基础。
1366 3
|
索引 Python
Pandas 2.2 中文官方教程和指南(一)(4)
Pandas 2.2 中文官方教程和指南(一)
145 0
|
存储 SQL JSON
Pandas 2.2 中文官方教程和指南(一)(3)
Pandas 2.2 中文官方教程和指南(一)
201 0
|
XML 关系型数据库 PostgreSQL
Pandas 2.2 中文官方教程和指南(一)(2)
Pandas 2.2 中文官方教程和指南(一)
399 0
|
XML 关系型数据库 MySQL
Pandas 2.2 中文官方教程和指南(一)(1)
Pandas 2.2 中文官方教程和指南(一)
566 0
|
C++ 索引 Python
Pandas 2.2 中文官方教程和指南(五)(4)
Pandas 2.2 中文官方教程和指南(五)
118 0