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

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

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

对 DataFrame 列应用不同函数

通过将字典传递给 aggregate,你可以对 DataFrame 的列应用不同的聚合函数:

In [115]: grouped.agg({"C": "sum", "D": lambda x: np.std(x, ddof=1)})
Out[115]: 
 C         D
A 
bar  0.392940  1.366330
foo -1.796421  0.884785 

函数名称也可以是字符串。为了使字符串有效,它必须在 GroupBy 上实现:

In [116]: grouped.agg({"C": "sum", "D": "std"})
Out[116]: 
 C         D
A 
bar  0.392940  1.366330
foo -1.796421  0.884785 
```## 转换
转换是一个 GroupBy 操作,其结果与被分组的对象索引相同。常见示例包括 `cumsum()` 和 `diff()`。
```py
In [117]: speeds
Out[117]: 
 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 [118]: grouped = speeds.groupby("class")["max_speed"]
In [119]: grouped.cumsum()
Out[119]: 
falcon     389.0
parrot     413.0
lion        80.2
monkey       NaN
leopard    138.2
Name: max_speed, dtype: float64
In [120]: grouped.diff()
Out[120]: 
falcon       NaN
parrot    -365.0
lion         NaN
monkey       NaN
leopard      NaN
Name: max_speed, dtype: float64 

与聚合不同,用于拆分原始对象的分组不包含在结果中。

注意

由于转换不包括用于拆分结果的分组,因此 DataFrame.groupby()Series.groupby() 中的参数 as_indexsort 没有效果。

转换的常见用途是将结果添加回原始 DataFrame 中。

In [121]: result = speeds.copy()
In [122]: result["cumsum"] = grouped.cumsum()
In [123]: result["diff"] = grouped.diff()
In [124]: result
Out[124]: 
 class           order  max_speed  cumsum   diff
falcon     bird   Falconiformes      389.0   389.0    NaN
parrot     bird  Psittaciformes       24.0   413.0 -365.0
lion     mammal       Carnivora       80.2    80.2    NaN
monkey   mammal        Primates        NaN     NaN    NaN
leopard  mammal       Carnivora       58.0   138.2    NaN 

内置转换方法

GroupBy 上的以下方法作为转换操作。

方法 描述
bfill() 在每个组内向后填充 NA 值
cumcount() 计算每个组内的累积计数
cummax() 计算每个组内的累积最大值
cummin() 计算每个组内的累积最小值
cumprod() 计算每个组内的累积乘积
cumsum() 计算每个组内的累积总和
diff() 计算每个组内相邻值之间的差异
ffill() 在每个组内填充 NA 值
pct_change() 计算每个组内相邻值之间的百分比变化
rank() 计算每个组内每个值的排名
shift() 在每个组内上下移动值

此外,将任何内置聚合方法作为字符串传递给transform()(参见下一节)将在组内广播结果,产生转换后的结果。如果聚合方法有高效实现,这也将是高效的。

transform() 方法

与聚合方法类似,transform() 方法可以接受字符串别名,指向前一节中内置的转换方法。它也可以接受字符串别名,指向内置的聚合方法。当提供聚合方法时,结果将在组内广播。

In [125]: speeds
Out[125]: 
 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 [126]: grouped = speeds.groupby("class")[["max_speed"]]
In [127]: grouped.transform("cumsum")
Out[127]: 
 max_speed
falcon       389.0
parrot       413.0
lion          80.2
monkey         NaN
leopard      138.2
In [128]: grouped.transform("sum")
Out[128]: 
 max_speed
falcon       413.0
parrot       413.0
lion         138.2
monkey       138.2
leopard      138.2 

除了字符串别名外,transform() 方法还可以接受用户定义的函数(UDFs)。UDF 必须:

  • 返回的结果要么与组块的大小相同,要么可以广播到组块的大小(例如,标量,grouped.transform(lambda x: x.iloc[-1]))。
  • 在组块上逐列操作。使用 chunk.apply 对第一组块应用转换。
  • 不要在组块上执行原位操作。组块应被视为不可变的,对组块的更改可能会产生意外结果。查看使用用户定义函数(UDF)方法进行变异获取更多信息。
  • (可选)一次操作整个组块的所有列。如果支持此操作,将从第二块开始使用快速路径。

注意

通过提供 UDF 给 transform 进行转换通常比在 GroupBy 上使用内置方法性能较差。考虑将复杂操作拆分为一系列利用内置方法的操作。

本节中的所有示例都可以通过调用内置方法而不是使用 UDFs 来提高性能。查看下面的示例。

从版本 2.0.0 开始更改:当在分组的 DataFrame 上使用 .transform 并且转换函数返回一个 DataFrame 时,pandas 现在会将结果的索引与输入的索引对齐。您可以在转换函数内部调用 .to_numpy() 来避免对齐。

与聚合方法类似,结果的数据类型将反映转换函数的数据类型。如果不同组的结果具有不同的数据类型,则将以与 DataFrame 构造相同的方式确定公共数据类型。

假设我们希望在每个组内标准化数据:

In [129]: index = pd.date_range("10/1/1999", periods=1100)
In [130]: ts = pd.Series(np.random.normal(0.5, 2, 1100), index)
In [131]: ts = ts.rolling(window=100, min_periods=100).mean().dropna()
In [132]: ts.head()
Out[132]: 
2000-01-08    0.779333
2000-01-09    0.778852
2000-01-10    0.786476
2000-01-11    0.782797
2000-01-12    0.798110
Freq: D, dtype: float64
In [133]: ts.tail()
Out[133]: 
2002-09-30    0.660294
2002-10-01    0.631095
2002-10-02    0.673601
2002-10-03    0.709213
2002-10-04    0.719369
Freq: D, dtype: float64
In [134]: transformed = ts.groupby(lambda x: x.year).transform(
 .....:    lambda x: (x - x.mean()) / x.std()
 .....: )
 .....: 

我们期望结果现在在每个组内具有均值为 0 和标准差为 1(浮点误差范围内),我们可以轻松检查:

# Original Data
In [135]: grouped = ts.groupby(lambda x: x.year)
In [136]: grouped.mean()
Out[136]: 
2000    0.442441
2001    0.526246
2002    0.459365
dtype: float64
In [137]: grouped.std()
Out[137]: 
2000    0.131752
2001    0.210945
2002    0.128753
dtype: float64
# Transformed Data
In [138]: grouped_trans = transformed.groupby(lambda x: x.year)
In [139]: grouped_trans.mean()
Out[139]: 
2000   -4.870756e-16
2001   -1.545187e-16
2002    4.136282e-16
dtype: float64
In [140]: grouped_trans.std()
Out[140]: 
2000    1.0
2001    1.0
2002    1.0
dtype: float64 

我们还可以直观比较原始数据和转换后的数据集。

In [141]: compare = pd.DataFrame({"Original": ts, "Transformed": transformed})
In [142]: compare.plot()
Out[142]: <Axes: > 


具有较低维度输出的转换函数将被广播以匹配输入数组的形状。

In [143]: ts.groupby(lambda x: x.year).transform(lambda x: x.max() - x.min())
Out[143]: 
2000-01-08    0.623893
2000-01-09    0.623893
2000-01-10    0.623893
2000-01-11    0.623893
2000-01-12    0.623893
 ... 
2002-09-30    0.558275
2002-10-01    0.558275
2002-10-02    0.558275
2002-10-03    0.558275
2002-10-04    0.558275
Freq: D, Length: 1001, dtype: float64 

另一个常见的数据转换是用组均值替换缺失数据。

In [144]: cols = ["A", "B", "C"]
In [145]: values = np.random.randn(1000, 3)
In [146]: values[np.random.randint(0, 1000, 100), 0] = np.nan
In [147]: values[np.random.randint(0, 1000, 50), 1] = np.nan
In [148]: values[np.random.randint(0, 1000, 200), 2] = np.nan
In [149]: data_df = pd.DataFrame(values, columns=cols)
In [150]: data_df
Out[150]: 
 A         B         C
0    1.539708 -1.166480  0.533026
1    1.302092 -0.505754       NaN
2   -0.371983  1.104803 -0.651520
3   -1.309622  1.118697 -1.161657
4   -1.924296  0.396437  0.812436
..        ...       ...       ...
995 -0.093110  0.683847 -0.774753
996 -0.185043  1.438572       NaN
997 -0.394469 -0.642343  0.011374
998 -1.174126  1.857148       NaN
999  0.234564  0.517098  0.393534
[1000 rows x 3 columns]
In [151]: countries = np.array(["US", "UK", "GR", "JP"])
In [152]: key = countries[np.random.randint(0, 4, 1000)]
In [153]: grouped = data_df.groupby(key)
# Non-NA count in each group
In [154]: grouped.count()
Out[154]: 
 A    B    C
GR  209  217  189
JP  240  255  217
UK  216  231  193
US  239  250  217
In [155]: transformed = grouped.transform(lambda x: x.fillna(x.mean())) 

我们可以验证转换后数据中组均值未发生变化,并且转换后数据不包含任何缺失值。

In [156]: grouped_trans = transformed.groupby(key)
In [157]: grouped.mean()  # original group means
Out[157]: 
 A         B         C
GR -0.098371 -0.015420  0.068053
JP  0.069025  0.023100 -0.077324
UK  0.034069 -0.052580 -0.116525
US  0.058664 -0.020399  0.028603
In [158]: grouped_trans.mean()  # transformation did not change group means
Out[158]: 
 A         B         C
GR -0.098371 -0.015420  0.068053
JP  0.069025  0.023100 -0.077324
UK  0.034069 -0.052580 -0.116525
US  0.058664 -0.020399  0.028603
In [159]: grouped.count()  # original has some missing data points
Out[159]: 
 A    B    C
GR  209  217  189
JP  240  255  217
UK  216  231  193
US  239  250  217
In [160]: grouped_trans.count()  # counts after transformation
Out[160]: 
 A    B    C
GR  228  228  228
JP  267  267  267
UK  247  247  247
US  258  258  258
In [161]: grouped_trans.size()  # Verify non-NA count equals group size
Out[161]: 
GR    228
JP    267
UK    247
US    258
dtype: int64 

如上面的注意中所述,本节中的每个示例都可以使用内置方法更有效地计算。在下面的代码中,使用 UDF 的低效方法被注释掉,更快的替代方法出现在下面。

# result = ts.groupby(lambda x: x.year).transform(
#     lambda x: (x - x.mean()) / x.std()
# )
In [162]: grouped = ts.groupby(lambda x: x.year)
In [163]: result = (ts - grouped.transform("mean")) / grouped.transform("std")
# result = ts.groupby(lambda x: x.year).transform(lambda x: x.max() - x.min())
In [164]: grouped = ts.groupby(lambda x: x.year)
In [165]: result = grouped.transform("max") - grouped.transform("min")
# grouped = data_df.groupby(key)
# result = grouped.transform(lambda x: x.fillna(x.mean()))
In [166]: grouped = data_df.groupby(key)
In [167]: result = data_df.fillna(grouped.transform("mean")) 
```### 窗口和重采样操作
可以将 `resample()`、`expanding()` 和 `rolling()` 作为 groupby 的方法使用。
下面的示例将在列 B 的样本上应用 `rolling()` 方法,基于列 A 的组。
```py
In [168]: df_re = pd.DataFrame({"A": [1] * 10 + [5] * 10, "B": np.arange(20)})
In [169]: df_re
Out[169]: 
 A   B
0   1   0
1   1   1
2   1   2
3   1   3
4   1   4
.. ..  ..
15  5  15
16  5  16
17  5  17
18  5  18
19  5  19
[20 rows x 2 columns]
In [170]: df_re.groupby("A").rolling(4).B.mean()
Out[170]: 
A 
1  0      NaN
 1      NaN
 2      NaN
 3      1.5
 4      2.5
 ... 
5  15    13.5
 16    14.5
 17    15.5
 18    16.5
 19    17.5
Name: B, Length: 20, dtype: float64 

expanding() 方法将为每个特定组的所有成员累积给定操作(在示例中为 sum())。

In [171]: df_re.groupby("A").expanding().sum()
Out[171]: 
 B
A 
1 0     0.0
 1     1.0
 2     3.0
 3     6.0
 4    10.0
...     ...
5 15   75.0
 16   91.0
 17  108.0
 18  126.0
 19  145.0
[20 rows x 1 columns] 

假设您想要在数据框的每个组中使用 resample() 方法获得每日频率,并希望使用 ffill() 方法填充缺失值。

In [172]: df_re = pd.DataFrame(
 .....:    {
 .....:        "date": pd.date_range(start="2016-01-01", periods=4, freq="W"),
 .....:        "group": [1, 1, 2, 2],
 .....:        "val": [5, 6, 7, 8],
 .....:    }
 .....: ).set_index("date")
 .....: 
In [173]: df_re
Out[173]: 
 group  val
date 
2016-01-03      1    5
2016-01-10      1    6
2016-01-17      2    7
2016-01-24      2    8
In [174]: df_re.groupby("group").resample("1D", include_groups=False).ffill()
Out[174]: 
 val
group date 
1     2016-01-03    5
 2016-01-04    5
 2016-01-05    5
 2016-01-06    5
 2016-01-07    5
...               ...
2     2016-01-20    7
 2016-01-21    7
 2016-01-22    7
 2016-01-23    7
 2016-01-24    8
[16 rows x 1 columns] 
```## 过滤
过滤是一个 GroupBy 操作,它对原始分组对象进行子集化。它可以过滤掉整个组、部分组或两者。过滤返回调用对象的过滤版本,包括提供的分组列。在下面的示例中,`class` 包含在结果中。
```py
In [175]: speeds
Out[175]: 
 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 [176]: speeds.groupby("class").nth(1)
Out[176]: 
 class           order  max_speed
parrot    bird  Psittaciformes       24.0
monkey  mammal        Primates        NaN 

注意

与聚合不同,过滤不会将组键添加到结果的索引中。因此,传递 as_index=Falsesort=True 不会影响这些方法。

过滤将尊重对 GroupBy 对象列的子集。

In [177]: speeds.groupby("class")[["order", "max_speed"]].nth(1)
Out[177]: 
 order  max_speed
parrot  Psittaciformes       24.0
monkey        Primates        NaN 

内置过滤

GroupBy 上的以下方法充当过滤。所有这些方法都有一个高效的、GroupBy 特定的实现。

方法 描述
head() 选择每个组的前几行
nth() 选择每个组的第 n 行
tail() 选择每个组的底部行

用户还可以使用变换以及布尔索引在组内构建复杂的过滤。例如,假设我们有产品和其体积的组,并且希望将数据子集缩小到仅捕获每个组内总体积不超过 90%的最大产品。

In [178]: product_volumes = pd.DataFrame(
 .....:    {
 .....:        "group": list("xxxxyyy"),
 .....:        "product": list("abcdefg"),
 .....:        "volume": [10, 30, 20, 15, 40, 10, 20],
 .....:    }
 .....: )
 .....: 
In [179]: product_volumes
Out[179]: 
 group product  volume
0     x       a      10
1     x       b      30
2     x       c      20
3     x       d      15
4     y       e      40
5     y       f      10
6     y       g      20
# Sort by volume to select the largest products first
In [180]: product_volumes = product_volumes.sort_values("volume", ascending=False)
In [181]: grouped = product_volumes.groupby("group")["volume"]
In [182]: cumpct = grouped.cumsum() / grouped.transform("sum")
In [183]: cumpct
Out[183]: 
4    0.571429
1    0.400000
2    0.666667
6    0.857143
3    0.866667
0    1.000000
5    1.000000
Name: volume, dtype: float64
In [184]: significant_products = product_volumes[cumpct <= 0.9]
In [185]: significant_products.sort_values(["group", "product"])
Out[185]: 
 group product  volume
1     x       b      30
2     x       c      20
3     x       d      15
4     y       e      40
6     y       g      20 

filter方法

注意

通过向filter提供用户定义函数(UDF)进行过滤通常比使用 GroupBy 上的内置方法性能较差。考虑将复杂操作分解为一系列利用内置方法的操作。

filter方法接受一个用户定义函数(UDF),当应用于整个组时,返回TrueFalsefilter方法的结果是 UDF 返回True的组的子集。

假设我们只想取属于总和大于 2 的组的元素。

In [186]: sf = pd.Series([1, 1, 2, 3, 3, 3])
In [187]: sf.groupby(sf).filter(lambda x: x.sum() > 2)
Out[187]: 
3    3
4    3
5    3
dtype: int64 

另一个有用的操作是过滤出仅属于只有几个成员的组的元素。

In [188]: dff = pd.DataFrame({"A": np.arange(8), "B": list("aabbbbcc")})
In [189]: dff.groupby("B").filter(lambda x: len(x) > 2)
Out[189]: 
 A  B
2  2  b
3  3  b
4  4  b
5  5  b 

或者,而不是删除有问题的组,我们可以返回一个类似索引对象,其中未通过过滤器的组填充为 NaN。

In [190]: dff.groupby("B").filter(lambda x: len(x) > 2, dropna=False)
Out[190]: 
 A    B
0  NaN  NaN
1  NaN  NaN
2  2.0    b
3  3.0    b
4  4.0    b
5  5.0    b
6  NaN  NaN
7  NaN  NaN 

对于具有多列的数据框,过滤器应明确指定列作为过滤条件。

In [191]: dff["C"] = np.arange(8)
In [192]: dff.groupby("B").filter(lambda x: len(x["C"]) > 2)
Out[192]: 
 A  B  C
2  2  b  2
3  3  b  3
4  4  b  4
5  5  b  5 
```## 灵活的`apply`
对于分组数据上的一些操作可能不适合于聚合、转换或过滤类别。对于这些情况,可以使用`apply`函数。
警告
`apply`必须尝试从结果中推断出它应该充当规约器、转换器*或*过滤器,具体取决于传递给它的内容。因此,分组列可能包含在输出中,也可能不包含。虽然它试图智能猜测如何行事,但有时可能猜错。
注意
本节中的所有示例都可以更可靠、更高效地使用其他 pandas 功能计算。
```py
In [193]: df
Out[193]: 
 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 [194]: grouped = df.groupby("A")
# could also just call .describe()
In [195]: grouped["C"].apply(lambda x: x.describe())
Out[195]: 
A 
bar  count    3.000000
 mean     0.130980
 std      0.181231
 min     -0.077118
 25%      0.069390
 ... 
foo  min     -1.143704
 25%     -0.862495
 50%     -0.575247
 75%     -0.408530
 max      1.193555
Name: C, Length: 16, dtype: float64 

返回结果的维度也可能会改变:

In [196]: grouped = df.groupby('A')['C']
In [197]: def f(group):
 .....:    return pd.DataFrame({'original': group,
 .....:                         'demeaned': group - group.mean()})
 .....: 
In [198]: grouped.apply(f)
Out[198]: 
 original  demeaned
A 
bar 1  0.254161  0.123181
 3  0.215897  0.084917
 5 -0.077118 -0.208098
foo 0 -0.575247 -0.215962
 2 -1.143704 -0.784420
 4  1.193555  1.552839
 6 -0.408530 -0.049245
 7 -0.862495 -0.503211 

Series 上的apply可以对来自应用函数的返回值本身为系列的值进行操作,并可能将结果上转换为数据框:

In [199]: def f(x):
 .....:    return pd.Series([x, x ** 2], index=["x", "x²"])
 .....: 
In [200]: s = pd.Series(np.random.rand(5))
In [201]: s
Out[201]: 
0    0.582898
1    0.098352
2    0.001438
3    0.009420
4    0.815826
dtype: float64
In [202]: s.apply(f)
Out[202]: 
 x       x²
0  0.582898  0.339770
1  0.098352  0.009673
2  0.001438  0.000002
3  0.009420  0.000089
4  0.815826  0.665572

与 aggregate()方法类似,结果的数据类型将反映应用函数的数据类型。如果不同组的结果具有不同的数据类型,则将以与DataFrame构造相同的方式确定公共数据类型。

使用group_keys控制分组列的放置

要控制分组列是否包含在索引中,可以使用默认为Truegroup_keys参数。比较

In [203]: df.groupby("A", group_keys=True).apply(lambda x: x, include_groups=False)
Out[203]: 
 B         C         D
A 
bar 1    one  0.254161  1.511763
 3  three  0.215897 -0.990582
 5    two -0.077118  1.211526
foo 0    one -0.575247  1.346061
 2    two -1.143704  1.627081
 4    two  1.193555 -0.441652
 6    one -0.408530  0.268520
 7  three -0.862495  0.024580 

In [204]: df.groupby("A", group_keys=False).apply(lambda x: x, include_groups=False)
Out[204]: 
 B         C         D
0    one -0.575247  1.346061
1    one  0.254161  1.511763
2    two -1.143704  1.627081
3  three  0.215897 -0.990582
4    two  1.193555 -0.441652
5    two -0.077118  1.211526
6    one -0.408530  0.268520
7  three -0.862495  0.024580 

Numba 加速例程

版本 1.1 中的新功能。

如果已安装Numba作为可选依赖项,则transformaggregate方法支持engine='numba'engine_kwargs参数。有关参数的一般用法和性能考虑,请参阅使用 Numba 增强性能。

函数签名必须以values, index 完全开始,因为属于每个组的数据将传递给values,分组索引将传递给index

警告

当使用engine='numba'时,内部不会有“回退”行为。分组数据和分组索引将作为 NumPy 数组传递给 JITed 用户定义的函数,不会尝试任何替代执行。

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

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

热门文章

最新文章