Pandas-2-2-中文文档-四-

Pandas 2.2 中文文档(四)

原文:pandas.pydata.org/docs/

数据结构简介

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

我们将从一个快速、非全面的概述开始,介绍 pandas 中的基本数据结构,以帮助您入门。关于数据类型、索引、轴标签和对齐的基本行为适用于所有对象。要开始,请导入 NumPy 并将 pandas 加载到您的命名空间中:

In [1]: import numpy as np

In [2]: import pandas as pd 

从根本上说,数据对齐是固有的。除非您明确这样做,否则标签和数据之间的链接不会被打破。

我们将简要介绍数据结构,然后考虑所有广泛功能和方法的各个类别在单独的部分中。

Series

Series是一个能够容纳任何数据类型(整数、字符串、浮点数、Python 对象等)的一维带标签数组。轴标签总称为索引。创建Series的基本方法是调用:

s = pd.Series(data, index=index) 

在这里,data可以是许多不同的东西:

  • 一个 Python 字典

  • 一个 ndarray

  • 标量值(比如 5)

传递的索引是一个轴标签列表。因此,这根据data 是的情况分为几种情况:

来自 ndarray

如果data是一个 ndarray,则索引必须与data的长度相同。如果没有传递索引,将创建一个具有值[0, ..., len(data) - 1]的索引。

In [3]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [4]: s
Out[4]: 
a    0.469112
b   -0.282863
c   -1.509059
d   -1.135632
e    1.212112
dtype: float64

In [5]: s.index
Out[5]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

In [6]: pd.Series(np.random.randn(5))
Out[6]: 
0   -0.173215
1    0.119209
2   -1.044236
3   -0.861849
4   -2.104569
dtype: float64 

注意

pandas 支持非唯一索引值。如果尝试执行不支持重复索引值的操作,将在那时引发异常。

来自字典

Series可以从字典实例化:

In [7]: d = {"b": 1, "a": 0, "c": 2}

In [8]: pd.Series(d)
Out[8]: 
b    1
a    0
c    2
dtype: int64 

如果传递了索引,则将从数据中与索引中的标签对应的值提取出来。

In [9]: d = {"a": 0.0, "b": 1.0, "c": 2.0}

In [10]: pd.Series(d)
Out[10]: 
a    0.0
b    1.0
c    2.0
dtype: float64

In [11]: pd.Series(d, index=["b", "c", "d", "a"])
Out[11]: 
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64 

注意

NaN(不是一个数字)是 pandas 中使用的标准缺失数据标记。

来自标量值

如果data是一个标量值,则必须提供一个索引。该值将被重复以匹配索引的长度。

In [12]: pd.Series(5.0, index=["a", "b", "c", "d", "e"])
Out[12]: 
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64 

Series 类似于 ndarray

Series的行为与ndarray非常相似,并且是大多数 NumPy 函数的有效参数。但是,诸如切片之类的操作也会切片索引。

In [13]: s.iloc[0]
Out[13]: 0.4691122999071863

In [14]: s.iloc[:3]
Out[14]: 
a    0.469112
b   -0.282863
c   -1.509059
dtype: float64

In [15]: s[s > s.median()]
Out[15]: 
a    0.469112
e    1.212112
dtype: float64

In [16]: s.iloc[[4, 3, 1]]
Out[16]: 
e    1.212112
d   -1.135632
b   -0.282863
dtype: float64

In [17]: np.exp(s)
Out[17]: 
a    1.598575
b    0.753623
c    0.221118
d    0.321219
e    3.360575
dtype: float64 

注意

我们将在索引部分中讨论类似于s.iloc[[4, 3, 1]]的基于数组的索引。

像 NumPy 数组一样,pandas 的Series具有单一的dtype

In [18]: s.dtype
Out[18]: dtype('float64') 

这通常是一个 NumPy dtype。然而,pandas 和第三方库在一些地方扩展了 NumPy 的类型系统,此时 dtype 将是一个ExtensionDtype。pandas 内的一些示例是分类数据和可空整数数据类型。更多信息请参见 dtypes。

如果你需要实际支持Series的数组,请使用Series.array

In [19]: s.array
Out[19]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64 

访问数组在你需要执行一些操作而不需要索引(例如禁用自动对齐)时非常有用。

Series.array将始终是一个ExtensionArray。简而言之,ExtensionArray 是一个围绕一个或多个具体数组的薄包装器,比如一个numpy.ndarray. pandas 知道如何获取一个ExtensionArray并将其存储在一个SeriesDataFrame的列中。更多信息请参见 dtypes。

虽然Series类似于 ndarray,如果你需要一个实际的 ndarray,那么请使用Series.to_numpy()

In [20]: s.to_numpy()
Out[20]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121]) 

即使SeriesExtensionArray支持,Series.to_numpy()将返回一个 NumPy ndarray。

Series 类似于字典

一个Series也类似于一个固定大小的字典,你可以通过索引标签获取和设置值:

In [21]: s["a"]
Out[21]: 0.4691122999071863

In [22]: s["e"] = 12.0

In [23]: s
Out[23]: 
a     0.469112
b    -0.282863
c    -1.509059
d    -1.135632
e    12.000000
dtype: float64

In [24]: "e" in s
Out[24]: True

In [25]: "f" in s
Out[25]: False 

如果一个标签不包含在索引中,将会引发异常:

In [26]: s["f"]
---------------------------------------------------------------------------
KeyError  Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key)
  3804 try:
-> 3805     return self._engine.get_loc(casted_key)
  3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'f'

The above exception was the direct cause of the following exception:

KeyError  Traceback (most recent call last)
Cell In[26], line 1
----> 1 s["f"]

File ~/work/pandas/pandas/pandas/core/series.py:1121, in Series.__getitem__(self, key)
  1118     return self._values[key]
  1120 elif key_is_scalar:
-> 1121     return self._get_value(key)
  1123 # Convert generator to list before going through hashable part
  1124 # (We will iterate through the generator there to check for slices)
  1125 if is_iterator(key):

File ~/work/pandas/pandas/pandas/core/series.py:1237, in Series._get_value(self, label, takeable)
  1234     return self._values[label]
  1236 # Similar to Index.get_value, but we do not fall back to positional
-> 1237 loc = self.index.get_loc(label)
  1239 if is_integer(loc):
  1240     return self._values[loc]

File ~/work/pandas/pandas/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
  3807     if isinstance(casted_key, slice) or (
  3808         isinstance(casted_key, abc.Iterable)
  3809         and any(isinstance(x, slice) for x in casted_key)
  3810     ):
  3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
  3813 except TypeError:
  3814     # If we have a listlike key, _check_indexing_error will raise
  3815     #  InvalidIndexError. Otherwise we fall through and re-raise
  3816     #  the TypeError.
  3817     self._check_indexing_error(key)

KeyError: 'f' 

使用Series.get()方法,缺失的标签将返回 None 或指定的默认值:

In [27]: s.get("f")

In [28]: s.get("f", np.nan)
Out[28]: nan 

这些标签也可以通过属性访问。

矢量化操作和与 Series 的标签对齐

在使用原始 NumPy 数组时,通常不需要逐个值循环。在 pandas 中使用 Series 时也是如此。Series 也可以传递给大多数期望 ndarray 的 NumPy 方法。

In [29]: s + s
Out[29]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [30]: s * 2
Out[30]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [31]: np.exp(s)
Out[31]: 
a         1.598575
b         0.753623
c         0.221118
d         0.321219
e    162754.791419
dtype: float64 

Series 与 ndarray 之间的一个关键区别是,Series 之间的操作会根据标签自动对齐数据。因此,您可以编写计算而无需考虑所涉及的 Series 是否具有相同的标签。

In [32]: s.iloc[1:] + s.iloc[:-1]
Out[32]: 
a         NaN
b   -0.565727
c   -3.018117
d   -2.271265
e         NaN
dtype: float64 

未对齐的 Series 之间的操作结果将具有涉及的索引的并集。如果一个标签在其中一个 Series 中找不到,结果将被标记为缺失的 NaN。能够编写代码而无需进行任何显式数据对齐,为交互式数据分析和研究提供了巨大的自由和灵活性。pandas 数据结构的集成数据对齐功能使 pandas 在处理带标签数据的相关工具中脱颖而出。

注意

一般来说,我们选择使不同索引对象之间的操作的默认结果产生索引的并集,以避免信息丢失。即使数据缺失,具有索引标签通常也是计算的重要信息。当然,您可以通过 dropna 函数选择删除缺失数据的标签。

名称属性

Series 还具有一个 name 属性:

In [33]: s = pd.Series(np.random.randn(5), name="something")

In [34]: s
Out[34]: 
0   -0.494929
1    1.071804
2    0.721555
3   -0.706771
4   -1.039575
Name: something, dtype: float64

In [35]: s.name
Out[35]: 'something' 

Series name 在许多情况下可以自动分配,特别是当从 DataFrame 中选择单个列时,name 将被分配为列标签。

您可以使用 pandas.Series.rename() 方法重命名一个 Series

In [36]: s2 = s.rename("different")

In [37]: s2.name
Out[37]: 'different' 

请注意,ss2 指代不同的对象。 ## DataFrame

DataFrame 是一个具有不同类型列的二维标记数据结构。你可以将它看作是一个电子表格或 SQL 表,或者是一组 Series 对象的字典。它通常是最常用的 pandas 对象。与 Series 一样,DataFrame 接受许多不同类型的输入:

  • 1D ndarrays、列表、字典或 Series 的字典

  • 2-D numpy.ndarray

  • 结构化或记录 ndarray

  • 一个 Series

  • 另一个 DataFrame

除了数据,你还可以选择传递 index(行标签)和 columns(列标签)参数。如果传递了索引和/或列,你将保证结果 DataFrame 的索引和/或列。因此,一个 Series 字典加上一个特定索引将丢弃所有与传递索引不匹配的数据。

如果没有传递轴标签,它们将根据常识规则从输入数据中构建。

从 Series 或字典的字典

结果的 索引 将是各个 Series 的索引的 并集。如果有任何嵌套字典,这些将首先转换为 Series。如果没有传递列,列将是字典键的有序列表。

In [38]: d = {
 ....:    "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
 ....:    "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
 ....: }
 ....: 

In [39]: df = pd.DataFrame(d)

In [40]: df
Out[40]: 
 one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In [41]: pd.DataFrame(d, index=["d", "b", "a"])
Out[41]: 
 one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In [42]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[42]: 
 two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN 

行和列标签可以分别通过访问 indexcolumns 属性来访问:

注意

当传递一组特定列以及数据字典时,传递的列将覆盖字典中的键。

In [43]: df.index
Out[43]: Index(['a', 'b', 'c', 'd'], dtype='object')

In [44]: df.columns
Out[44]: Index(['one', 'two'], dtype='object') 

从 ndarrays / 列表的字典

所有的 ndarrays 必须具有相同的长度。如果传递了索引,它也必须与数组的长度相同。如果没有传递索引,结果将是 range(n),其中 n 是数组的长度。

In [45]: d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}

In [46]: pd.DataFrame(d)
Out[46]: 
 one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

In [47]: pd.DataFrame(d, index=["a", "b", "c", "d"])
Out[47]: 
 one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0 

从结构化或记录数组

这种情况与数组字典处理方式相同。

In [48]: data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])

In [49]: data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]

In [50]: pd.DataFrame(data)
Out[50]: 
 A    B         C
0  1  2.0  b'Hello'
1  2  3.0  b'World'

In [51]: pd.DataFrame(data, index=["first", "second"])
Out[51]: 
 A    B         C
first   1  2.0  b'Hello'
second  2  3.0  b'World'

In [52]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[52]: 
 C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0 

注意

DataFrame 不打算完全像一个二维 NumPy ndarray 一样工作。

来自一个字典列表

In [53]: data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]

In [54]: pd.DataFrame(data2)
Out[54]: 
 a   b     c
0  1   2   NaN
1  5  10  20.0

In [55]: pd.DataFrame(data2, index=["first", "second"])
Out[55]: 
 a   b     c
first   1   2   NaN
second  5  10  20.0

In [56]: pd.DataFrame(data2, columns=["a", "b"])
Out[56]: 
 a   b
0  1   2
1  5  10 
```  ### 来自一个元组字典

你可以通过传递一个元组字典自动创建一个 MultiIndexed frame。

```py
In [57]: pd.DataFrame(
 ....:    {
 ....:        ("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
 ....:        ("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
 ....:        ("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
 ....:        ("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
 ....:        ("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
 ....:    }
 ....: )
 ....: 
Out[57]: 
 a              b 
 b    a    c    a     b
A B  1.0  4.0  5.0  8.0  10.0
 C  2.0  3.0  6.0  7.0   NaN
 D  NaN  NaN  NaN  NaN   9.0 
```  ### 来自一个系列

结果将是一个具有与输入 Series 相同索引的 DataFrame,并且具有一个列,其名称是 Series 的原始名称(仅在没有提供其他列名时)。

```py
In [58]: ser = pd.Series(range(3), index=list("abc"), name="ser")

In [59]: pd.DataFrame(ser)
Out[59]: 
 ser
a    0
b    1
c    2 
```  ### 来自一个命名元组列表

列表中第一个 `namedtuple` 的字段名确定 `DataFrame` 的列。 其余的命名元组(或元组)只是被解包,它们的值被提供给 `DataFrame` 的行。 如果任何一个元组比第一个 `namedtuple` 短,则相应行中的后续列将被标记为缺失值。 如果有任何一个比第一个 `namedtuple` 长,则会引发 `ValueError`。

```py
In [60]: from collections import namedtuple

In [61]: Point = namedtuple("Point", "x y")

In [62]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[62]: 
 x  y
0  0  0
1  0  3
2  2  3

In [63]: Point3D = namedtuple("Point3D", "x y z")

In [64]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[64]: 
 x  y    z
0  0  0  0.0
1  0  3  5.0
2  2  3  NaN 
```  ### 从数据类列表

[PEP557](https://www.python.org/dev/peps/pep-0557) 中介绍的数据类可以传递给 DataFrame 构造函数。 将数据类列表传递给它等同于传递字典列表。

请注意,列表中的所有值都应该是数据类,列表中混合类型的值会导致 `TypeError`。

```py
In [65]: from dataclasses import make_dataclass

In [66]: Point = make_dataclass("Point", [("x", int), ("y", int)])

In [67]: pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
Out[67]: 
 x  y
0  0  0
1  0  3
2  2  3 

缺失数据

要构造一个带有缺失数据的 DataFrame,我们使用 np.nan 来表示缺失值。 或者,您可以将 numpy.MaskedArray 作为数据参数传递给 DataFrame 构造函数,其掩码条目将被视为缺失值。 更多信息请参见缺失数据。

替代构造函数

DataFrame.from_dict

DataFrame.from_dict() 接受一个字典的字典或者一个数组序列的字典,并返回一个 DataFrame。 它的操作方式与 DataFrame 构造函数类似,除了默认情况下为 'columns'orient 参数外,还可以设置为 'index',以使用字典键作为行标签。

In [68]: pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
Out[68]: 
 A  B
0  1  4
1  2  5
2  3  6 

如果传递 orient='index',则键将成为行标签。 在这种情况下,您还可以传递所需的列名:

In [69]: pd.DataFrame.from_dict(
 ....:    dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
 ....:    orient="index",
 ....:    columns=["one", "two", "three"],
 ....: )
 ....: 
Out[69]: 
 one  two  three
A    1    2      3
B    4    5      6 

DataFrame.from_records

DataFrame.from_records() 接受一个元组列表或具有结构化 dtype 的 ndarray。 它的工作方式类似于普通的 DataFrame 构造函数,只是生成的 DataFrame 索引可以是结构化 dtype 的特定字段。

In [70]: data
Out[70]: 
array([(1, 2., b'Hello'), (2, 3., b'World')],
 dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

In [71]: pd.DataFrame.from_records(data, index="C")
Out[71]: 
 A    B
C 
b'Hello'  1  2.0
b'World'  2  3.0 

列选择、添加、删除

你可以把DataFrame语义上看作是一个具有相同索引的 Series 对象的字典。 获取、设置和删除列的操作与类似的字典操作具有相同的语法:

In [72]: df["one"]
Out[72]: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64

In [73]: df["three"] = df["one"] * df["two"]

In [74]: df["flag"] = df["one"] > 2

In [75]: df
Out[75]: 
 one  two  three   flag
a  1.0  1.0    1.0  False
b  2.0  2.0    4.0  False
c  3.0  3.0    9.0   True
d  NaN  4.0    NaN  False 

列可以像字典一样被删除或弹出:

In [76]: del df["two"]

In [77]: three = df.pop("three")

In [78]: df
Out[78]: 
 one   flag
a  1.0  False
b  2.0  False
c  3.0   True
d  NaN  False 

当插入标量值时,它会自然地传播以填充列:

In [79]: df["foo"] = "bar"

In [80]: df
Out[80]: 
 one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar 

当插入一个与DataFrame具有不同索引的Series时,它将被调整为 DataFrame 的索引:

In [81]: df["one_trunc"] = df["one"][:2]

In [82]: df
Out[82]: 
 one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN 

您可以插入原始 ndarrays,但它们的长度必须与 DataFrame 的索引长度匹配。

默认情况下,列会被插入到末尾。DataFrame.insert() 在列的特定位置插入:

In [83]: df.insert(1, "bar", df["one"])

In [84]: df
Out[84]: 
 one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN 
```  ### 在方法链中分配新列

灵感源于[dplyr 的](https://dplyr.tidyverse.org/reference/mutate.html) `mutate` 动词,DataFrame 具有一个`assign()` 方法,允许您轻松创建可能源自现有列的新列。

```py
In [85]: iris = pd.read_csv("data/iris.data")

In [86]: iris.head()
Out[86]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name
0          5.1         3.5          1.4         0.2  Iris-setosa
1          4.9         3.0          1.4         0.2  Iris-setosa
2          4.7         3.2          1.3         0.2  Iris-setosa
3          4.6         3.1          1.5         0.2  Iris-setosa
4          5.0         3.6          1.4         0.2  Iris-setosa

In [87]: iris.assign(sepal_ratio=iris["SepalWidth"] / iris["SepalLength"]).head()
Out[87]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

在上面的示例中,我们插入了一个预先计算的值。我们还可以传递一个要在分配给的 DataFrame 上评估的一个参数的函数。

In [88]: iris.assign(sepal_ratio=lambda x: (x["SepalWidth"] / x["SepalLength"])).head()
Out[88]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

assign() 总是返回数据的副本,保持原始 DataFrame 不变。

传递可调用对象,而不是要插入的实际值,在没有对 DataFrame 的引用时非常有用。在操作链中使用assign()时,这是常见的。例如,我们可以将 DataFrame 限制为仅包含萼片长度大于 5 的观测值,计算比率,并绘制:

In [89]: (
 ....:    iris.query("SepalLength > 5")
 ....:    .assign(
 ....:        SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
 ....:        PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
 ....:    )
 ....:    .plot(kind="scatter", x="SepalRatio", y="PetalRatio")
 ....: )
 ....: 
Out[89]: <Axes: xlabel='SepalRatio', ylabel='PetalRatio'> 

../_images/basics_assign.png

由于传递了一个函数,因此该函数在分配给的 DataFrame 上计算。重要的是,这是已经被过滤为那些萼片长度大于 5 的行的 DataFrame。首先进行过滤,然后进行比率计算。这是一个示例,我们没有对过滤的 DataFrame 可用的引用。

assign() 的函数签名只是 **kwargs。键是新字段的列名,值可以是要插入的值(例如,Series或 NumPy 数组),或者是要在DataFrame上调用的一个参数的函数。返回原始DataFrame副本,并插入新值。

**kwargs的顺序是保留的。这允许依赖赋值,其中**kwargs中后面的表达式可以引用同一assign()中先前创建的列。

In [90]: dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})

In [91]: dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
Out[91]: 
 A  B  C   D
0  1  4  5   6
1  2  5  7   9
2  3  6  9  12 

在第二个表达式中,x['C']将指向新创建的列,等于dfa['A'] + dfa['B']

索引/选择

索引的基础知识如下:

操作 语法 结果
选择列 df[col] Series
通过标签选择行 df.loc[label] Series
通过整数位置选择行 df.iloc[loc] Series
切片行 df[5:10] DataFrame
通过布尔向量选择行 df[bool_vec] DataFrame

例如,行选择返回一个其索引为DataFrame的列的Series

In [92]: df.loc["b"]
Out[92]: 
one            2.0
bar            2.0
flag         False
foo            bar
one_trunc      2.0
Name: b, dtype: object

In [93]: df.iloc[2]
Out[93]: 
one           3.0
bar           3.0
flag         True
foo           bar
one_trunc     NaN
Name: c, dtype: object 

对于更详尽的基于标签的索引和切片处理,请参阅索引部分。我们将在重新索引部分中讨论重新索引/符合新标签集的基础知识。

数据对齐和算术

DataFrame对象之间的数据对齐会自动在列和索引(行标签)上对齐。同样,结果对象将具有列和行标签的并集。

In [94]: df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])

In [95]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])

In [96]: df + df2
Out[96]: 
 A         B         C   D
0  0.045691 -0.014138  1.380871 NaN
1 -0.955398 -1.501007  0.037181 NaN
2 -0.662690  1.534833 -0.859691 NaN
3 -2.452949  1.237274 -0.133712 NaN
4  1.414490  1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7       NaN       NaN       NaN NaN
8       NaN       NaN       NaN NaN
9       NaN       NaN       NaN NaN 

DataFrameSeries之间进行操作时,默认行为是将Series索引DataFrame对齐,从而以行方式进行广播。例如:

In [97]: df - df.iloc[0]
Out[97]: 
 A         B         C         D
0  0.000000  0.000000  0.000000  0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2  0.253128  0.829678  0.010026 -1.991234
3 -1.311128  0.054325 -1.724913 -1.620544
4  0.573025  1.500742 -0.676070  1.367331
5 -1.741248  0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282  0.000430
7 -0.743894  0.411013 -0.929563 -0.282386
8 -1.194921  1.320690  0.238224 -1.482644
9  2.293786  1.856228  0.773289 -1.446531 

要对匹配和广播行为进行显式控制,请参阅灵活的二进制操作部分。

与标量进行算术运算是逐元素进行的:

In [98]: df * 5 + 2
Out[98]: 
 A         B         C          D
0   3.359299 -0.124862  4.835102   3.381160
1  -3.437003 -1.368449  2.568242  -5.392133
2   4.624938  4.023526  4.885230  -6.575010
3  -3.196342  0.146766 -3.789461  -4.721559
4   6.224426  7.378849  1.454750  10.217815
5  -5.346940  3.785103 -1.373001  -6.884519
6  -2.844569 -4.472618  4.068691   3.383309
7  -0.360173  1.930201  0.187285   1.969232
8  -2.615303  6.478587  6.026220  -4.032059
9  14.828230  9.156280  8.701544  -3.851494

In [99]: 1 / df
Out[99]: 
 A          B         C           D
0  3.678365  -2.353094  1.763605    3.620145
1 -0.919624  -1.484363  8.799067   -0.676395
2  1.904807   2.470934  1.732964   -0.583090
3 -0.962215  -2.697986 -0.863638   -0.743875
4  1.183593   0.929567 -9.170108    0.608434
5 -0.680555   2.800959 -1.482360   -0.562777
6 -1.032084  -0.772485  2.416988    3.614523
7 -2.118489 -71.634509 -2.758294 -162.507295
8 -1.083352   1.116424  1.241860   -0.828904
9  0.389765   0.698687  0.746097   -0.854483

In [100]: df ** 4
Out[100]: 
 A             B         C             D
0   0.005462  3.261689e-02  0.103370  5.822320e-03
1   1.398165  2.059869e-01  0.000167  4.777482e+00
2   0.075962  2.682596e-02  0.110877  8.650845e+00
3   1.166571  1.887302e-02  1.797515  3.265879e+00
4   0.509555  1.339298e+00  0.000141  7.297019e+00
5   4.661717  1.624699e-02  0.207103  9.969092e+00
6   0.881334  2.808277e+00  0.029302  5.858632e-03
7   0.049647  3.797614e-08  0.017276  1.433866e-09
8   0.725974  6.437005e-01  0.420446  2.118275e+00
9  43.329821  4.196326e+00  3.227153  1.875802e+00 

布尔运算也是逐元素进行的:

In [101]: df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)

In [102]: df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)

In [103]: df1 & df2
Out[103]: 
 a      b
0  False  False
1  False   True
2   True  False

In [104]: df1 | df2
Out[104]: 
 a     b
0  True  True
1  True  True
2  True  True

In [105]: df1 ^ df2
Out[105]: 
 a      b
0   True   True
1   True  False
2  False   True

In [106]: -df1
Out[106]: 
 a      b
0  False   True
1   True  False
2  False  False 

转置

要进行转置,访问T属性或DataFrame.transpose(),类似于一个 ndarray:

# only show the first 5 rows
In [107]: df[:5].T
Out[107]: 
 0         1         2         3         4
A  0.271860 -1.087401  0.524988 -1.039268  0.844885
B -0.424972 -0.673690  0.404705 -0.370647  1.075770
C  0.567020  0.113648  0.577046 -1.157892 -0.109050
D  0.276232 -1.478427 -1.715002 -1.344312  1.643563 

DataFrame 与 NumPy 函数的互操作性

大多数 NumPy 函数可以直接在SeriesDataFrame上调用。

In [108]: np.exp(df)
Out[108]: 
 A         B         C         D
0   1.312403  0.653788  1.763006  1.318154
1   0.337092  0.509824  1.120358  0.227996
2   1.690438  1.498861  1.780770  0.179963
3   0.353713  0.690288  0.314148  0.260719
4   2.327710  2.932249  0.896686  5.173571
5   0.230066  1.429065  0.509360  0.169161
6   0.379495  0.274028  1.512461  1.318720
7   0.623732  0.986137  0.695904  0.993865
8   0.397301  2.449092  2.237242  0.299269
9  13.009059  4.183951  3.820223  0.310274

In [109]: np.asarray(df)
Out[109]: 
array([[ 0.2719, -0.425 ,  0.567 ,  0.2762],
 [-1.0874, -0.6737,  0.1136, -1.4784],
 [ 0.525 ,  0.4047,  0.577 , -1.715 ],
 [-1.0393, -0.3706, -1.1579, -1.3443],
 [ 0.8449,  1.0758, -0.109 ,  1.6436],
 [-1.4694,  0.357 , -0.6746, -1.7769],
 [-0.9689, -1.2945,  0.4137,  0.2767],
 [-0.472 , -0.014 , -0.3625, -0.0062],
 [-0.9231,  0.8957,  0.8052, -1.2064],
 [ 2.5656,  1.4313,  1.3403, -1.1703]]) 

DataFrame并不打算作为 ndarray 的直接替代品,因为它的索引语义和数据模型在某些地方与 n 维数组有很大不同。

Series实现了__array_ufunc__,这使其能够与 NumPy 的通用函数一起使用。

ufunc 被应用于Series中的底层数组。

In [110]: ser = pd.Series([1, 2, 3, 4])

In [111]: np.exp(ser)
Out[111]: 
0     2.718282
1     7.389056
2    20.085537
3    54.598150
dtype: float64 

当多个Series传递给 ufunc 时,在执行操作之前它们会被对齐。

与库的其他部分一样,pandas 将自动对齐带有多个输入的 ufunc 的标记输入。例如,在两个具有不同顺序标签的Series上使用numpy.remainder()将在操作之前对齐。

In [112]: ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])

In [113]: ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])

In [114]: ser1
Out[114]: 
a    1
b    2
c    3
dtype: int64

In [115]: ser2
Out[115]: 
b    1
a    3
c    5
dtype: int64

In [116]: np.remainder(ser1, ser2)
Out[116]: 
a    1
b    0
c    3
dtype: int64 

通常情况下,两个索引的并集被取出,并且非重叠的值被填充为缺失值。

In [117]: ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])

In [118]: ser3
Out[118]: 
b    2
c    4
d    6
dtype: int64

In [119]: np.remainder(ser1, ser3)
Out[119]: 
a    NaN
b    0.0
c    3.0
d    NaN
dtype: float64 

当将二进制 ufunc 应用于SeriesIndex时,Series的实现优先,并返回一个Series

In [120]: ser = pd.Series([1, 2, 3])

In [121]: idx = pd.Index([4, 5, 6])

In [122]: np.maximum(ser, idx)
Out[122]: 
0    4
1    5
2    6
dtype: int64 

NumPy ufuncs 可以安全地应用于由非 ndarray 数组支持的Series,例如arrays.SparseArray(参见稀疏计算)。如果可能,ufunc 将在不将底层数据转换为 ndarray 的情况下应用。

控制台显示

一个非常大的DataFrame将被截断以在控制台中显示。您也可以使用info()来获取摘要信息。(baseball数据集来自plyr R 包):

In [123]: baseball = pd.read_csv("data/baseball.csv")

In [124]: print(baseball)
 id     player  year  stint team  lg  ...    so  ibb  hbp   sh   sf  gidp
0   88641  womacto01  2006      2  CHN  NL  ...   4.0  0.0  0.0  3.0  0.0   0.0
1   88643  schilcu01  2006      1  BOS  AL  ...   1.0  0.0  0.0  0.0  0.0   0.0
..    ...        ...   ...    ...  ...  ..  ...   ...  ...  ...  ...  ...   ...
98  89533   aloumo01  2007      1  NYN  NL  ...  30.0  5.0  2.0  0.0  3.0  13.0
99  89534  alomasa02  2007      1  NYN  NL  ...   3.0  0.0  0.0  0.0  0.0   0.0

[100 rows x 23 columns]

In [125]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   id      100 non-null    int64 
 1   player  100 non-null    object 
 2   year    100 non-null    int64 
 3   stint   100 non-null    int64 
 4   team    100 non-null    object 
 5   lg      100 non-null    object 
 6   g       100 non-null    int64 
 7   ab      100 non-null    int64 
 8   r       100 non-null    int64 
 9   h       100 non-null    int64 
 10  X2b     100 non-null    int64 
 11  X3b     100 non-null    int64 
 12  hr      100 non-null    int64 
 13  rbi     100 non-null    float64
 14  sb      100 non-null    float64
 15  cs      100 non-null    float64
 16  bb      100 non-null    int64 
 17  so      100 non-null    float64
 18  ibb     100 non-null    float64
 19  hbp     100 non-null    float64
 20  sh      100 non-null    float64
 21  sf      100 non-null    float64
 22  gidp    100 non-null    float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.1+ KB 

然而,使用DataFrame.to_string()将以表格形式返回DataFrame的字符串表示,尽管它不总是适合控制台宽度:

In [126]: print(baseball.iloc[-20:, :12].to_string())
 id     player  year  stint team  lg    g   ab   r    h  X2b  X3b
80  89474  finlest01  2007      1  COL  NL   43   94   9   17    3    0
81  89480  embreal01  2007      1  OAK  AL    4    0   0    0    0    0
82  89481  edmonji01  2007      1  SLN  NL  117  365  39   92   15    2
83  89482  easleda01  2007      1  NYN  NL   76  193  24   54    6    0
84  89489  delgaca01  2007      1  NYN  NL  139  538  71  139   30    0
85  89493  cormirh01  2007      1  CIN  NL    6    0   0    0    0    0
86  89494  coninje01  2007      2  NYN  NL   21   41   2    8    2    0
87  89495  coninje01  2007      1  CIN  NL   80  215  23   57   11    1
88  89497  clemero02  2007      1  NYA  AL    2    2   0    1    0    0
89  89498  claytro01  2007      2  BOS  AL    8    6   1    0    0    0
90  89499  claytro01  2007      1  TOR  AL   69  189  23   48   14    0
91  89501  cirilje01  2007      2  ARI  NL   28   40   6    8    4    0
92  89502  cirilje01  2007      1  MIN  AL   50  153  18   40    9    2
93  89521  bondsba01  2007      1  SFN  NL  126  340  75   94   14    0
94  89523  biggicr01  2007      1  HOU  NL  141  517  68  130   31    3
95  89525  benitar01  2007      2  FLO  NL   34    0   0    0    0    0
96  89526  benitar01  2007      1  SFN  NL   19    0   0    0    0    0
97  89530  ausmubr01  2007      1  HOU  NL  117  349  38   82   16    3
98  89533   aloumo01  2007      1  NYN  NL   87  328  51  112   19    1
99  89534  alomasa02  2007      1  NYN  NL    8   22   1    3    1    0 

默认情况下,宽 DataFrame 将跨多行打印:

In [127]: pd.DataFrame(np.random.randn(3, 12))
Out[127]: 
 0         1         2   ...        9         10        11
0 -1.226825  0.769804 -1.281247  ... -1.110336 -0.619976  0.149748
1 -0.732339  0.687738  0.176444  ...  1.462696 -1.743161 -0.826591
2 -0.345352  1.314232  0.690579  ...  0.896171 -0.487602 -0.082240

[3 rows x 12 columns] 

通过设置display.width选项,您可以更改单行打印的数量:

In [128]: pd.set_option("display.width", 40)  # default is 80

In [129]: pd.DataFrame(np.random.randn(3, 12))
Out[129]: 
 0         1         2   ...        9         10        11
0 -2.182937  0.380396  0.084844  ... -0.023688  2.410179  1.450520
1  0.206053 -0.251905 -2.213588  ... -0.025747 -0.988387  0.094055
2  1.262731  1.289997  0.082423  ... -0.281461  0.030711  0.109121

[3 rows x 12 columns] 

您可以通过设置display.max_colwidth来调整各列的最大宽度

In [130]: datafile = {
 .....:    "filename": ["filename_01", "filename_02"],
 .....:    "path": [
 .....:        "media/user_name/storage/folder_01/filename_01",
 .....:        "media/user_name/storage/folder_02/filename_02",
 .....:    ],
 .....: }
 .....: 

In [131]: pd.set_option("display.max_colwidth", 30)

In [132]: pd.DataFrame(datafile)
Out[132]: 
 filename                           path
0  filename_01  media/user_name/storage/fo...
1  filename_02  media/user_name/storage/fo...

In [133]: pd.set_option("display.max_colwidth", 100)

In [134]: pd.DataFrame(datafile)
Out[134]: 
 filename                                           path
0  filename_01  media/user_name/storage/folder_01/filename_01
1  filename_02  media/user_name/storage/folder_02/filename_02 

您还可以通过expand_frame_repr选项禁用此功能。这将在一个块中打印表格。

DataFrame 列属性访问和 IPython 完成

如果DataFrame列标签是一个有效的 Python 变量名,则可以像属性一样访问该列:

In [135]: df = pd.DataFrame({"foo1": np.random.randn(5), "foo2": np.random.randn(5)})

In [136]: df
Out[136]: 
 foo1      foo2
0  1.126203  0.781836
1 -0.977349 -1.071357
2  1.474071  0.441153
3 -0.064034  2.353925
4 -1.282782  0.583787

In [137]: df.foo1
Out[137]: 
0    1.126203
1   -0.977349
2    1.474071
3   -0.064034
4   -1.282782
Name: foo1, dtype: float64 

列也与IPython完成机制连接,因此可以进行制表完成:

In [5]: df.foo<TAB>  # noqa: E225, E999
df.foo1  df.foo2 
```  ## Series

`Series`是一个能够容纳任何数据类型(整数、字符串、浮点数、Python 对象等)的一维标记数组。轴标签总称为**index**。创建`Series`的基本方法是调用:

```py
s = pd.Series(data, index=index) 

这里,data可以是许多不同的东西:

  • 一个 Python 字典

  • 一个 ndarray

  • 一个标量值(如 5)

传递的index是一个轴标签列表。因此,这根据data的内容分为几种情况:

从 ndarray

如果data是一个 ndarray,index必须与data的长度相同。如果没有传递索引,将创建一个具有值[0, ..., len(data) - 1]的索引。

In [3]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [4]: s
Out[4]: 
a    0.469112
b   -0.282863
c   -1.509059
d   -1.135632
e    1.212112
dtype: float64

In [5]: s.index
Out[5]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

In [6]: pd.Series(np.random.randn(5))
Out[6]: 
0   -0.173215
1    0.119209
2   -1.044236
3   -0.861849
4   -2.104569
dtype: float64 

注意

pandas 支持非唯一索引值。如果尝试执行不支持重复索引值的操作,那么将在那时引发异常。

从字典

Series可以从字典实例化:

In [7]: d = {"b": 1, "a": 0, "c": 2}

In [8]: pd.Series(d)
Out[8]: 
b    1
a    0
c    2
dtype: int64 

如果传递了索引,则将从数据中与索引标签对应的值提取出来。

In [9]: d = {"a": 0.0, "b": 1.0, "c": 2.0}

In [10]: pd.Series(d)
Out[10]: 
a    0.0
b    1.0
c    2.0
dtype: float64

In [11]: pd.Series(d, index=["b", "c", "d", "a"])
Out[11]: 
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64 

注意

NaN(不是一个数字)是 pandas 中使用的标准缺失数据标记。

从标量值

如果data是一个标量值,则必须提供一个索引。该值将重复以匹配index的长度。

In [12]: pd.Series(5.0, index=["a", "b", "c", "d", "e"])
Out[12]: 
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64 

Series 类似于 ndarray

Series的行为与ndarray非常相似,并且是大多数 NumPy 函数的有效参数。然而,像切片这样的操作也会切片索引。

In [13]: s.iloc[0]
Out[13]: 0.4691122999071863

In [14]: s.iloc[:3]
Out[14]: 
a    0.469112
b   -0.282863
c   -1.509059
dtype: float64

In [15]: s[s > s.median()]
Out[15]: 
a    0.469112
e    1.212112
dtype: float64

In [16]: s.iloc[[4, 3, 1]]
Out[16]: 
e    1.212112
d   -1.135632
b   -0.282863
dtype: float64

In [17]: np.exp(s)
Out[17]: 
a    1.598575
b    0.753623
c    0.221118
d    0.321219
e    3.360575
dtype: float64 

注意

我们将在索引部分中讨论类似s.iloc[[4, 3, 1]]的基于数组的索引。

与 NumPy 数组类似,pandas 的Series具有单一的dtype

In [18]: s.dtype
Out[18]: dtype('float64') 

这通常是一个 NumPy dtype。然而,pandas 和第三方库在一些地方扩展了 NumPy 的类型系统,此时 dtype 将是一个ExtensionDtype。pandas 中的一些示例包括 Categorical data 和 Nullable integer data type。更多信息请参见 dtypes。

如果需要一个Series的实际数组支持,请使用Series.array

In [19]: s.array
Out[19]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64 

访问数组在需要执行一些操作而不需要索引时(例如禁用自动对齐)时非常有用。

Series.array始终是一个ExtensionArray。简而言之,ExtensionArray 是对一个或多个具体数组(如numpy.ndarray)的薄包装。pandas 知道如何将一个ExtensionArray存储在SeriesDataFrame的列中。更多信息请参见 dtypes。

虽然Series类似于 ndarray,但如果需要一个实际的 ndarray,则使用Series.to_numpy()

In [20]: s.to_numpy()
Out[20]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121]) 

即使Series由一个ExtensionArray��持,Series.to_numpy()也会返回一个 NumPy ndarray。

Series 类似于字典

一个Series也类似于一个固定大小的字典,可以通过索引标签获取和设置值:

In [21]: s["a"]
Out[21]: 0.4691122999071863

In [22]: s["e"] = 12.0

In [23]: s
Out[23]: 
a     0.469112
b    -0.282863
c    -1.509059
d    -1.135632
e    12.000000
dtype: float64

In [24]: "e" in s
Out[24]: True

In [25]: "f" in s
Out[25]: False 

如果标签不包含在索引中,则会引发异常:

In [26]: s["f"]
---------------------------------------------------------------------------
KeyError  Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key)
  3804 try:
-> 3805     return self._engine.get_loc(casted_key)
  3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'f'

The above exception was the direct cause of the following exception:

KeyError  Traceback (most recent call last)
Cell In[26], line 1
----> 1 s["f"]

File ~/work/pandas/pandas/pandas/core/series.py:1121, in Series.__getitem__(self, key)
  1118     return self._values[key]
  1120 elif key_is_scalar:
-> 1121     return self._get_value(key)
  1123 # Convert generator to list before going through hashable part
  1124 # (We will iterate through the generator there to check for slices)
  1125 if is_iterator(key):

File ~/work/pandas/pandas/pandas/core/series.py:1237, in Series._get_value(self, label, takeable)
  1234     return self._values[label]
  1236 # Similar to Index.get_value, but we do not fall back to positional
-> 1237 loc = self.index.get_loc(label)
  1239 if is_integer(loc):
  1240     return self._values[loc]

File ~/work/pandas/pandas/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
  3807     if isinstance(casted_key, slice) or (
  3808         isinstance(casted_key, abc.Iterable)
  3809         and any(isinstance(x, slice) for x in casted_key)
  3810     ):
  3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
  3813 except TypeError:
  3814     # If we have a listlike key, _check_indexing_error will raise
  3815     #  InvalidIndexError. Otherwise we fall through and re-raise
  3816     #  the TypeError.
  3817     self._check_indexing_error(key)

KeyError: 'f' 

使用Series.get()方法,如果缺少标签,则返回 None 或指定的默认值:

In [27]: s.get("f")

In [28]: s.get("f", np.nan)
Out[28]: nan 

这些标签也可以通过属性访问。

与 Series 进行矢量化操作和标签对齐

在使用原始 NumPy 数组时,通常不需要逐个值循环。在 pandas 中使用Series时也是如此。Series也可以传递给大多数期望 ndarray 的 NumPy 方法。

In [29]: s + s
Out[29]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [30]: s * 2
Out[30]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [31]: np.exp(s)
Out[31]: 
a         1.598575
b         0.753623
c         0.221118
d         0.321219
e    162754.791419
dtype: float64 

Series和 ndarray 之间的一个关键区别是,Series之间的操作会根据标签自动对齐数据。因此,您可以编写计算而不必考虑所涉及的Series是否具有相同的标签。

In [32]: s.iloc[1:] + s.iloc[:-1]
Out[32]: 
a         NaN
b   -0.565727
c   -3.018117
d   -2.271265
e         NaN
dtype: float64 

未对齐的Series之间的操作结果将具有涉及的索引的并集。如果一个标签在一个Series或另一个中找不到,则结果将被标记为缺失的NaN。能够编写不进行任何显式数据对齐的代码为交互式数据分析和研究提供了巨大的自由和灵活性。pandas 数据结构的集成数据对齐功能使 pandas 在处理带标签数据的相关工具中脱颖而出。

注意

一般来说,我们选择使不同索引对象之间的操作的默认结果产生索引的并集,以避免信息丢失。即使数据缺失,具有索引标签通常也是计算的重要信息。当然,您可以通过dropna函数选择删除缺失数据的标签。

名称属性

Series还有一个name属性:

In [33]: s = pd.Series(np.random.randn(5), name="something")

In [34]: s
Out[34]: 
0   -0.494929
1    1.071804
2    0.721555
3   -0.706771
4   -1.039575
Name: something, dtype: float64

In [35]: s.name
Out[35]: 'something' 

在许多情况下,Seriesname可以自动分配,特别是当从DataFrame中选择单列时,name将被分配为列标签。

您可以使用pandas.Series.rename()方法重命名一个Series

In [36]: s2 = s.rename("different")

In [37]: s2.name
Out[37]: 'different' 

请注意,ss2指向不同的对象。

Series 类似于 ndarray

Series的行为与ndarray非常相似,并且是大多数 NumPy 函数的有效参数。但是,诸如切片之类的操作也会切片索引。

In [13]: s.iloc[0]
Out[13]: 0.4691122999071863

In [14]: s.iloc[:3]
Out[14]: 
a    0.469112
b   -0.282863
c   -1.509059
dtype: float64

In [15]: s[s > s.median()]
Out[15]: 
a    0.469112
e    1.212112
dtype: float64

In [16]: s.iloc[[4, 3, 1]]
Out[16]: 
e    1.212112
d   -1.135632
b   -0.282863
dtype: float64

In [17]: np.exp(s)
Out[17]: 
a    1.598575
b    0.753623
c    0.221118
d    0.321219
e    3.360575
dtype: float64 

注意

我们将在索引部分中讨论类似于s.iloc[[4, 3, 1]]的基于数组的索引。

像 NumPy 数组一样,pandas 的Series具有单一的dtype

In [18]: s.dtype
Out[18]: dtype('float64') 

这通常是一个 NumPy 的 dtype。然而,pandas 和第三方库在一些地方扩展了 NumPy 的类型系统,在这种情况下,dtype 将是一个 ExtensionDtype。pandas 中的一些示例包括 分类数据 和 可空整数数据类型。详情请参阅 dtypes。

如果你需要一个 Series 的实际数组支持,请使用 Series.array

In [19]: s.array
Out[19]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64 

当你需要执行一些操作而不使用索引时(例如禁用 自动对齐),访问数组可能很有用。

Series.array 总是一个 ExtensionArray。简而言之,ExtensionArray 是对一个或多个 具体 数组的轻量级包装,比如 numpy.ndarray。pandas 知道如何获取一个 ExtensionArray 并将其存储在一个 SeriesDataFrame 的列中。详情请参阅 dtypes。

虽然 Series 类似于 ndarray,但如果你需要一个 实际 的 ndarray,请使用 Series.to_numpy()

In [20]: s.to_numpy()
Out[20]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121]) 

即使 Series 是由 ExtensionArray 支持的,Series.to_numpy() 也会返回一个 NumPy ndarray。

Series 类似于字典

一个 Series 也像一个固定大小的字典,你可以通过索引标签来获取和设置值:

In [21]: s["a"]
Out[21]: 0.4691122999071863

In [22]: s["e"] = 12.0

In [23]: s
Out[23]: 
a     0.469112
b    -0.282863
c    -1.509059
d    -1.135632
e    12.000000
dtype: float64

In [24]: "e" in s
Out[24]: True

In [25]: "f" in s
Out[25]: False 

如果标签不包含在索引中,则会引发异常:

In [26]: s["f"]
---------------------------------------------------------------------------
KeyError  Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key)
  3804 try:
-> 3805     return self._engine.get_loc(casted_key)
  3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'f'

The above exception was the direct cause of the following exception:

KeyError  Traceback (most recent call last)
Cell In[26], line 1
----> 1 s["f"]

File ~/work/pandas/pandas/pandas/core/series.py:1121, in Series.__getitem__(self, key)
  1118     return self._values[key]
  1120 elif key_is_scalar:
-> 1121     return self._get_value(key)
  1123 # Convert generator to list before going through hashable part
  1124 # (We will iterate through the generator there to check for slices)
  1125 if is_iterator(key):

File ~/work/pandas/pandas/pandas/core/series.py:1237, in Series._get_value(self, label, takeable)
  1234     return self._values[label]
  1236 # Similar to Index.get_value, but we do not fall back to positional
-> 1237 loc = self.index.get_loc(label)
  1239 if is_integer(loc):
  1240     return self._values[loc]

File ~/work/pandas/pandas/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
  3807     if isinstance(casted_key, slice) or (
  3808         isinstance(casted_key, abc.Iterable)
  3809         and any(isinstance(x, slice) for x in casted_key)
  3810     ):
  3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
  3813 except TypeError:
  3814     # If we have a listlike key, _check_indexing_error will raise
  3815     #  InvalidIndexError. Otherwise we fall through and re-raise
  3816     #  the TypeError.
  3817     self._check_indexing_error(key)

KeyError: 'f' 

使用 Series.get() 方法,如果缺少标签,则返回 None 或指定的默认值:

In [27]: s.get("f")

In [28]: s.get("f", np.nan)
Out[28]: nan 

这些标签也可以通过 属性 来访问。

矢量化操作和 Series 的标签对齐

当使用原始的 NumPy 数组时,通常不需要逐值循环。在 pandas 中使用Series时也是如此。Series也可以传递给大多数期望 ndarray 的 NumPy 方法。

In [29]: s + s
Out[29]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [30]: s * 2
Out[30]: 
a     0.938225
b    -0.565727
c    -3.018117
d    -2.271265
e    24.000000
dtype: float64

In [31]: np.exp(s)
Out[31]: 
a         1.598575
b         0.753623
c         0.221118
d         0.321219
e    162754.791419
dtype: float64 

Series和 ndarray 之间的一个关键区别是,Series之间的操作会根据标签自动对齐数据。因此,你可以在不考虑涉及的Series是否具有相同标签的情况下编写计算。

In [32]: s.iloc[1:] + s.iloc[:-1]
Out[32]: 
a         NaN
b   -0.565727
c   -3.018117
d   -2.271265
e         NaN
dtype: float64 

不对齐的Series之间的操作结果将具有涉及的索引的并集。如果一个标签在一个Series中找不到或另一个中找不到,则结果将标记为缺失的NaN。能够编写不执行任何显式数据对齐的代码为交互式数据分析和研究提供了巨大的自由和灵活性。pandas 数据结构的集成数据对齐功能使其在处理带有标签数据的相关工具中脱颖而出。

注意

一般来说,我们选择让不同索引对象之间的操作的默认结果产生索引的并集,以避免信息丢失。即使数据缺失,拥有索引标签通常也是计算的重要信息。当然,你可以选择通过dropna函数丢弃带有缺失数据的标签。

名称属性

Series还有一个name属性:

In [33]: s = pd.Series(np.random.randn(5), name="something")

In [34]: s
Out[34]: 
0   -0.494929
1    1.071804
2    0.721555
3   -0.706771
4   -1.039575
Name: something, dtype: float64

In [35]: s.name
Out[35]: 'something' 

在许多情况下,DataFramename可以被自动赋值,特别是当从DataFrame中选择单列时,name将被分配为列标签。

你可以使用pandas.Series.rename()方法重命名一个Series

In [36]: s2 = s.rename("different")

In [37]: s2.name
Out[37]: 'different' 

请注意,ss2指向不同的对象。

DataFrame

DataFrame是一个带有可能不同类型列的二维标签数据结构。你可以将它视为电子表格或 SQL 表,或者是一系列 Series 对象的字典。它通常是最常用的 pandas 对象。与 Series 一样,DataFrame 接受许多不同类型的输入:

  • 1D ndarrays、列表、字典或Series的字典

  • 2-D numpy.ndarray

  • 结构化或记录 ndarray

  • Series

  • 另一个DataFrame

除了数据之外,您还可以选择传递index(行标签)和columns(列标签)参数。如果传递了索引和/或列,则保证了结果 DataFrame 的索引和/或列。因此,字典的 Series 加上特定索引将丢弃所有与传递索引不匹配的数据。

如果未传递轴标签,它们将根据常识规则从输入数据构建。

来自 Series 字典或字典

结果的索引将是各个 Series 的并集。如果有任何嵌套的字典,它们将首先被转换为 Series。如果未传递任何列,则列将是字典键的有序列表。

In [38]: d = {
 ....:    "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
 ....:    "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
 ....: }
 ....: 

In [39]: df = pd.DataFrame(d)

In [40]: df
Out[40]: 
 one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In [41]: pd.DataFrame(d, index=["d", "b", "a"])
Out[41]: 
 one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In [42]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[42]: 
 two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN 

行和列标签分别可以通过访问indexcolumns属性来访问:

注意

当与数据字典一起传递了特定列集时,传递的列将覆盖字典中的键。

In [43]: df.index
Out[43]: Index(['a', 'b', 'c', 'd'], dtype='object')

In [44]: df.columns
Out[44]: Index(['one', 'two'], dtype='object') 

来自字典的 ndarrays / 列表

所有 ndarrays 必须具有相同的长度。如果传递了索引,则它必须与数组的长度相同。如果未传递索引,则结果将是 range(n),其中 n 是数组长度。

In [45]: d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}

In [46]: pd.DataFrame(d)
Out[46]: 
 one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

In [47]: pd.DataFrame(d, index=["a", "b", "c", "d"])
Out[47]: 
 one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0 

来自结构化或记录数组

这种情况与字典数组的处理方式相同。

In [48]: data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])

In [49]: data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]

In [50]: pd.DataFrame(data)
Out[50]: 
 A    B         C
0  1  2.0  b'Hello'
1  2  3.0  b'World'

In [51]: pd.DataFrame(data, index=["first", "second"])
Out[51]: 
 A    B         C
first   1  2.0  b'Hello'
second  2  3.0  b'World'

In [52]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[52]: 
 C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0 

注意

DataFrame 不打算像 2 维 NumPy ndarray 一样工作。

来自字典列表

In [53]: data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]

In [54]: pd.DataFrame(data2)
Out[54]: 
 a   b     c
0  1   2   NaN
1  5  10  20.0

In [55]: pd.DataFrame(data2, index=["first", "second"])
Out[55]: 
 a   b     c
first   1   2   NaN
second  5  10  20.0

In [56]: pd.DataFrame(data2, columns=["a", "b"])
Out[56]: 
 a   b
0  1   2
1  5  10 
```  ### 来自元组字典

通过传递元组字典,您可以自动创建一个多级索引的框架。

```py
In [57]: pd.DataFrame(
 ....:    {
 ....:        ("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
 ....:        ("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
 ....:        ("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
 ....:        ("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
 ....:        ("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
 ....:    }
 ....: )
 ....: 
Out[57]: 
 a              b 
 b    a    c    a     b
A B  1.0  4.0  5.0  8.0  10.0
 C  2.0  3.0  6.0  7.0   NaN
 D  NaN  NaN  NaN  NaN   9.0 
```  ### 来自 Series

结果将是一个具有与输入 Series 相同索引的 DataFrame,并且有一个列,其名称为 Series 的原始名称(仅当没有提供其他列名时)。

```py
In [58]: ser = pd.Series(range(3), index=list("abc"), name="ser")

In [59]: pd.DataFrame(ser)
Out[59]: 
 ser
a    0
b    1
c    2 
```  ### 来自命名元组列表

列表中第一个`namedtuple`的字段名确定`DataFrame`的列。剩余的命名元组(或元组)只需展开,它们的值就会被输入到`DataFrame`的行中。如果任何一个元组比第一个`namedtuple`短,那么相应行中的后续列将被标记为缺失值。如果有任何一个元组比第一个`namedtuple`长,就会引发`ValueError`。

```py
In [60]: from collections import namedtuple

In [61]: Point = namedtuple("Point", "x y")

In [62]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[62]: 
 x  y
0  0  0
1  0  3
2  2  3

In [63]: Point3D = namedtuple("Point3D", "x y z")

In [64]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[64]: 
 x  y    z
0  0  0  0.0
1  0  3  5.0
2  2  3  NaN 
```  ### 来自数据类列表

正如[PEP557](https://www.python.org/dev/peps/pep-0557)中介绍的数据类,可以传递给 DataFrame 构造函数。传递数据类列表相当于传递字典列表。

请注意,列表中的所有值都应该是数据类,混合类型会导致`TypeError`。

```py
In [65]: from dataclasses import make_dataclass

In [66]: Point = make_dataclass("Point", [("x", int), ("y", int)])

In [67]: pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
Out[67]: 
 x  y
0  0  0
1  0  3
2  2  3 

缺失数据

要构建具有缺失数据的 DataFrame,我们使用np.nan表示缺失值。或者,您可以将numpy.MaskedArray作为数据参数传递给 DataFrame 构造函数,其掩码条目将被视为缺失值。更多信息请参见缺失数据。

替代构造函数

DataFrame.from_dict

DataFrame.from_dict() 接受一个字典的字典或者一个数组样式序列的字典,并返回一个 DataFrame。它的操作类似于DataFrame 构造函数,除了orient参数默认为'columns',但可以设置为'index'以使用字典键作为行标签。

In [68]: pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
Out[68]: 
 A  B
0  1  4
1  2  5
2  3  6 

如果传递orient='index',键将成为行标签。在这种情况下,您还可以传递所需的列名:

In [69]: pd.DataFrame.from_dict(
 ....:    dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
 ....:    orient="index",
 ....:    columns=["one", "two", "three"],
 ....: )
 ....: 
Out[69]: 
 one  two  three
A    1    2      3
B    4    5      6 

DataFrame.from_records

DataFrame.from_records() 接受一个元组列表或者具有结构化 dtype 的 ndarray。它的工作方式类似于普通的DataFrame 构造函数,不同之处在于生成的 DataFrame 索引可能是结构化 dtype 的特定字段。

In [70]: data
Out[70]: 
array([(1, 2., b'Hello'), (2, 3., b'World')],
 dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

In [71]: pd.DataFrame.from_records(data, index="C")
Out[71]: 
 A    B
C 
b'Hello'  1  2.0
b'World'  2  3.0 

列选择、添加、删除

你可以将一个DataFrame在语义上视为具有相同索引的Series对象的字典。获取、设置和删除列的操作与类似的字典操作具有相同的语法:

In [72]: df["one"]
Out[72]: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64

In [73]: df["three"] = df["one"] * df["two"]

In [74]: df["flag"] = df["one"] > 2

In [75]: df
Out[75]: 
 one  two  three   flag
a  1.0  1.0    1.0  False
b  2.0  2.0    4.0  False
c  3.0  3.0    9.0   True
d  NaN  4.0    NaN  False 

列可以像字典一样被删除或弹出:

In [76]: del df["two"]

In [77]: three = df.pop("three")

In [78]: df
Out[78]: 
 one   flag
a  1.0  False
b  2.0  False
c  3.0   True
d  NaN  False 

当插入标量值时,它将自然传播以填充列:

In [79]: df["foo"] = "bar"

In [80]: df
Out[80]: 
 one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar 

当插入一个与DataFrame索引不同的Series时,它将被调整为 DataFrame 的索引:

In [81]: df["one_trunc"] = df["one"][:2]

In [82]: df
Out[82]: 
 one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN 

你可以插入原始的 ndarrays,但它们的长度必须与 DataFrame 的索引长度匹配。

默认情况下,列会被插入到末尾。DataFrame.insert() 在列的特定位置插入:

In [83]: df.insert(1, "bar", df["one"])

In [84]: df
Out[84]: 
 one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN 
```  ### 在方法链中分配新列

受 [dplyr](https://dplyr.tidyverse.org/reference/mutate.html) 的 `mutate` 动词的启发,DataFrame 具有一个 `assign()` 方法,允许您轻松创建新列,这些新列可能来源于现有列。

```py
In [85]: iris = pd.read_csv("data/iris.data")

In [86]: iris.head()
Out[86]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name
0          5.1         3.5          1.4         0.2  Iris-setosa
1          4.9         3.0          1.4         0.2  Iris-setosa
2          4.7         3.2          1.3         0.2  Iris-setosa
3          4.6         3.1          1.5         0.2  Iris-setosa
4          5.0         3.6          1.4         0.2  Iris-setosa

In [87]: iris.assign(sepal_ratio=iris["SepalWidth"] / iris["SepalLength"]).head()
Out[87]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

在上面的示例中,我们插入了一个预先计算的值。我们还可以传递一个参数为 DataFrame 的函数,以在被赋值的 DataFrame 上进行评估。

In [88]: iris.assign(sepal_ratio=lambda x: (x["SepalWidth"] / x["SepalLength"])).head()
Out[88]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

assign() 总是 返回数据的副本,不会改变原始 DataFrame。

当你手头没有 DataFrame 的引用时,将可调用对象传递给 assign()(在一系列操作中使用时很常见)比传递要插入的实际值更有用。例如,我们可以将 DataFrame 限制为仅包含萼片长度大于 5 的观测值,计算比率并绘制:

In [89]: (
 ....:    iris.query("SepalLength > 5")
 ....:    .assign(
 ....:        SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
 ....:        PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
 ....:    )
 ....:    .plot(kind="scatter", x="SepalRatio", y="PetalRatio")
 ....: )
 ....: 
Out[89]: <Axes: xlabel='SepalRatio', ylabel='PetalRatio'> 

../_images/basics_assign.png

由于传递了一个函数,因此该函数在被赋值的 DataFrame 上被计算。重要的是,这是已经被过滤为萼片长度大于 5 的那些行的 DataFrame。首先进行过滤,然后进行比率计算。这是一个示例,在该示例中我们没有引用 被过滤 的 DataFrame。

assign() 的函数签名简单地是 **kwargs。键是新字段的列名,值可以是要插入的值(例如,Series 或 NumPy 数组),也可以是要在 DataFrame 上调用的一个参数的函数。返回原始 DataFrame副本,并插入新值。

**kwargs 的顺序被保留。这允许进行 依赖 赋值,其中 **kwargs 中稍后的表达式可以引用同一 assign() 中稍早创建的列。

In [90]: dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})

In [91]: dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
Out[91]: 
 A  B  C   D
0  1  4  5   6
1  2  5  7   9
2  3  6  9  12 

在第二个表达式中,x['C'] 将引用新创建的列,该列等于 dfa['A'] + dfa['B']

索引/选择

索引的基础如下:

操作 语法 结果
选择列 df[col] Series
通过标签选择行 df.loc[label] Series
通过整数位置选择行 df.iloc[loc] Series
切片行 df[5:10] DataFrame
通过布尔向量选择行 df[bool_vec] DataFrame

例如,行选择返回一个索引为DataFrame列的Series

In [92]: df.loc["b"]
Out[92]: 
one            2.0
bar            2.0
flag         False
foo            bar
one_trunc      2.0
Name: b, dtype: object

In [93]: df.iloc[2]
Out[93]: 
one           3.0
bar           3.0
flag         True
foo           bar
one_trunc     NaN
Name: c, dtype: object 

要详细了解基于标签的索引和切片的复杂处理,请参阅索引部分。我们将在重新索引部分中讨论重新索引 / 符合新标签集的基础知识。

数据对齐和算术

DataFrame 对象之间的数据对齐会自动在列和索引(行标签)上对齐。同样,结果对象将具有列和行标签的并集。

In [94]: df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])

In [95]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])

In [96]: df + df2
Out[96]: 
 A         B         C   D
0  0.045691 -0.014138  1.380871 NaN
1 -0.955398 -1.501007  0.037181 NaN
2 -0.662690  1.534833 -0.859691 NaN
3 -2.452949  1.237274 -0.133712 NaN
4  1.414490  1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7       NaN       NaN       NaN NaN
8       NaN       NaN       NaN NaN
9       NaN       NaN       NaN NaN 

DataFrameSeries之间进行操作时,默认行为是将Series索引DataFrame进行对齐,因此以行方式进行广播。例如:

In [97]: df - df.iloc[0]
Out[97]: 
 A         B         C         D
0  0.000000  0.000000  0.000000  0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2  0.253128  0.829678  0.010026 -1.991234
3 -1.311128  0.054325 -1.724913 -1.620544
4  0.573025  1.500742 -0.676070  1.367331
5 -1.741248  0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282  0.000430
7 -0.743894  0.411013 -0.929563 -0.282386
8 -1.194921  1.320690  0.238224 -1.482644
9  2.293786  1.856228  0.773289 -1.446531 

要显式控制匹配和广播行为,请参阅灵活的二进制操作部分。

与标量进行算术运算是逐元素进行的:

In [98]: df * 5 + 2
Out[98]: 
 A         B         C          D
0   3.359299 -0.124862  4.835102   3.381160
1  -3.437003 -1.368449  2.568242  -5.392133
2   4.624938  4.023526  4.885230  -6.575010
3  -3.196342  0.146766 -3.789461  -4.721559
4   6.224426  7.378849  1.454750  10.217815
5  -5.346940  3.785103 -1.373001  -6.884519
6  -2.844569 -4.472618  4.068691   3.383309
7  -0.360173  1.930201  0.187285   1.969232
8  -2.615303  6.478587  6.026220  -4.032059
9  14.828230  9.156280  8.701544  -3.851494

In [99]: 1 / df
Out[99]: 
 A          B         C           D
0  3.678365  -2.353094  1.763605    3.620145
1 -0.919624  -1.484363  8.799067   -0.676395
2  1.904807   2.470934  1.732964   -0.583090
3 -0.962215  -2.697986 -0.863638   -0.743875
4  1.183593   0.929567 -9.170108    0.608434
5 -0.680555   2.800959 -1.482360   -0.562777
6 -1.032084  -0.772485  2.416988    3.614523
7 -2.118489 -71.634509 -2.758294 -162.507295
8 -1.083352   1.116424  1.241860   -0.828904
9  0.389765   0.698687  0.746097   -0.854483

In [100]: df ** 4
Out[100]: 
 A             B         C             D
0   0.005462  3.261689e-02  0.103370  5.822320e-03
1   1.398165  2.059869e-01  0.000167  4.777482e+00
2   0.075962  2.682596e-02  0.110877  8.650845e+00
3   1.166571  1.887302e-02  1.797515  3.265879e+00
4   0.509555  1.339298e+00  0.000141  7.297019e+00
5   4.661717  1.624699e-02  0.207103  9.969092e+00
6   0.881334  2.808277e+00  0.029302  5.858632e-03
7   0.049647  3.797614e-08  0.017276  1.433866e-09
8   0.725974  6.437005e-01  0.420446  2.118275e+00
9  43.329821  4.196326e+00  3.227153  1.875802e+00 

布尔运算也是逐元素进行的:

In [101]: df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)

In [102]: df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)

In [103]: df1 & df2
Out[103]: 
 a      b
0  False  False
1  False   True
2   True  False

In [104]: df1 | df2
Out[104]: 
 a     b
0  True  True
1  True  True
2  True  True

In [105]: df1 ^ df2
Out[105]: 
 a      b
0   True   True
1   True  False
2  False   True

In [106]: -df1
Out[106]: 
 a      b
0  False   True
1   True  False
2  False  False 

转置

要进行转置,请访问T属性或DataFrame.transpose(),类似于 ndarray:

# only show the first 5 rows
In [107]: df[:5].T
Out[107]: 
 0         1         2         3         4
A  0.271860 -1.087401  0.524988 -1.039268  0.844885
B -0.424972 -0.673690  0.404705 -0.370647  1.075770
C  0.567020  0.113648  0.577046 -1.157892 -0.109050
D  0.276232 -1.478427 -1.715002 -1.344312  1.643563 

DataFrame 与 NumPy 函数的互操作性

大多数 NumPy 函数可以直接在SeriesDataFrame上调用。

In [108]: np.exp(df)
Out[108]: 
 A         B         C         D
0   1.312403  0.653788  1.763006  1.318154
1   0.337092  0.509824  1.120358  0.227996
2   1.690438  1.498861  1.780770  0.179963
3   0.353713  0.690288  0.314148  0.260719
4   2.327710  2.932249  0.896686  5.173571
5   0.230066  1.429065  0.509360  0.169161
6   0.379495  0.274028  1.512461  1.318720
7   0.623732  0.986137  0.695904  0.993865
8   0.397301  2.449092  2.237242  0.299269
9  13.009059  4.183951  3.820223  0.310274

In [109]: np.asarray(df)
Out[109]: 
array([[ 0.2719, -0.425 ,  0.567 ,  0.2762],
 [-1.0874, -0.6737,  0.1136, -1.4784],
 [ 0.525 ,  0.4047,  0.577 , -1.715 ],
 [-1.0393, -0.3706, -1.1579, -1.3443],
 [ 0.8449,  1.0758, -0.109 ,  1.6436],
 [-1.4694,  0.357 , -0.6746, -1.7769],
 [-0.9689, -1.2945,  0.4137,  0.2767],
 [-0.472 , -0.014 , -0.3625, -0.0062],
 [-0.9231,  0.8957,  0.8052, -1.2064],
 [ 2.5656,  1.4313,  1.3403, -1.1703]]) 

DataFrame 并不打算成为 ndarray 的直接替代品,因为它的索引语义和数据模型在某些地方与 n 维数组有很大的不同。

Series 实现了 __array_ufunc__,这使其能够与 NumPy 的通用函数一起使用。

ufunc 应用于Series中的底层数组。

In [110]: ser = pd.Series([1, 2, 3, 4])

In [111]: np.exp(ser)
Out[111]: 
0     2.718282
1     7.389056
2    20.085537
3    54.598150
dtype: float64 

当多个Series传递给 ufunc 时,它们在执行操作之前会进行对齐。

与库的其他部分一样,pandas 将自动对齐具有多个输入的 ufunc 的标记输入。例如,在两个具有不同顺序标签的Series上使用numpy.remainder()将在操作之前对齐。

In [112]: ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])

In [113]: ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])

In [114]: ser1
Out[114]: 
a    1
b    2
c    3
dtype: int64

In [115]: ser2
Out[115]: 
b    1
a    3
c    5
dtype: int64

In [116]: np.remainder(ser1, ser2)
Out[116]: 
a    1
b    0
c    3
dtype: int64 

通常情况下,将取两个索引的并集,并用缺失值填充不重叠的值。

In [117]: ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])

In [118]: ser3
Out[118]: 
b    2
c    4
d    6
dtype: int64

In [119]: np.remainder(ser1, ser3)
Out[119]: 
a    NaN
b    0.0
c    3.0
d    NaN
dtype: float64 

当对SeriesIndex应用二进制 ufunc 时,Series实现优先,并返回一个Series

In [120]: ser = pd.Series([1, 2, 3])

In [121]: idx = pd.Index([4, 5, 6])

In [122]: np.maximum(ser, idx)
Out[122]: 
0    4
1    5
2    6
dtype: int64 

NumPy ufuncs 可以安全地应用于由非 ndarray 数组支持的Series,例如arrays.SparseArray(参见稀疏计算)。如果可能,ufunc 将在不将基础数据转换为 ndarray 的情况下应用。

控制台显示

一个非常大的DataFrame将被截断以在控制台中显示。您还可以使用info()获取摘要信息。(baseball数据集来自plyr R 包):

In [123]: baseball = pd.read_csv("data/baseball.csv")

In [124]: print(baseball)
 id     player  year  stint team  lg  ...    so  ibb  hbp   sh   sf  gidp
0   88641  womacto01  2006      2  CHN  NL  ...   4.0  0.0  0.0  3.0  0.0   0.0
1   88643  schilcu01  2006      1  BOS  AL  ...   1.0  0.0  0.0  0.0  0.0   0.0
..    ...        ...   ...    ...  ...  ..  ...   ...  ...  ...  ...  ...   ...
98  89533   aloumo01  2007      1  NYN  NL  ...  30.0  5.0  2.0  0.0  3.0  13.0
99  89534  alomasa02  2007      1  NYN  NL  ...   3.0  0.0  0.0  0.0  0.0   0.0

[100 rows x 23 columns]

In [125]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   id      100 non-null    int64 
 1   player  100 non-null    object 
 2   year    100 non-null    int64 
 3   stint   100 non-null    int64 
 4   team    100 non-null    object 
 5   lg      100 non-null    object 
 6   g       100 non-null    int64 
 7   ab      100 non-null    int64 
 8   r       100 non-null    int64 
 9   h       100 non-null    int64 
 10  X2b     100 non-null    int64 
 11  X3b     100 non-null    int64 
 12  hr      100 non-null    int64 
 13  rbi     100 non-null    float64
 14  sb      100 non-null    float64
 15  cs      100 non-null    float64
 16  bb      100 non-null    int64 
 17  so      100 non-null    float64
 18  ibb     100 non-null    float64
 19  hbp     100 non-null    float64
 20  sh      100 non-null    float64
 21  sf      100 non-null    float64
 22  gidp    100 non-null    float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.1+ KB 

然而,使用DataFrame.to_string()将以表格形式返回DataFrame的字符串表示,尽管它不总是适合控制台宽度:

In [126]: print(baseball.iloc[-20:, :12].to_string())
 id     player  year  stint team  lg    g   ab   r    h  X2b  X3b
80  89474  finlest01  2007      1  COL  NL   43   94   9   17    3    0
81  89480  embreal01  2007      1  OAK  AL    4    0   0    0    0    0
82  89481  edmonji01  2007      1  SLN  NL  117  365  39   92   15    2
83  89482  easleda01  2007      1  NYN  NL   76  193  24   54    6    0
84  89489  delgaca01  2007      1  NYN  NL  139  538  71  139   30    0
85  89493  cormirh01  2007      1  CIN  NL    6    0   0    0    0    0
86  89494  coninje01  2007      2  NYN  NL   21   41   2    8    2    0
87  89495  coninje01  2007      1  CIN  NL   80  215  23   57   11    1
88  89497  clemero02  2007      1  NYA  AL    2    2   0    1    0    0
89  89498  claytro01  2007      2  BOS  AL    8    6   1    0    0    0
90  89499  claytro01  2007      1  TOR  AL   69  189  23   48   14    0
91  89501  cirilje01  2007      2  ARI  NL   28   40   6    8    4    0
92  89502  cirilje01  2007      1  MIN  AL   50  153  18   40    9    2
93  89521  bondsba01  2007      1  SFN  NL  126  340  75   94   14    0
94  89523  biggicr01  2007      1  HOU  NL  141  517  68  130   31    3
95  89525  benitar01  2007      2  FLO  NL   34    0   0    0    0    0
96  89526  benitar01  2007      1  SFN  NL   19    0   0    0    0    0
97  89530  ausmubr01  2007      1  HOU  NL  117  349  38   82   16    3
98  89533   aloumo01  2007      1  NYN  NL   87  328  51  112   19    1
99  89534  alomasa02  2007      1  NYN  NL    8   22   1    3    1    0 

默认情况下,宽 DataFrame 将跨多行打印:

In [127]: pd.DataFrame(np.random.randn(3, 12))
Out[127]: 
 0         1         2   ...        9         10        11
0 -1.226825  0.769804 -1.281247  ... -1.110336 -0.619976  0.149748
1 -0.732339  0.687738  0.176444  ...  1.462696 -1.743161 -0.826591
2 -0.345352  1.314232  0.690579  ...  0.896171 -0.487602 -0.082240

[3 rows x 12 columns] 

通过设置display.width选项,您可以更改单行打印的数量:

In [128]: pd.set_option("display.width", 40)  # default is 80

In [129]: pd.DataFrame(np.random.randn(3, 12))
Out[129]: 
 0         1         2   ...        9         10        11
0 -2.182937  0.380396  0.084844  ... -0.023688  2.410179  1.450520
1  0.206053 -0.251905 -2.213588  ... -0.025747 -0.988387  0.094055
2  1.262731  1.289997  0.082423  ... -0.281461  0.030711  0.109121

[3 rows x 12 columns] 

通过设置display.max_colwidth,您可以调整各列的最大宽度

In [130]: datafile = {
 .....:    "filename": ["filename_01", "filename_02"],
 .....:    "path": [
 .....:        "media/user_name/storage/folder_01/filename_01",
 .....:        "media/user_name/storage/folder_02/filename_02",
 .....:    ],
 .....: }
 .....: 

In [131]: pd.set_option("display.max_colwidth", 30)

In [132]: pd.DataFrame(datafile)
Out[132]: 
 filename                           path
0  filename_01  media/user_name/storage/fo...
1  filename_02  media/user_name/storage/fo...

In [133]: pd.set_option("display.max_colwidth", 100)

In [134]: pd.DataFrame(datafile)
Out[134]: 
 filename                                           path
0  filename_01  media/user_name/storage/folder_01/filename_01
1  filename_02  media/user_name/storage/folder_02/filename_02 

您还可以通过expand_frame_repr选项禁用此功能。这将以一个块打印表格。

DataFrame 列属性访问和 IPython 完成

如果DataFrame列标签是有效的 Python 变量名,则可以像属性一样访问该列:

In [135]: df = pd.DataFrame({"foo1": np.random.randn(5), "foo2": np.random.randn(5)})

In [136]: df
Out[136]: 
 foo1      foo2
0  1.126203  0.781836
1 -0.977349 -1.071357
2  1.474071  0.441153
3 -0.064034  2.353925
4 -1.282782  0.583787

In [137]: df.foo1
Out[137]: 
0    1.126203
1   -0.977349
2    1.474071
3   -0.064034
4   -1.282782
Name: foo1, dtype: float64 

列也与IPython完成机制连接,因此可以进行制表符补全:

In [5]: df.foo<TAB>  # noqa: E225, E999
df.foo1  df.foo2 

来自 Series 或 dicts 的字典

结果的索引将是各个 Series 的索引的并集。如果有任何嵌套的字典,这些将首先转换为 Series。如果未传递任何列,则列将是字典键的有序列表。

In [38]: d = {
 ....:    "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
 ....:    "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
 ....: }
 ....: 

In [39]: df = pd.DataFrame(d)

In [40]: df
Out[40]: 
 one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In [41]: pd.DataFrame(d, index=["d", "b", "a"])
Out[41]: 
 one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In [42]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[42]: 
 two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN 

分别通过访问indexcolumns属性可以访问行和列标签:

注意

当特定的列集与数据字典一起传递时,传递的列会覆盖字典中的键。

In [43]: df.index
Out[43]: Index(['a', 'b', 'c', 'd'], dtype='object')

In [44]: df.columns
Out[44]: Index(['one', 'two'], dtype='object') 

从 ndarray / 列表的字典

所有的 ndarray 必须具有相同的长度。如果传递了一个索引,它必须与数组的长度相同。如果没有传递索引,结果将是range(n),其中n是数组的长度。

In [45]: d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}

In [46]: pd.DataFrame(d)
Out[46]: 
 one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

In [47]: pd.DataFrame(d, index=["a", "b", "c", "d"])
Out[47]: 
 one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0 

从结构化或记录数组

这种情况与数组字典的处理方式相同。

In [48]: data = np.zeros((2,), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])

In [49]: data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]

In [50]: pd.DataFrame(data)
Out[50]: 
 A    B         C
0  1  2.0  b'Hello'
1  2  3.0  b'World'

In [51]: pd.DataFrame(data, index=["first", "second"])
Out[51]: 
 A    B         C
first   1  2.0  b'Hello'
second  2  3.0  b'World'

In [52]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[52]: 
 C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0 

注意

DataFrame 的预期工作方式不是完全像 2 维 NumPy ndarray 一样。

从字典的列表

In [53]: data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]

In [54]: pd.DataFrame(data2)
Out[54]: 
 a   b     c
0  1   2   NaN
1  5  10  20.0

In [55]: pd.DataFrame(data2, index=["first", "second"])
Out[55]: 
 a   b     c
first   1   2   NaN
second  5  10  20.0

In [56]: pd.DataFrame(data2, columns=["a", "b"])
Out[56]: 
 a   b
0  1   2
1  5  10 

从元组的字典

你可以通过传递一个元组字典来自动创建一个 MultiIndexed frame。

In [57]: pd.DataFrame(
 ....:    {
 ....:        ("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
 ....:        ("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
 ....:        ("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
 ....:        ("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
 ....:        ("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
 ....:    }
 ....: )
 ....: 
Out[57]: 
 a              b 
 b    a    c    a     b
A B  1.0  4.0  5.0  8.0  10.0
 C  2.0  3.0  6.0  7.0   NaN
 D  NaN  NaN  NaN  NaN   9.0 

从 Series

结果将是一个具有与输入 Series 相同索引的 DataFrame,并且只有一个列,其名称是 Series 的原始名称(仅当未提供其他列名时)。

In [58]: ser = pd.Series(range(3), index=list("abc"), name="ser")

In [59]: pd.DataFrame(ser)
Out[59]: 
 ser
a    0
b    1
c    2 

从命名元组的列表

列表中第一个namedtuple的字段名确定DataFrame的列。剩余的命名元组(或元组)只是简单地解包,它们的值被输入到DataFrame的行中。如果任何一个元组比第一个namedtuple短,那么相应行中后面的列将被标记为缺失值。如果任何一个比第一个namedtuple长,将引发ValueError

In [60]: from collections import namedtuple

In [61]: Point = namedtuple("Point", "x y")

In [62]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[62]: 
 x  y
0  0  0
1  0  3
2  2  3

In [63]: Point3D = namedtuple("Point3D", "x y z")

In [64]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[64]: 
 x  y    z
0  0  0  0.0
1  0  3  5.0
2  2  3  NaN 

从数据类的列表

PEP557 中介绍的数据类可以传递给 DataFrame 构造函数。传递一个数据类列表等同于传递一个字典列表。

请注意,列表中的所有值都应该是数据类,混合类型的列表会导致TypeError

In [65]: from dataclasses import make_dataclass

In [66]: Point = make_dataclass("Point", [("x", int), ("y", int)])

In [67]: pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
Out[67]: 
 x  y
0  0  0
1  0  3
2  2  3 

缺失数据

为构造带有缺失数据的 DataFrame,我们使用np.nan表示缺失值。或者,您可以将numpy.MaskedArray作为数据参数传递给 DataFrame 构造函数,其掩码条目将被视为缺失值。有关更多信息,请参阅缺失数据。

替代构造函数

DataFrame.from_dict

DataFrame.from_dict()接受一个字典的字典或一个数组样式序列的字典,并返回一个 DataFrame。它的操作类似于DataFrame构造函数,除了默认情况下是'columns'orient参数,但可以设置为'index'以将字典键用作行标签。

In [68]: pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
Out[68]: 
 A  B
0  1  4
1  2  5
2  3  6 

如果传递orient='index',则键将是行标签。在这种情况下,你也可以传递所需的列名:

In [69]: pd.DataFrame.from_dict(
 ....:    dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
 ....:    orient="index",
 ....:    columns=["one", "two", "three"],
 ....: )
 ....: 
Out[69]: 
 one  two  three
A    1    2      3
B    4    5      6 

DataFrame.from_records

DataFrame.from_records() 接受一个元组列表或具有结构化 dtype 的 ndarray。它的工作方式类似于普通的 DataFrame 构造函数,只是结果 DataFrame 的索引可能是结构化 dtype 的特定字段。

In [70]: data
Out[70]: 
array([(1, 2., b'Hello'), (2, 3., b'World')],
 dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

In [71]: pd.DataFrame.from_records(data, index="C")
Out[71]: 
 A    B
C 
b'Hello'  1  2.0
b'World'  2  3.0 

列选择、添加、删除

你可以将 DataFrame 在语义上视为具有相同索引的 Series 对象的字典。获取、设置和删除列的操作与相应的字典操作具有相同的语法:

In [72]: df["one"]
Out[72]: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64

In [73]: df["three"] = df["one"] * df["two"]

In [74]: df["flag"] = df["one"] > 2

In [75]: df
Out[75]: 
 one  two  three   flag
a  1.0  1.0    1.0  False
b  2.0  2.0    4.0  False
c  3.0  3.0    9.0   True
d  NaN  4.0    NaN  False 

列可以像操作字典一样被删除或弹出:

In [76]: del df["two"]

In [77]: three = df.pop("three")

In [78]: df
Out[78]: 
 one   flag
a  1.0  False
b  2.0  False
c  3.0   True
d  NaN  False 

当插入一个标量值时,它自然会被传播以填充列:

In [79]: df["foo"] = "bar"

In [80]: df
Out[80]: 
 one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar 

当插入一个不具有与 DataFrame 相同索引的 Series 时,它将被调整为 DataFrame 的索引:

In [81]: df["one_trunc"] = df["one"][:2]

In [82]: df
Out[82]: 
 one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN 

你可以插入原始的 ndarray,但它们的长度必须与 DataFrame 的索引长度相匹配。

默认情况下,列会被插入到末尾。DataFrame.insert() 在列中的特定位置插入:

In [83]: df.insert(1, "bar", df["one"])

In [84]: df
Out[84]: 
 one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN 

在方法链中分配新列

dplyr mutate 动词的启发,DataFrame 具有一个assign() 方法,允许您轻松创建可能派生自现有列的新列。

In [85]: iris = pd.read_csv("data/iris.data")

In [86]: iris.head()
Out[86]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name
0          5.1         3.5          1.4         0.2  Iris-setosa
1          4.9         3.0          1.4         0.2  Iris-setosa
2          4.7         3.2          1.3         0.2  Iris-setosa
3          4.6         3.1          1.5         0.2  Iris-setosa
4          5.0         3.6          1.4         0.2  Iris-setosa

In [87]: iris.assign(sepal_ratio=iris["SepalWidth"] / iris["SepalLength"]).head()
Out[87]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

在上面的例子中,我们插入了一个预先计算的值。我们也可以传递一个带有一个参数的函数,该函数将在被分配给的 DataFrame 上评估。

In [88]: iris.assign(sepal_ratio=lambda x: (x["SepalWidth"] / x["SepalLength"])).head()
Out[88]: 
 SepalLength  SepalWidth  PetalLength  PetalWidth         Name  sepal_ratio
0          5.1         3.5          1.4         0.2  Iris-setosa     0.686275
1          4.9         3.0          1.4         0.2  Iris-setosa     0.612245
2          4.7         3.2          1.3         0.2  Iris-setosa     0.680851
3          4.6         3.1          1.5         0.2  Iris-setosa     0.673913
4          5.0         3.6          1.4         0.2  Iris-setosa     0.720000 

assign() 总是 返回数据的副本,不会改变原始的 DataFrame。

传递一个可调用对象,而不是要插入的实际值,当你手头没有 DataFrame 的引用时会很有用。在一系列操作中使用assign()时,这是很常见的情况。例如,我们可以将 DataFrame 限制为仅包含萼片长度大于 5 的观测值,计算比率,并绘制:

In [89]: (
 ....:    iris.query("SepalLength > 5")
 ....:    .assign(
 ....:        SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
 ....:        PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
 ....:    )
 ....:    .plot(kind="scatter", x="SepalRatio", y="PetalRatio")
 ....: )
 ....: 
Out[89]: <Axes: xlabel='SepalRatio', ylabel='PetalRatio'> 

../_images/basics_assign.png

由于传入了一个函数,该函数是在被分配的 DataFrame 上计算的。重要的是,这是被过滤为那些萼片长度大于 5 的行的 DataFrame。过滤首先发生,然后是比率计算。这是一个例子,我们没有 过滤 DataFrame 的引用可用。

assign() 的函数签名只是 **kwargs。键是新字段的列名,值可以是要插入的值(例如,Series 或 NumPy 数组),或者是要在DataFrame 上调用的一个参数的函数。返回原始DataFrame副本,并插入新值。

**kwargs 的顺序被保留。这允许进行依赖分配,其中在 **kwargs 中较晚的表达式可以引用同一assign() 中较早创建的列。

In [90]: dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})

In [91]: dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
Out[91]: 
 A  B  C   D
0  1  4  5   6
1  2  5  7   9
2  3  6  9  12 

在第二个表达式中,x['C']将引用新创建的列,其等于dfa['A'] + dfa['B']

索引/选择

索引的基础如下所示:

操作 语法 结果
选择列 df[col] Series
根据标签选择行 df.loc[label] Series
根据整数位置选择行 df.iloc[loc] Series
切片行 df[5:10] DataFrame
根据布尔向量选择行 df[bool_vec] DataFrame

行选择,例如,返回一个其索引为DataFrame列的Series

In [92]: df.loc["b"]
Out[92]: 
one            2.0
bar            2.0
flag         False
foo            bar
one_trunc      2.0
Name: b, dtype: object

In [93]: df.iloc[2]
Out[93]: 
one           3.0
bar           3.0
flag         True
foo           bar
one_trunc     NaN
Name: c, dtype: object 

要对复杂的基于标签的索引和切片进行更全面的处理,请参阅索引部分。我们将在重新索引部分讨论重新索引/符合新标签集的基础知识。

数据对齐和算术

DataFrame 对象之间的数据对齐会自动在列和索引(行标签)上进行对齐。同样,结果对象将具有列和行标签的并集。

In [94]: df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])

In [95]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])

In [96]: df + df2
Out[96]: 
 A         B         C   D
0  0.045691 -0.014138  1.380871 NaN
1 -0.955398 -1.501007  0.037181 NaN
2 -0.662690  1.534833 -0.859691 NaN
3 -2.452949  1.237274 -0.133712 NaN
4  1.414490  1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7       NaN       NaN       NaN NaN
8       NaN       NaN       NaN NaN
9       NaN       NaN       NaN NaN 

当在 DataFrameSeries 之间进行操作时,默认行为是将 Series索引DataFrame 对齐,从而进行按行广播。例如:

In [97]: df - df.iloc[0]
Out[97]: 
 A         B         C         D
0  0.000000  0.000000  0.000000  0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2  0.253128  0.829678  0.010026 -1.991234
3 -1.311128  0.054325 -1.724913 -1.620544
4  0.573025  1.500742 -0.676070  1.367331
5 -1.741248  0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282  0.000430
7 -0.743894  0.411013 -0.929563 -0.282386
8 -1.194921  1.320690  0.238224 -1.482644
9  2.293786  1.856228  0.773289 -1.446531 

要对匹配和广播行为进行显式控制,请参阅 灵活的二元操作 部分。

与标量的算术运算按元素进行:

In [98]: df * 5 + 2
Out[98]: 
 A         B         C          D
0   3.359299 -0.124862  4.835102   3.381160
1  -3.437003 -1.368449  2.568242  -5.392133
2   4.624938  4.023526  4.885230  -6.575010
3  -3.196342  0.146766 -3.789461  -4.721559
4   6.224426  7.378849  1.454750  10.217815
5  -5.346940  3.785103 -1.373001  -6.884519
6  -2.844569 -4.472618  4.068691   3.383309
7  -0.360173  1.930201  0.187285   1.969232
8  -2.615303  6.478587  6.026220  -4.032059
9  14.828230  9.156280  8.701544  -3.851494

In [99]: 1 / df
Out[99]: 
 A          B         C           D
0  3.678365  -2.353094  1.763605    3.620145
1 -0.919624  -1.484363  8.799067   -0.676395
2  1.904807   2.470934  1.732964   -0.583090
3 -0.962215  -2.697986 -0.863638   -0.743875
4  1.183593   0.929567 -9.170108    0.608434
5 -0.680555   2.800959 -1.482360   -0.562777
6 -1.032084  -0.772485  2.416988    3.614523
7 -2.118489 -71.634509 -2.758294 -162.507295
8 -1.083352   1.116424  1.241860   -0.828904
9  0.389765   0.698687  0.746097   -0.854483

In [100]: df ** 4
Out[100]: 
 A             B         C             D
0   0.005462  3.261689e-02  0.103370  5.822320e-03
1   1.398165  2.059869e-01  0.000167  4.777482e+00
2   0.075962  2.682596e-02  0.110877  8.650845e+00
3   1.166571  1.887302e-02  1.797515  3.265879e+00
4   0.509555  1.339298e+00  0.000141  7.297019e+00
5   4.661717  1.624699e-02  0.207103  9.969092e+00
6   0.881334  2.808277e+00  0.029302  5.858632e-03
7   0.049647  3.797614e-08  0.017276  1.433866e-09
8   0.725974  6.437005e-01  0.420446  2.118275e+00
9  43.329821  4.196326e+00  3.227153  1.875802e+00 

布尔运算也是按元素进行的:

In [101]: df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)

In [102]: df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)

In [103]: df1 & df2
Out[103]: 
 a      b
0  False  False
1  False   True
2   True  False

In [104]: df1 | df2
Out[104]: 
 a     b
0  True  True
1  True  True
2  True  True

In [105]: df1 ^ df2
Out[105]: 
 a      b
0   True   True
1   True  False
2  False   True

In [106]: -df1
Out[106]: 
 a      b
0  False   True
1   True  False
2  False  False 

转置

要进行转置,访问 T 属性或 DataFrame.transpose(),类似于 ndarray:

# only show the first 5 rows
In [107]: df[:5].T
Out[107]: 
 0         1         2         3         4
A  0.271860 -1.087401  0.524988 -1.039268  0.844885
B -0.424972 -0.673690  0.404705 -0.370647  1.075770
C  0.567020  0.113648  0.577046 -1.157892 -0.109050
D  0.276232 -1.478427 -1.715002 -1.344312  1.643563 

DataFrame 与 NumPy 函数的互操作性

大多数 NumPy 函数可以直接在 SeriesDataFrame 上调用。

In [108]: np.exp(df)
Out[108]: 
 A         B         C         D
0   1.312403  0.653788  1.763006  1.318154
1   0.337092  0.509824  1.120358  0.227996
2   1.690438  1.498861  1.780770  0.179963
3   0.353713  0.690288  0.314148  0.260719
4   2.327710  2.932249  0.896686  5.173571
5   0.230066  1.429065  0.509360  0.169161
6   0.379495  0.274028  1.512461  1.318720
7   0.623732  0.986137  0.695904  0.993865
8   0.397301  2.449092  2.237242  0.299269
9  13.009059  4.183951  3.820223  0.310274

In [109]: np.asarray(df)
Out[109]: 
array([[ 0.2719, -0.425 ,  0.567 ,  0.2762],
 [-1.0874, -0.6737,  0.1136, -1.4784],
 [ 0.525 ,  0.4047,  0.577 , -1.715 ],
 [-1.0393, -0.3706, -1.1579, -1.3443],
 [ 0.8449,  1.0758, -0.109 ,  1.6436],
 [-1.4694,  0.357 , -0.6746, -1.7769],
 [-0.9689, -1.2945,  0.4137,  0.2767],
 [-0.472 , -0.014 , -0.3625, -0.0062],
 [-0.9231,  0.8957,  0.8052, -1.2064],
 [ 2.5656,  1.4313,  1.3403, -1.1703]]) 

DataFrame 不打算作为 ndarray 的直接替代品,因为其索引语义和数据模型在某些地方与 n 维数组有很大不同。

Series 实现了 __array_ufunc__,这使其能够与 NumPy 的 通用函数 一起使用。

Series 中,ufunc 应用于底层数组。

In [110]: ser = pd.Series([1, 2, 3, 4])

In [111]: np.exp(ser)
Out[111]: 
0     2.718282
1     7.389056
2    20.085537
3    54.598150
dtype: float64 

当多个 Series 传递给一个 ufunc 时,它们在执行操作之前被对齐。

与库的其他部分一样,pandas 在多输入的 ufunc 中会自动对齐带标签的输入。例如,在两个具有不同顺序标签的 Series 上使用 numpy.remainder() 将在操作之前对齐。

In [112]: ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])

In [113]: ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])

In [114]: ser1
Out[114]: 
a    1
b    2
c    3
dtype: int64

In [115]: ser2
Out[115]: 
b    1
a    3
c    5
dtype: int64

In [116]: np.remainder(ser1, ser2)
Out[116]: 
a    1
b    0
c    3
dtype: int64 

通常,两个索引的并集被取出,非重叠值填充为缺失值。

In [117]: ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])

In [118]: ser3
Out[118]: 
b    2
c    4
d    6
dtype: int64

In [119]: np.remainder(ser1, ser3)
Out[119]: 
a    NaN
b    0.0
c    3.0
d    NaN
dtype: float64 

当对一个 Series 和一个 Index 应用二元 ufunc 时,Series 实现优先,并返回一个 Series

In [120]: ser = pd.Series([1, 2, 3])

In [121]: idx = pd.Index([4, 5, 6])

In [122]: np.maximum(ser, idx)
Out[122]: 
0    4
1    5
2    6
dtype: int64 

NumPy ufuncs 可以安全应用于由非 ndarray 数组支持的Series,例如arrays.SparseArray(参见稀疏计算)。如果可能,ufunc 将在不将基础数据转换为 ndarray 的情况下应用。

控制台显示

一个非常大的DataFrame将被截断以在控制台中显示。您还可以使用info()获取摘要信息。(baseball数据集来自plyr R 包):

In [123]: baseball = pd.read_csv("data/baseball.csv")

In [124]: print(baseball)
 id     player  year  stint team  lg  ...    so  ibb  hbp   sh   sf  gidp
0   88641  womacto01  2006      2  CHN  NL  ...   4.0  0.0  0.0  3.0  0.0   0.0
1   88643  schilcu01  2006      1  BOS  AL  ...   1.0  0.0  0.0  0.0  0.0   0.0
..    ...        ...   ...    ...  ...  ..  ...   ...  ...  ...  ...  ...   ...
98  89533   aloumo01  2007      1  NYN  NL  ...  30.0  5.0  2.0  0.0  3.0  13.0
99  89534  alomasa02  2007      1  NYN  NL  ...   3.0  0.0  0.0  0.0  0.0   0.0

[100 rows x 23 columns]

In [125]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   id      100 non-null    int64 
 1   player  100 non-null    object 
 2   year    100 non-null    int64 
 3   stint   100 non-null    int64 
 4   team    100 non-null    object 
 5   lg      100 non-null    object 
 6   g       100 non-null    int64 
 7   ab      100 non-null    int64 
 8   r       100 non-null    int64 
 9   h       100 non-null    int64 
 10  X2b     100 non-null    int64 
 11  X3b     100 non-null    int64 
 12  hr      100 non-null    int64 
 13  rbi     100 non-null    float64
 14  sb      100 non-null    float64
 15  cs      100 non-null    float64
 16  bb      100 non-null    int64 
 17  so      100 non-null    float64
 18  ibb     100 non-null    float64
 19  hbp     100 non-null    float64
 20  sh      100 non-null    float64
 21  sf      100 non-null    float64
 22  gidp    100 non-null    float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.1+ KB 

然而,使用DataFrame.to_string()将以表格形式返回DataFrame的字符串表示,尽管它不总是适合控制台宽度:

In [126]: print(baseball.iloc[-20:, :12].to_string())
 id     player  year  stint team  lg    g   ab   r    h  X2b  X3b
80  89474  finlest01  2007      1  COL  NL   43   94   9   17    3    0
81  89480  embreal01  2007      1  OAK  AL    4    0   0    0    0    0
82  89481  edmonji01  2007      1  SLN  NL  117  365  39   92   15    2
83  89482  easleda01  2007      1  NYN  NL   76  193  24   54    6    0
84  89489  delgaca01  2007      1  NYN  NL  139  538  71  139   30    0
85  89493  cormirh01  2007      1  CIN  NL    6    0   0    0    0    0
86  89494  coninje01  2007      2  NYN  NL   21   41   2    8    2    0
87  89495  coninje01  2007      1  CIN  NL   80  215  23   57   11    1
88  89497  clemero02  2007      1  NYA  AL    2    2   0    1    0    0
89  89498  claytro01  2007      2  BOS  AL    8    6   1    0    0    0
90  89499  claytro01  2007      1  TOR  AL   69  189  23   48   14    0
91  89501  cirilje01  2007      2  ARI  NL   28   40   6    8    4    0
92  89502  cirilje01  2007      1  MIN  AL   50  153  18   40    9    2
93  89521  bondsba01  2007      1  SFN  NL  126  340  75   94   14    0
94  89523  biggicr01  2007      1  HOU  NL  141  517  68  130   31    3
95  89525  benitar01  2007      2  FLO  NL   34    0   0    0    0    0
96  89526  benitar01  2007      1  SFN  NL   19    0   0    0    0    0
97  89530  ausmubr01  2007      1  HOU  NL  117  349  38   82   16    3
98  89533   aloumo01  2007      1  NYN  NL   87  328  51  112   19    1
99  89534  alomasa02  2007      1  NYN  NL    8   22   1    3    1    0 

默认情况下,宽数据框将跨多行打印:

In [127]: pd.DataFrame(np.random.randn(3, 12))
Out[127]: 
 0         1         2   ...        9         10        11
0 -1.226825  0.769804 -1.281247  ... -1.110336 -0.619976  0.149748
1 -0.732339  0.687738  0.176444  ...  1.462696 -1.743161 -0.826591
2 -0.345352  1.314232  0.690579  ...  0.896171 -0.487602 -0.082240

[3 rows x 12 columns] 

通过设置display.width选项,可以更改单行打印的数量:

In [128]: pd.set_option("display.width", 40)  # default is 80

In [129]: pd.DataFrame(np.random.randn(3, 12))
Out[129]: 
 0         1         2   ...        9         10        11
0 -2.182937  0.380396  0.084844  ... -0.023688  2.410179  1.450520
1  0.206053 -0.251905 -2.213588  ... -0.025747 -0.988387  0.094055
2  1.262731  1.289997  0.082423  ... -0.281461  0.030711  0.109121

[3 rows x 12 columns] 

通过设置display.max_colwidth可以调整各列的最大宽度

In [130]: datafile = {
 .....:    "filename": ["filename_01", "filename_02"],
 .....:    "path": [
 .....:        "media/user_name/storage/folder_01/filename_01",
 .....:        "media/user_name/storage/folder_02/filename_02",
 .....:    ],
 .....: }
 .....: 

In [131]: pd.set_option("display.max_colwidth", 30)

In [132]: pd.DataFrame(datafile)
Out[132]: 
 filename                           path
0  filename_01  media/user_name/storage/fo...
1  filename_02  media/user_name/storage/fo...

In [133]: pd.set_option("display.max_colwidth", 100)

In [134]: pd.DataFrame(datafile)
Out[134]: 
 filename                                           path
0  filename_01  media/user_name/storage/folder_01/filename_01
1  filename_02  media/user_name/storage/folder_02/filename_02 

您还可以通过expand_frame_repr选项禁用此功能。这将以一个块打印表格。

DataFrame 列属性访问和 IPython 补全

如果DataFrame列标签是有效的 Python 变量名,则可以像属性一样访问该列:

In [135]: df = pd.DataFrame({"foo1": np.random.randn(5), "foo2": np.random.randn(5)})

In [136]: df
Out[136]: 
 foo1      foo2
0  1.126203  0.781836
1 -0.977349 -1.071357
2  1.474071  0.441153
3 -0.064034  2.353925
4 -1.282782  0.583787

In [137]: df.foo1
Out[137]: 
0    1.126203
1   -0.977349
2    1.474071
3   -0.064034
4   -1.282782
Name: foo1, dtype: float64 

列也与IPython的补全机制相连,因此可以进行制表完成:

In [5]: df.foo<TAB>  # noqa: E225, E999
df.foo1  df.foo2 

基本功能

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

在这里,我们讨论了与 pandas 数据结构共同的许多基本功能。首先,让我们创建一些示例对象,就像我们在 10 分钟入门 pandas 部分中所做的那样:

In [1]: index = pd.date_range("1/1/2000", periods=8)

In [2]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"]) 

头部和尾部

要查看 Series 或 DataFrame 对象的小样本,请使用 head()tail() 方法。默认显示的元素数量为五个,但您可以传递自定义数量。

In [4]: long_series = pd.Series(np.random.randn(1000))

In [5]: long_series.head()
Out[5]: 
0   -1.157892
1   -1.344312
2    0.844885
3    1.075770
4   -0.109050
dtype: float64

In [6]: long_series.tail(3)
Out[6]: 
997   -0.289388
998   -1.020544
999    0.589993
dtype: float64 
```  ## 属性和基础数据

pandas 对象具有许多属性,使您能够访问元数据

+   **shape**: 给出对象的轴维度,与 ndarray 一致

+   轴标签

    +   **Series**: *索引*(唯一轴)

    +   **DataFrame**: *索引*(行)和 *列*

请注意��**这些属性可以安全地分配给**!

```py
In [7]: df[:2]
Out[7]: 
 A         B         C
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929

In [8]: df.columns = [x.lower() for x in df.columns]

In [9]: df
Out[9]: 
 a         b         c
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03  1.071804  0.721555 -0.706771
2000-01-04 -1.039575  0.271860 -0.424972
2000-01-05  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427
2000-01-07  0.524988  0.404705  0.577046
2000-01-08 -1.715002 -1.039268 -0.370647 

pandas 对象(IndexSeriesDataFrame)可以被视为数组的容器,其中包含实际数据并执行实际计算。对于许多类型,底层数组是一个 numpy.ndarray。然而,pandas 和第三方库可能会扩展 NumPy 的类型系统以支持自定义数组(请参阅 dtypes)。

要获取 IndexSeries 中的实际数据,请使用 .array 属性

In [10]: s.array
Out[10]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64

In [11]: s.index.array
Out[11]: 
<NumpyExtensionArray>
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object 

array 将始终是一个 ExtensionArray。关于 ExtensionArray 的确切细节以及 pandas 为什么使用它们略微超出了本介绍的范围。更多信息请参见 dtypes。

如果您知道需要 NumPy 数组,请使用 to_numpy()numpy.asarray()

In [12]: s.to_numpy()
Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

In [13]: np.asarray(s)
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121]) 

当 Series 或 Index 由 ExtensionArray 支持时,to_numpy() 可能涉及复制数据和强制值。更多信息请参见 dtypes。

to_numpy() 可以控制生成的 numpy.ndarraydtype。例如,考虑带有时区的日期时间。NumPy 没有一种 dtype 来表示带时区的日期时间,因此有两种可能有用的表示方式:

  1. 一个带有 Timestamp 对象的对象-dtype numpy.ndarray,每个对象都具有正确的 tz

  2. 一个 datetime64[ns] -dtype 的 numpy.ndarray,其中值已转换为 UTC 并且时区被丢弃

时区可能会被保留为 dtype=object

In [14]: ser = pd.Series(pd.date_range("2000", periods=2, tz="CET"))

In [15]: ser.to_numpy(dtype=object)
Out[15]: 
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
 Timestamp('2000-01-02 00:00:00+0100', tz='CET')], dtype=object) 

或者使用 dtype='datetime64[ns]' 丢弃

In [16]: ser.to_numpy(dtype="datetime64[ns]")
Out[16]: 
array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'],
 dtype='datetime64[ns]') 

获取 DataFrame 中的“原始数据”可能会更加复杂。当您的 DataFrame 的所有列只有一个数据类型时,DataFrame.to_numpy() 将返回底层数据:

In [17]: df.to_numpy()
Out[17]: 
array([[-0.1732,  0.1192, -1.0442],
 [-0.8618, -2.1046, -0.4949],
 [ 1.0718,  0.7216, -0.7068],
 [-1.0396,  0.2719, -0.425 ],
 [ 0.567 ,  0.2762, -1.0874],
 [-0.6737,  0.1136, -1.4784],
 [ 0.525 ,  0.4047,  0.577 ],
 [-1.715 , -1.0393, -0.3706]]) 

如果 DataFrame 包含同质类型数据,那么 ndarray 实际上可以就地修改,并且更改将反映在数据结构中。对于异构数据(例如 DataFrame 的某些列不全是相同的 dtype),情况则不同。与轴标签不同,值属性本身不能被赋值。

注意

在处理异构数据时,生成的 ndarray 的 dtype 将被选择以容纳所有涉及的数据。例如,如果涉及字符串,则结果将是对象 dtype。如果只有浮点数和整数,则生成的数组将是浮点数 dtype。

在过去,pandas 推荐使用 Series.valuesDataFrame.values 从 Series 或 DataFrame 中提取数据。您仍然会在旧代码库和在线上找到这些引用。未来,我们建议避免使用 .values,而是使用 .array.to_numpy().values 有以下缺点:

  1. 当你的 Series 包含一个扩展类型时,不清楚 Series.values 返回一个 NumPy 数组还是扩展数组。Series.array 总是返回一个 ExtensionArray,并且永远不会复制数据。Series.to_numpy() 总是返回一个 NumPy 数组,可能会造成复制/强制转换值的代价。

  2. 当你的 DataFrame 包含不同数据类型时,DataFrame.values 可能涉及复制数据并将值强制转换为一个公共的数据类型,这是一个相对昂贵的操作。DataFrame.to_numpy() 作为一个方法,更清楚地表明返回的 NumPy 数组可能不是 DataFrame 中相同数据的视图。 ## 加速操作

pandas 支持使用 numexpr 库和 bottleneck 库加速某些类型的二进制数值和布尔操作。

当处理大型数据集时,这些库特别有用,并提供了大幅加速。numexpr 使用智能分块、缓存和多核。bottleneck 是一组专门的 cython 程序,当处理具有 nans 的数组时特别快。

这里是一个示例(使用 100 列 x 100,000 行的 DataFrames):

操作 0.11.0 (毫秒) 之前版本 (毫秒) 与之前版本的比率
df1 > df2 13.32 125.35 0.1063
df1 * df2 21.71 36.63 0.5928
df1 + df2 22.04 36.50 0.6039

强烈建议安装这两个库。查看 推荐依赖项 部分获取更多安装信息。

这两者默认启用,你可以通过设置选项来控制这一点:

pd.set_option("compute.use_bottleneck", False)
pd.set_option("compute.use_numexpr", False) 
```  ## 灵活的二进制操作

在 pandas 数据结构之间进行二进制操作时,有两个关键点值得注意:

+   高维(例如 DataFrame)和低维(例如 Series)对象之间的广播行为。

+   计算中的缺失数据。

我们将演示如何独立处理这些问题,尽管它们可以同时处理。

### 匹配/广播行为

DataFrame 具有方法 `add()`、`sub()`、`mul()`、`div()` 和相关函数 `radd()`、`rsub()`,… 用于执行二进制操作。对于广播行为,Series 输入是主要关注点。使用这些函数,您可以通过 **axis** 关键字匹配 *index* 或 *columns*:

```py
In [18]: df = pd.DataFrame(
 ....:    {
 ....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 ....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 ....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 ....:    }
 ....: )
 ....: 

In [19]: df
Out[19]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [20]: row = df.iloc[1]

In [21]: column = df["two"]

In [22]: df.sub(row, axis="columns")
Out[22]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [23]: df.sub(row, axis=1)
Out[23]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [24]: df.sub(column, axis="index")
Out[24]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516

In [25]: df.sub(column, axis=0)
Out[25]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516 

此外,您可以将 MultiIndexed DataFrame 的一个级别与 Series 对齐。

In [26]: dfmi = df.copy()

In [27]: dfmi.index = pd.MultiIndex.from_tuples(
 ....:    [(1, "a"), (1, "b"), (1, "c"), (2, "a")], names=["first", "second"]
 ....: )
 ....: 

In [28]: dfmi.sub(column, axis=0, level="second")
Out[28]: 
 one       two     three
first second 
1     a      -0.377535  0.000000       NaN
 b      -1.569069  0.000000 -1.962513
 c      -0.783123  0.000000 -0.250933
2     a            NaN -1.493173 -2.385688 

Series 和 Index 还支持内置的 divmod()。此函数同时执行地板除法和取模运算��返回与左侧相同类型的两元组。例如:

In [29]: s = pd.Series(np.arange(10))

In [30]: s
Out[30]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [31]: div, rem = divmod(s, 3)

In [32]: div
Out[32]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [33]: rem
Out[33]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [34]: idx = pd.Index(np.arange(10))

In [35]: idx
Out[35]: Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [36]: div, rem = divmod(idx, 3)

In [37]: div
Out[37]: Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [38]: rem
Out[38]: Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64') 

我们还可以进行逐元素的 divmod() 操作:

In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [40]: div
Out[40]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [41]: rem
Out[41]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64 

缺失数据 / 使用填充值的操作

在 Series 和 DataFrame 中,算术函数有一个 fill_value 选项,即在某个位置的值缺失时要替换的值。例如,当添加两个 DataFrame 对象时,您可能希望将 NaN 视为 0,除非两个 DataFrame 都缺少该值,此时结果将为 NaN(如果需要,您可以稍后使用 fillna 将 NaN 替换为其他值)。

In [42]: df2 = df.copy()

In [43]: df2.loc["a", "three"] = 1.0

In [44]: df
Out[44]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [45]: df2
Out[45]: 
 one       two     three
a  1.394981  1.772517  1.000000
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [46]: df + df2
Out[46]: 
 one       two     three
a  2.789963  3.545034       NaN
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343

In [47]: df.add(df2, fill_value=0)
Out[47]: 
 one       two     three
a  2.789963  3.545034  1.000000
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343 

灵活的比较

Series 和 DataFrame 具有二进制比较方法 eqneltgtlege,其行为类似于上述二进制算术操作:

In [48]: df.gt(df2)
Out[48]: 
 one    two  three
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [49]: df2.ne(df)
Out[49]: 
 one    two  three
a  False  False   True
b  False  False  False
c  False  False  False
d   True  False  False 

这些操作产生与左侧输入相同类型的 pandas 对象,其 dtype 为 bool。这些 boolean 对象可用于索引操作,请参阅 布尔索引 部分。### 布尔缩减

您可以应用以下缩减操作:emptyany()all()bool(),以提供一种总结布尔结果的方式。

In [50]: (df > 0).all()
Out[50]: 
one      False
two       True
three    False
dtype: bool

In [51]: (df > 0).any()
Out[51]: 
one      True
two      True
three    True
dtype: bool 

您可以将结果缩减为最终布尔值。

In [52]: (df > 0).any().any()
Out[52]: True 

您可以测试 pandas 对象是否为空,通过 empty 属性。

In [53]: df.empty
Out[53]: False

In [54]: pd.DataFrame(columns=list("ABC")).empty
Out[54]: True 

警告

断言 pandas 对象的真实性会引发错误,因为空值或值的测试是模棱两可的。

In [55]: if df:
 ....:    print(True)
 ....: 
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-55-318d08b2571a> in ?()
----> 1 if df:
  2     print(True)

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 
In [56]: df and df2
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-56-b241b64bb471> in ?()
----> 1 df and df2

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 

有关更详细讨论,请参阅陷阱。 ### 比较对象是否等价

通常您可能会发现有多种计算相同结果的方法。 举个简单的例子,考虑 df + dfdf * 2。 为了测试这两个计算是否产生相同的结果,考虑使用 (df + df == df * 2).all()。 但事实上,这个表达式是 False:

In [57]: df + df == df * 2
Out[57]: 
 one   two  three
a   True  True  False
b   True  True   True
c   True  True   True
d  False  True   True

In [58]: (df + df == df * 2).all()
Out[58]: 
one      False
two       True
three    False
dtype: bool 

请注意,布尔 DataFrame df + df == df * 2 包含一些 False 值! 这是因为 NaN 不会被视为相等:

In [59]: np.nan == np.nan
Out[59]: False 

因此,NDFrames(如 Series 和 DataFrames)具有一个用于测试相等性的equals()方法,其中对应位置的 NaN 被视为相等。

In [60]: (df + df).equals(df * 2)
Out[60]: True 

请注意,为了使相等性为 True,Series 或 DataFrame 索引需要按相同顺序排列:

In [61]: df1 = pd.DataFrame({"col": ["foo", 0, np.nan]})

In [62]: df2 = pd.DataFrame({"col": [np.nan, 0, "foo"]}, index=[2, 1, 0])

In [63]: df1.equals(df2)
Out[63]: False

In [64]: df1.equals(df2.sort_index())
Out[64]: True 

比较类似数组的对象

当将 pandas 数据结构与标量值进行比较时,您可以方便地执行逐元素比较:

In [65]: pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]: 
0     True
1    False
2    False
dtype: bool

In [66]: pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False]) 

pandas 还处理相同长度的不同类似数组对象之间的逐元素比较:

In [67]: pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]: 
0     True
1     True
2    False
dtype: bool

In [68]: pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]: 
0     True
1     True
2    False
dtype: bool 

尝试比较不同长度的IndexSeries对象会引发一个 ValueError:

In [69]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[69], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

In [70]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[70], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects 

合并重叠数据集

偶尔会出现一个问题,即合并两个类似的数据集,其中一个数据集中的值优先于另一个。 一个例子是两个表示特定经济指标的数据系列,其中一个被认为是“更高质量”的。 但是,较低质量的系列可能在历史上延伸得更久,或者数据覆盖更完整。 因此,我们希望将两个 DataFrame 对象组合在一起,其中一个 DataFrame 中的缺失值会有条件地用另一个 DataFrame 中的相同标记值填充。 实现此操作的函数是combine_first(),我们进行演示:

In [71]: df1 = pd.DataFrame(
 ....:    {"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
 ....: )
 ....: 

In [72]: df2 = pd.DataFrame(
 ....:    {
 ....:        "A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
 ....:        "B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
 ....:    }
 ....: )
 ....: 

In [73]: df1
Out[73]: 
 A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [74]: df2
Out[74]: 
 A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [75]: df1.combine_first(df2)
Out[75]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 

一般的 DataFrame 组合

上面的combine_first()方法调用了更一般的DataFrame.combine()。 此方法接受另一个 DataFrame 和一个组合器函数,对齐输入 DataFrame,然后将组合器函数传递给一对 Series(即,列名称相同的列)。

因此,例如,要重现combine_first()如上所示:

In [76]: def combiner(x, y):
 ....:    return np.where(pd.isna(x), y, x)
 ....: 

In [77]: df1.combine(df2, combiner)
Out[77]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 
```  ## 描述性统计

有大量用于计算描述性统计和其他相关操作的方法,适用于 Series、DataFrame。其中大多数是聚合(因此产生较低维度的结果),如`sum()`、`mean()`和`quantile()`等,但有些方法,如`cumsum()`和`cumprod()`,会产生相同大小的对象。一般来说,这些方法接受一个**axis**参数,就像*ndarray.{sum, std, …}*一样,但是轴可以通过名称或整数指定:

+   **Series**:不需要轴参数

+   **DataFrame**: “index”(���=0,默认), “columns”(轴=1)

例如:

```py
In [78]: df
Out[78]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [79]: df.mean(0)
Out[79]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [80]: df.mean(1)
Out[80]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64 

所有这些方法都有一个skipna选项,表示是否排除缺失数据(默认为True):

In [81]: df.sum(0, skipna=False)
Out[81]: 
one           NaN
two      5.442353
three         NaN
dtype: float64

In [82]: df.sum(axis=1, skipna=True)
Out[82]: 
a    3.167498
b    2.204786
c    3.401050
d   -0.333828
dtype: float64 

结合广播/算术行为,可以非常简洁地描述各种统计程序,比如标准化(使数据均值为零,标准差为 1):

In [83]: ts_stand = (df - df.mean()) / df.std()

In [84]: ts_stand.std()
Out[84]: 
one      1.0
two      1.0
three    1.0
dtype: float64

In [85]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

In [86]: xs_stand.std(1)
Out[86]: 
a    1.0
b    1.0
c    1.0
d    1.0
dtype: float64 

请注意,像cumsum()cumprod()这样的方法会保留NaN值的位置。这与expanding()rolling()有些不同,因为NaN的行为还受min_periods参数的影响。

In [87]: df.cumsum()
Out[87]: 
 one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873 

这里是常见函数的快速参考摘要表。每个函数还可以接受一个可选的level参数,仅在对象具有分层索引时适用。

函数 描述
count 非 NA 观测数量
sum 值的总和
mean 值的均值
median 值的算术中位数
min 最小值
max 最大值
mode 众数
abs 绝对值
prod 值的乘积
std 修正的样本标准差
var 无偏方差
sem 均值的标准误差
skew 样本偏度(3 阶矩)
kurt 样本峰度(4 阶矩)
quantile 样本分位数(%处的值)
cumsum 累积和
cumprod 累积乘积
cummax 累积最大值
cummin 累积最小值

请注意,偶然情况下,一些 NumPy 方法,如 meanstdsum,默认情况下会在 Series 输入中排除 NA:

In [88]: np.mean(df["one"])
Out[88]: 0.8110935116651192

In [89]: np.mean(df["one"].to_numpy())
Out[89]: nan 

Series.nunique() 将返回 Series 中唯一非 NA 值的数量:

In [90]: series = pd.Series(np.random.randn(500))

In [91]: series[20:500] = np.nan

In [92]: series[10:20] = 5

In [93]: series.nunique()
Out[93]: 11 

数据总结:describe

有一个方便的 describe() 函数,它计算关于 Series 或 DataFrame 的列的各种摘要统计信息(当然不包括 NA):

In [94]: series = pd.Series(np.random.randn(1000))

In [95]: series[::2] = np.nan

In [96]: series.describe()
Out[96]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
25%       -0.699070
50%       -0.069718
75%        0.714483
max        3.160915
dtype: float64

In [97]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])

In [98]: frame.iloc[::2] = np.nan

In [99]: frame.describe()
Out[99]: 
 a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.033387    0.030045   -0.043719   -0.051686    0.005979
std      1.017152    0.978743    1.025270    1.015988    1.006695
min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
25%     -0.647623   -0.576449   -0.712369   -0.691338   -0.691115
50%      0.047578   -0.021499   -0.023888   -0.032652   -0.025363
75%      0.729907    0.775880    0.618896    0.670047    0.649748
max      2.740139    2.752332    3.004229    2.728702    3.240991 

您可以选择要包含在输出中的特定百��位数:

In [100]: series.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
Out[100]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
5%        -1.645423
25%       -0.699070
50%       -0.069718
75%        0.714483
95%        1.711409
max        3.160915
dtype: float64 

默认情况下,中位数始终包括在内。

对于非数字 Series 对象,describe() 将提供关于唯一值数量和最频繁出现值的简单摘要:

In [101]: s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])

In [102]: s.describe()
Out[102]: 
count     9
unique    4
top       a
freq      5
dtype: object 

请注意,在混合类型的 DataFrame 对象上,describe() 将限制摘要仅包括数值列或(如果没有)仅包括分类列:

In [103]: frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})

In [104]: frame.describe()
Out[104]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000 

可以通过提供类型列表作为 include/exclude 参数来控制此行为。也可以使用特殊值 all

In [105]: frame.describe(include=["object"])
Out[105]: 
 a
count     4
unique    2
top     Yes
freq      2

In [106]: frame.describe(include=["number"])
Out[106]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [107]: frame.describe(include="all")
Out[107]: 
 a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000 

该功能依赖于 select_dtypes。有关接受的输入的详细信息,请参考那里。 ### 最小/最大值的索引

idxmin()idxmax() 函数在 Series 和 DataFrame 上计算具有最小和最大对应值的索引标签:

In [108]: s1 = pd.Series(np.random.randn(5))

In [109]: s1
Out[109]: 
0    1.118076
1   -0.352051
2   -1.242883
3   -1.277155
4   -0.641184
dtype: float64

In [110]: s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)

In [111]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])

In [112]: df1
Out[112]: 
 A         B         C
0 -0.327863 -0.946180 -0.137570
1 -0.186235 -0.257213 -0.486567
2 -0.507027 -0.871259 -0.111110
3  2.000339 -2.430505  0.089759
4 -0.321434 -0.033695  0.096271

In [113]: df1.idxmin(axis=0)
Out[113]: 
A    2
B    3
C    1
dtype: int64

In [114]: df1.idxmax(axis=1)
Out[114]: 
0    C
1    A
2    C
3    A
4    C
dtype: object 

当有多行(或列)匹配最小或最大值时,idxmin()idxmax() 返回第一个匹配的索引:

In [115]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))

In [116]: df3
Out[116]: 
 A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [117]: df3["A"].idxmin()
Out[117]: 'd' 

注意

idxminidxmax 在 NumPy 中称为 argminargmax。 ### 值计数(直方图)/ 众数

value_counts() Series 方法计算值的 1D 数组的直方图。它也可以用作常规数组的函数:

In [118]: data = np.random.randint(0, 7, size=50)

In [119]: data
Out[119]: 
array([6, 6, 2, 3, 5, 3, 2, 5, 4, 5, 4, 3, 4, 5, 0, 2, 0, 4, 2, 0, 3, 2,
 2, 5, 6, 5, 3, 4, 6, 4, 3, 5, 6, 4, 3, 6, 2, 6, 6, 2, 3, 4, 2, 1,
 6, 2, 6, 1, 5, 4])

In [120]: s = pd.Series(data)

In [121]: s.value_counts()
Out[121]: 
6    10
2    10
4     9
3     8
5     8
0     3
1     2
Name: count, dtype: int64 

value_counts() 方法可用于计算跨多列的组合。默认情况下使用所有列,但可以使用 subset 参数选择子集。

In [122]: data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}

In [123]: frame = pd.DataFrame(data)

In [124]: frame.value_counts()
Out[124]: 
a  b
1  x    1
2  x    1
3  y    1
4  y    1
Name: count, dtype: int64 

类似地,您可以获取 Series 或 DataFrame 中值的最频繁出现的值(即众数):

In [125]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [126]: s5.mode()
Out[126]: 
0    3
1    7
dtype: int64

In [127]: df5 = pd.DataFrame(
 .....:    {
 .....:        "A": np.random.randint(0, 7, size=50),
 .....:        "B": np.random.randint(-10, 15, size=50),
 .....:    }
 .....: )
 .....: 

In [128]: df5.mode()
Out[128]: 
 A   B
0  1.0  -9
1  NaN  10
2  NaN  13 

离散化和分位数

连续值可以使用 cut()(基于值的区间)和 qcut()(基于样本分位数的区间)函数进行离散化:

In [129]: arr = np.random.randn(20)

In [130]: factor = pd.cut(arr, 4)

In [131]: factor
Out[131]: 
[(-0.251, 0.464], (-0.968, -0.251], (0.464, 1.179], (-0.251, 0.464], (-0.968, -0.251], ..., (-0.251, 0.464], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251]]
Length: 20
Categories (4, interval[float64, right]): [(-0.968, -0.251] < (-0.251, 0.464] < (0.464, 1.179] <
 (1.179, 1.893]]

In [132]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [133]: factor
Out[133]: 
[(0, 1], (-1, 0], (0, 1], (0, 1], (-1, 0], ..., (-1, 0], (-1, 0], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64, right]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]] 

qcut() 计算样本分位数。例如,我们可以将一些正态分布数据切分成相等大小的四分位数:

In [134]: arr = np.random.randn(30)

In [135]: factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])

In [136]: factor
Out[136]: 
[(0.569, 1.184], (-2.278, -0.301], (-2.278, -0.301], (0.569, 1.184], (0.569, 1.184], ..., (-0.301, 0.569], (1.184, 2.346], (1.184, 2.346], (-0.301, 0.569], (-2.278, -0.301]]
Length: 30
Categories (4, interval[float64, right]): [(-2.278, -0.301] < (-0.301, 0.569] < (0.569, 1.184] <
 (1.184, 2.346]] 

我们也可以传入无限值来定义区间:

In [137]: arr = np.random.randn(20)

In [138]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [139]: factor
Out[139]: 
[(-inf, 0.0], (0.0, inf], (0.0, inf], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (0.0, inf], (0.0, inf]]
Length: 20
Categories (2, interval[float64, right]): [(-inf, 0.0] < (0.0, inf]] 
```  ## 函数应用

要将您自己或其他库的函数应用于 pandas 对象,您应该了解以下三种方法。使用的适当方法取决于您的函数是希望在整个 `DataFrame` 或 `Series` 上操作,是按行还是按列,还是逐元素操作。

1.  表格函数应用: `pipe()`

1.  按行或列应用函数: `apply()`

1.  聚合 API: `agg()` 和 `transform()`

1.  逐元素应用函数: `map()`

### 表格函数应用

`DataFrames` 和 `Series` 可以传入函数。但是,如果函数需要在链中调用,请考虑使用 `pipe()` 方法。

首先进行一些设置:

```py
In [140]: def extract_city_name(df):
 .....: """
 .....:    Chicago, IL -> Chicago for city_name column
 .....:    """
 .....:    df["city_name"] = df["city_and_code"].str.split(",").str.get(0)
 .....:    return df
 .....: 

In [141]: def add_country_name(df, country_name=None):
 .....: """
 .....:    Chicago -> Chicago-US for city_name column
 .....:    """
 .....:    col = "city_name"
 .....:    df["city_and_country"] = df[col] + country_name
 .....:    return df
 .....: 

In [142]: df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]}) 

extract_city_nameadd_country_name 是接受并返回 DataFrames 的函数。

现在比较以下内容:

In [143]: add_country_name(extract_city_name(df_p), country_name="US")
Out[143]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

等同于:

In [144]: df_p.pipe(extract_city_name).pipe(add_country_name, country_name="US")
Out[144]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

pandas 鼓励第二种风格,即称为方法链。pipe 使得在方法链中使用您自己或其他库的函数与 pandas 方法一起变得容易。

在上面的示例中,函数 extract_city_nameadd_country_name 每个都期望一个 DataFrame 作为第一个位置参数。如果您希望应用的函数将其数据作为第二个参数呢?在这种情况下,使用一个元组 (callable, data_keyword) 提供给 pipe.pipeDataFrame 路由到元组中指定的参数。

例如,我们可以使用 statsmodels 拟合回归。他们的 API 首先期望一个公式,然后是第二个参数 dataDataFrame。我们将函数、关键字对 (sm.ols, 'data') 传递给 pipe

In [147]: import statsmodels.formula.api as sm

In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")

In [149]: (
 .....:    bb.query("h > 0")
 .....:    .assign(ln_h=lambda df: np.log(df.h))
 .....:    .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
 .....:    .fit()
 .....:    .summary()
 .....: )
 .....:
Out[149]:
<class 'statsmodels.iolib.summary.Summary'>
"""
 OLS Regression Results
==============================================================================
Dep. Variable:                     hr   R-squared:                       0.685
Model:                            OLS   Adj. R-squared:                  0.665
Method:                 Least Squares   F-statistic:                     34.28
Date:                Tue, 22 Nov 2022   Prob (F-statistic):           3.48e-15
Time:                        05:34:17   Log-Likelihood:                -205.92
No. Observations:                  68   AIC:                             421.8
Df Residuals:                      63   BIC:                             432.9
Df Model:                           4
Covariance Type:            nonrobust
===============================================================================
 coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept   -8484.7720   4664.146     -1.819      0.074   -1.78e+04     835.780
C(lg)[T.NL]    -2.2736      1.325     -1.716      0.091      -4.922       0.375
ln_h           -1.3542      0.875     -1.547      0.127      -3.103       0.395
year            4.2277      2.324      1.819      0.074      -0.417       8.872
g               0.1841      0.029      6.258      0.000       0.125       0.243
==============================================================================
Omnibus:                       10.875   Durbin-Watson:                   1.999
Prob(Omnibus):                  0.004   Jarque-Bera (JB):               17.298
Skew:                           0.537   Prob(JB):                     0.000175
Kurtosis:                       5.225   Cond. No.                     1.49e+07
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
""" 

管道方法受到 unix 管道以及更近期的dplyrmagrittr的启发,它们为R引入了流行的(%>%)(读作 pipe)运算符。这里的pipe实现非常干净,感觉就像在 Python 中很合适。我们鼓励您查看pipe()的源代码。

按行或列应用函数

可以使用apply()方法沿着 DataFrame 的轴应用任意函数,类似描述性统计方法,该方法接受一个可选的axis参数:

In [145]: df.apply(lambda x: np.mean(x))
Out[145]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [146]: df.apply(lambda x: np.mean(x), axis=1)
Out[146]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

In [147]: df.apply(lambda x: x.max() - x.min())
Out[147]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

In [148]: df.apply(np.cumsum)
Out[148]: 
 one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873

In [149]: df.apply(np.exp)
Out[149]: 
 one       two     three
a  4.034899  5.885648       NaN
b  1.409244  6.767440  0.950858
c  2.004201  4.385785  3.412466
d       NaN  1.322262  0.541630 

apply()方法还可以根据字符串方法名进行分派。

In [150]: df.apply("mean")
Out[150]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [151]: df.apply("mean", axis=1)
Out[151]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64 

传递给apply()的函数的返回类型会影响默认行为下DataFrame.apply的最终输出类型:

  • 如果应用的函数返回一个Series,最终输出是一个DataFrame。列与应用函数返回的Series的索引匹配。

  • 如果应用的函数返回任何其他类型,最终输出是一个Series

可以使用result_type来覆盖默认行为,它接受三个选项:reducebroadcastexpand。这些选项将决定类似列表的返回值如何扩展(或不扩展)到DataFrame

apply()结合一些巧妙的技巧可以用来回答关于数据集的许多问题。例如,假设我们想要提取每列中最大值出现的日期:

In [152]: tsdf = pd.DataFrame(
 .....:    np.random.randn(1000, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=1000),
 .....: )
 .....: 

In [153]: tsdf.apply(lambda x: x.idxmax())
Out[153]: 
A   2000-08-06
B   2001-01-18
C   2001-07-18
dtype: datetime64[ns] 

您还可以向apply()方法传递额外的参数和关键字参数。

In [154]: def subtract_and_divide(x, sub, divide=1):
 .....:    return (x - sub) / divide
 .....: 

In [155]: df_udf = pd.DataFrame(np.ones((2, 2)))

In [156]: df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]: 
 0         1
0 -1.333333 -1.333333
1 -1.333333 -1.333333 

另一个有用的功能是能够传递序列方法来对每一列或行执行一些序列操作:

In [157]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [158]: tsdf.iloc[3:7] = np.nan

In [159]: tsdf
Out[159]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950

In [160]: tsdf.apply(pd.Series.interpolate)
Out[160]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 -1.098598 -0.889659  0.092225
2000-01-05 -0.987349 -0.622526  0.321243
2000-01-06 -0.876100 -0.355392  0.550262
2000-01-07 -0.764851 -0.088259  0.779280
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950 

最后,apply()接受一个默认为 False 的参数raw,在应用函数之前将每行或列转换为 Series。当设置为 True 时,传递的函数将接收一个 ndarray 对象,如果您不需要索引功能,则具有积极的性能影响。

聚合 API

聚合 API 允许以一种简洁的方式表达可能的多个聚合操作。此 API 在 pandas 对象中是相似的,请参阅 groupby API、window API 和 resample API。聚合的入口是DataFrame.aggregate(),或别名DataFrame.agg()

我们将使用类似上面的起始框架:

In [161]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [162]: tsdf.iloc[3:7] = np.nan

In [163]: tsdf
Out[163]: 
 A         B         C
2000-01-01  1.257606  1.004194  0.167574
2000-01-02 -0.749892  0.288112 -0.757304
2000-01-03 -0.207550 -0.298599  0.116018
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.814347 -0.257623  0.869226
2000-01-09 -0.250663 -1.206601  0.896839
2000-01-10  2.169758 -1.333363  0.283157 

使用单个函数等同于apply()。您还可以将命名方法作为字符串传递。这些将返回聚合输出的Series

In [164]: tsdf.agg(lambda x: np.sum(x))
Out[164]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

In [165]: tsdf.agg("sum")
Out[165]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

# these are equivalent to a ``.sum()`` because we are aggregating
# on a single function
In [166]: tsdf.sum()
Out[166]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64 

Series进行单个聚合将返回一个标量值:

In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146 

使用多个函数进行聚合

您可以将多个聚合参数作为列表传递。每个传递函数的结果将成为结果DataFrame中的一行。这些自然地从聚合函数命名。

In [168]: tsdf.agg(["sum"])
Out[168]: 
 A         B        C
sum  3.033606 -1.803879  1.57551 

多个函数产生多个行:

In [169]: tsdf.agg(["sum", "mean"])
Out[169]: 
 A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585 

Series上,多个函数返回一个Series,由函数名称索引:

In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64 

传递一个lambda函数将产生一个<lambda>命名的行:

In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64 

传递命名函数将为该行产生该名称:

In [172]: def mymean(x):
 .....:    return x.mean()
 .....: 

In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64 

使用字典进行聚合

将列名的字典传递给标量或标量列表,以便在DataFrame.agg中自定义应用哪些函数于哪些列。请注意,结果不按任何特定顺序排列,您可以使用OrderedDict来保证顺序。

In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64 

传递类似列表的内容将生成一个DataFrame输出。您将获得所有聚合器的类似矩阵的输出。输出将包含所有唯一的函数。那些没有针对特定列注明的函数将为NaN

In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]: 
 A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879 

自定义描述

使用.agg()可以轻松创建一个类似于内置 describe 函数的自定义描述函数。

In [176]: from functools import partial

In [177]: q_25 = partial(pd.Series.quantile, q=0.25)

In [178]: q_25.__name__ = "25%"

In [179]: q_75 = partial(pd.Series.quantile, q=0.75)

In [180]: q_75.__name__ = "75%"

In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]: 
 A         B         C
count   6.000000  6.000000  6.000000
mean    0.505601 -0.300647  0.262585
std     1.103362  0.887508  0.606860
min    -0.749892 -1.333363 -0.757304
25%    -0.239885 -0.979600  0.128907
median  0.303398 -0.278111  0.225365
75%     1.146791  0.151678  0.722709
max     2.169758  1.004194  0.896839 
```  ### 转换 API

`transform()`方法返回一个与原始相同(相同大小)的对象。此 API 允许您一次提供*多个*操作,而不是逐个操作。其 API 与`.agg` API 非常相似。

我们创建一个类似于上述部分使用的框架。

```py
In [182]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [183]: tsdf.iloc[3:7] = np.nan

In [184]: tsdf
Out[184]: 
 A         B         C
2000-01-01 -0.428759 -0.864890 -0.675341
2000-01-02 -0.168731  1.338144 -1.279321
2000-01-03 -1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -1.240447 -0.201052
2000-01-09 -0.157795  0.791197 -1.144209
2000-01-10 -0.030876  0.371900  0.061932 

转换整个框架。.transform()允许输入函数为:NumPy 函数、字符串函数名称或用户定义的函数。

In [185]: tsdf.transform(np.abs)
Out[185]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [186]: tsdf.transform("abs")
Out[186]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [187]: tsdf.transform(lambda x: x.abs())
Out[187]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

这里transform()接收一个单个函数;这等同于ufunc应用。

In [188]: np.abs(tsdf)
Out[188]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

通过将单个函数传递给.transform()Series,将返回一个单个Series

In [189]: tsdf["A"].transform(np.abs)
Out[189]: 
2000-01-01    0.428759
2000-01-02    0.168731
2000-01-03    1.621034
2000-01-04         NaN
2000-01-05         NaN
2000-01-06         NaN
2000-01-07         NaN
2000-01-08    0.254374
2000-01-09    0.157795
2000-01-10    0.030876
Freq: D, Name: A, dtype: float64 

使用多个函数进行转换

传递多个函数将产生一个列多级索引的 DataFrame。第一级将是原始框架列名称;第二级将是转换函数的名称。

In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]: 
 A                   B                   C 
 absolute  <lambda>  absolute  <lambda>  absolute  <lambda>
2000-01-01  0.428759  0.571241  0.864890  0.135110  0.675341  0.324659
2000-01-02  0.168731  0.831269  1.338144  2.338144  1.279321 -0.279321
2000-01-03  1.621034 -0.621034  0.438107  1.438107  0.903794  1.903794
2000-01-04       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-08  0.254374  1.254374  1.240447 -0.240447  0.201052  0.798948
2000-01-09  0.157795  0.842205  0.791197  1.791197  1.144209 -0.144209
2000-01-10  0.030876  0.969124  0.371900  1.371900  0.061932  1.061932 

将多个函数传递给 Series 将产生一个 DataFrame。生成的列名将是转换函数。

In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]: 
 absolute  <lambda>
2000-01-01  0.428759  0.571241
2000-01-02  0.168731  0.831269
2000-01-03  1.621034 -0.621034
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374  1.254374
2000-01-09  0.157795  0.842205
2000-01-10  0.030876  0.969124 

使用字典进行转换

通过传递函数字典将允许每列进行选择性转换。

In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]: 
 A         B
2000-01-01  0.428759  0.135110
2000-01-02  0.168731  2.338144
2000-01-03  1.621034  1.438107
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374 -0.240447
2000-01-09  0.157795  1.791197
2000-01-10  0.030876  1.371900 

通过传递列表字典将生成具有这些选择性转换的多级索引 DataFrame。

In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]: 
 A         B 
 absolute  <lambda>      sqrt
2000-01-01  0.428759  0.135110       NaN
2000-01-02  0.168731  2.338144  1.156782
2000-01-03  1.621034  1.438107  0.661897
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -0.240447       NaN
2000-01-09  0.157795  1.791197  0.889493
2000-01-10  0.030876  1.371900  0.609836 
```  ### 应用逐元素函数

由于并非所有函数都可以矢量化(接受 NumPy 数组并返回另一个数组或值),因此 DataFrame 上的方法`map()` 和类似地 Series 上的`map()` 接受任何接受单个值并返回单个值的 Python 函数。例如:

```py
In [194]: df4 = df.copy()

In [195]: df4
Out[195]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [196]: def f(x):
 .....:    return len(str(x))
 .....: 

In [197]: df4["one"].map(f)
Out[197]: 
a    18
b    19
c    18
d     3
Name: one, dtype: int64

In [198]: df4.map(f)
Out[198]: 
 one  two  three
a   18   17      3
b   19   18     20
c   18   18     16
d    3   19     19 

Series.map() 还具有一个额外的功能;它可以用于轻松“链接”或“映射”由辅助系列定义的值。这与合并/连接功能密切相关:

In [199]: s = pd.Series(
 .....:    ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
 .....: )
 .....: 

In [200]: t = pd.Series({"six": 6.0, "seven": 7.0})

In [201]: s
Out[201]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

In [202]: s.map(t)
Out[202]: 
a    6.0
b    7.0
c    6.0
d    7.0
e    6.0
dtype: float64 
```  ## 重新索引和更改标签

`reindex()` 是 pandas 中的基本数据对齐方法。它用于实现几乎所有依赖于标签对齐功能的其他功能。重新索引意味着使数据符合匹配特定轴上给定标签集的数据。这实现了几件事情:

+   重新排序现有数据以匹配新的标签集

+   在标签位置插入缺失值(NA),在该标签处没有数据时

+   如果指定,使用逻辑填充缺失标签的数据(与处理时间序列数据密切相关)

这里是一个简单的例子:

```py
In [203]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [204]: s
Out[204]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
e   -1.326508
dtype: float64

In [205]: s.reindex(["e", "b", "f", "d"])
Out[205]: 
e   -1.326508
b    1.328614
f         NaN
d   -0.385845
dtype: float64 

在这里,f 标签未包含在 Series 中,因此在结果中显示为NaN

使用 DataFrame,您可以同时重新索引索引和列:

In [206]: df
Out[206]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [207]: df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
Out[207]: 
 three       two       one
c  1.227435  1.478369  0.695246
f       NaN       NaN       NaN
b -0.050390  1.912123  0.343054 

请注意,包含实际轴标签的Index对象可以在对象之间共享。因此,如果我们有一个 Series 和一个 DataFrame,则可以执行以下操作:

In [208]: rs = s.reindex(df.index)

In [209]: rs
Out[209]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
dtype: float64

In [210]: rs.index is df.index
Out[210]: True 

这意味着重新索引的 Series 的索引与 DataFrame 的索引是相同的 Python 对象。

DataFrame.reindex() 还支持“轴样式”调用约定,其中您指定单个labels参数和它适用的axis

In [211]: df.reindex(["c", "f", "b"], axis="index")
Out[211]: 
 one       two     three
c  0.695246  1.478369  1.227435
f       NaN       NaN       NaN
b  0.343054  1.912123 -0.050390

In [212]: df.reindex(["three", "two", "one"], axis="columns")
Out[212]: 
 three       two       one
a       NaN  1.772517  1.394981
b -0.050390  1.912123  0.343054
c  1.227435  1.478369  0.695246
d -0.613172  0.279344       NaN 

另请参阅

MultiIndex / 高级索引 是进行重新索引的更简洁方式。

注意

在编写对性能敏感的代码时,有充分理由花一些时间成为重新索引的高手:许多操作在预对齐数据上更快。添加两个未对齐的 DataFrame 内部触发重新索引步骤。对于探索性分析,你几乎不会注意到差异(因为reindex已经经过了大量优化),但是当 CPU 周期很重要时,偶尔在某些地方添加一些显式的reindex调用可能会产生影响。

重新索引以与另一个对象对齐

你可能希望取一个对象并重新索引其轴,使其标签与另一个对象相同。虽然这个语法很简单但冗长,但这是一个常见的操作,因此提供了reindex_like()方法来简化这个操作:

In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])

In [214]: df3 = df2 - df2.mean()

In [215]: df2
Out[215]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

In [216]: df3
Out[216]: 
 one       two
a  0.583888  0.051514
b -0.468040  0.191120
c -0.115848 -0.242634

In [217]: df.reindex_like(df2)
Out[217]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369 
```  ### 用`align`将对象与其他对象对齐

`align()`方法是同时对齐两个对象的最快方法。它支持一个`join`参数(与连接和合并相关):

> +   `join='outer'`:取索引的并集(默认)
> +   
> +   `join='left'`:使用调用对象的索引
> +   
> +   `join='right'`:使用传递对象的索引
> +   
> +   `join='inner'`:交集索引

它返回一个包含两个重新索引 Series 的元组:

```py
In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [219]: s1 = s[:4]

In [220]: s2 = s[1:]

In [221]: s1.align(s2)
Out[221]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e         NaN
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e    1.114285
 dtype: float64)

In [222]: s1.align(s2, join="inner")
Out[222]: 
(b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64)

In [223]: s1.align(s2, join="left")
Out[223]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64) 

对于 DataFrame,默认情况下,join 方法将应用于索引和列:

In [224]: df.align(df2, join="inner")
Out[224]: 
(        one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

你还可以传递一个axis选项,只在指定的轴上对齐:

In [225]: df.align(df2, join="inner", axis=0)
Out[225]: 
(        one       two     three
 a  1.394981  1.772517       NaN
 b  0.343054  1.912123 -0.050390
 c  0.695246  1.478369  1.227435,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

如果你将一个 Series 传递给DataFrame.align(),你可以选择使用axis参数在 DataFrame 的索引或列上同时对齐两个对象:

In [226]: df.align(df2.iloc[0], axis=1)
Out[226]: 
(        one     three       two
 a  1.394981       NaN  1.772517
 b  0.343054 -0.050390  1.912123
 c  0.695246  1.227435  1.478369
 d       NaN -0.613172  0.279344,
 one      1.394981
 three         NaN
 two      1.772517
 Name: a, dtype: float64) 
```  ### 重新索引时填充

`reindex()`接受一个可选参数`method`,这是从以下表中选择的填充方法:

| 方法 | 动作 |
| --- | --- |
| pad / ffill | 向前填充值 |
| bfill / backfill | 向后填充值 |
| nearest | 从最近的索引值填充 |

我们在一个简单的 Series 上演示这些填充方法:

```py
In [227]: rng = pd.date_range("1/3/2000", periods=8)

In [228]: ts = pd.Series(np.random.randn(8), index=rng)

In [229]: ts2 = ts.iloc[[0, 3, 6]]

In [230]: ts
Out[230]: 
2000-01-03    0.183051
2000-01-04    0.400528
2000-01-05   -0.015083
2000-01-06    2.395489
2000-01-07    1.414806
2000-01-08    0.118428
2000-01-09    0.733639
2000-01-10   -0.936077
Freq: D, dtype: float64

In [231]: ts2
Out[231]: 
2000-01-03    0.183051
2000-01-06    2.395489
2000-01-09    0.733639
Freq: 3D, dtype: float64

In [232]: ts2.reindex(ts.index)
Out[232]: 
2000-01-03    0.183051
2000-01-04         NaN
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07         NaN
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]: 
2000-01-03    0.183051
2000-01-04    2.395489
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    0.733639
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

这些方法要求索引是有序递增或递减的。

请注意,可以使用 ffill(除了method='nearest')或 interpolate 来实现相同的结果:

In [236]: ts2.reindex(ts.index).ffill()
Out[236]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

reindex()如果索引不是单调递增或递减,将引发 ValueError。fillna()interpolate()不会对索引的顺序执行任何检查。### 重新索引时填充的限制

limittolerance参数在重新索引时提供额外的填充控制。Limit 指定连续匹配的最大计数:

In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

相反,容差指定索引和索引器值之间的最大距离:

In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

请注意,当在DatetimeIndexTimedeltaIndexPeriodIndex上使用时,如果可能,tolerance将被强制转换为Timedelta。这允许您使用适当的字符串指定容差。### 从轴中删除标签

reindex密切相关的方法是drop()函数。它从轴中删除一组标签:

In [239]: df
Out[239]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [240]: df.drop(["a", "d"], axis=0)
Out[240]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435

In [241]: df.drop(["one"], axis=1)
Out[241]: 
 two     three
a  1.772517       NaN
b  1.912123 -0.050390
c  1.478369  1.227435
d  0.279344 -0.613172 

请注意,以下方法也有效,但不太明显/干净:

In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435 
```### 重命名/映射标签

`rename()`方法允许您根据某些映射(字典或 Series)或任意函数重新标记轴。

```py
In [243]: s
Out[243]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
dtype: float64

In [244]: s.rename(str.upper)
Out[244]: 
A   -0.186646
B   -1.692424
C   -0.303893
D   -1.425662
E    1.114285
dtype: float64 

如果传递一个函数,当使用任何标签调用时,它必须返回一个值(并且必须生成一组唯一值)。也可以使用字典或 Series:

In [245]: df.rename(
 .....:    columns={"one": "foo", "two": "bar"},
 .....:    index={"a": "apple", "b": "banana", "d": "durian"},
 .....: )
 .....: 
Out[245]: 
 foo       bar     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

如果映射不包括列/索引标签,则不会重命名。请注意,映射中的额外标签不会引发错误。

DataFrame.rename()还支持“轴样式”调用约定,您可以指定单个mapper和要将该映射应用于的axis

In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]: 
 foo       bar     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]: 
 one       two     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

最后,rename()还接受标量或类似列表的值来修改Series.name属性。

In [248]: s.rename("scalar-name")
Out[248]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
Name: scalar-name, dtype: float64 

方法DataFrame.rename_axis()Series.rename_axis()允许更改MultiIndex的特定名称(而不是标签)。

In [249]: df = pd.DataFrame(
 .....:    {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
 .....:    index=pd.MultiIndex.from_product(
 .....:        [["a", "b", "c"], [1, 2]], names=["let", "num"]
 .....:    ),
 .....: )
 .....: 

In [250]: df
Out[250]: 
 x   y
let num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [251]: df.rename_axis(index={"let": "abc"})
Out[251]: 
 x   y
abc num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [252]: df.rename_axis(index=str.upper)
Out[252]: 
 x   y
LET NUM 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60 
```## 迭代

pandas 对象的基本迭代行为取决于类型。在 Series 上进行迭代时,它被视为类似数组,基本迭代产生值。DataFrame 遵循字典样式的约定,迭代对象的“键”。

简而言之,基本迭代(`for i in object`)产生:

+   **Series**: 值

+   **DataFrame**: 列标签

因此,例如,迭代 DataFrame 会给出列名:

```py
In [253]: df = pd.DataFrame(
 .....:    {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
 .....: )
 .....: 

In [254]: for col in df:
 .....:    print(col)
 .....: 
col1
col2 

pandas 对象还具有类似字典的items()方法,用于迭代(key, value)对。

要迭代 DataFrame 的行,您可以使用以下方法:

  • iterrows():以(index, Series)对的形式迭代 DataFrame 的行。这将行转换为 Series 对象,可以更改数据类型并具有一些性能影响。

  • itertuples():以值的 namedtuples 形式迭代 DataFrame 的行。这比iterrows()快得多,并且在大多数情况下更适合用于迭代 DataFrame 的值。

警告

通过 pandas 对象进行迭代通常较慢。在许多情况下,手动迭代行是不必要的,并且可以通过以下方法之一避免:

  • 寻找矢量化解决方案:许多操作可以使用内置方法或 NumPy 函数(布尔)索引等进行,…

  • 当您有一个无法一次处理完整 DataFrame/Series 的函数时,最好使用apply()而不是迭代值。请参阅函数应用部分的文档。

  • 如果需要对值进行迭代操作但性能很重要,请考虑使用 cython 或 numba 编写内部循环。请参阅提高性能部分,了解一些此方法的示例。

警告

您绝对不应该修改正在迭代的内容。这并不保证在所有情况下都有效。根据数据类型,迭代器返回的是副本而不是视图,对其进行写入将不起作用!

例如,在以下情况中设置值没有效果:

In [255]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})

In [256]: for index, row in df.iterrows():
 .....:    row["a"] = 10
 .....: 

In [257]: df
Out[257]: 
 a  b
0  1  a
1  2  b
2  3  c 

items

与类似字典的接口一致,items()迭代键值对:

  • Series:(索引, 标量值)对

  • DataFrame:(列, Series)对

例如:

In [258]: for label, ser in df.items():
 .....:    print(label)
 .....:    print(ser)
 .....: 
a
0    1
1    2
2    3
Name: a, dtype: int64
b
0    a
1    b
2    c
Name: b, dtype: object 

iterrows

iterrows()允许您以 Series 对象的形式迭代 DataFrame 的行。它返回一个迭代器,产生每个索引值以及包含每行数据的 Series:

In [259]: for row_index, row in df.iterrows():
 .....:    print(row_index, row, sep="\n")
 .....: 
0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object 

注意

因为iterrows()为每行返回一个 Series,它不会在行之间保留数据类型(对于数据框的列,数据类型是保留的)。例如,

In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])

In [261]: df_orig.dtypes
Out[261]: 
int        int64
float    float64
dtype: object

In [262]: row = next(df_orig.iterrows())[1]

In [263]: row
Out[263]: 
int      1.0
float    1.5
Name: 0, dtype: float64 

row中的所有值,作为一个 Series 返回,现在都被转换为浮点数,包括列x中的原始整数值:

In [264]: row["int"].dtype
Out[264]: dtype('float64')

In [265]: df_orig["int"].dtype
Out[265]: dtype('int64') 

在迭代行时保留数据类型,最好使用itertuples(),它返回值的命名元组,通常比iterrows()快得多。

例如,转置数据框的一种构造性方法可能是:

In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})

In [267]: print(df2)
 x  y
0  1  4
1  2  5
2  3  6

In [268]: print(df2.T)
 0  1  2
x  1  2  3
y  4  5  6

In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})

In [270]: print(df2_t)
 0  1  2
x  1  2  3
y  4  5  6 

itertuples

itertuples() 方法将返回一个迭代器,为数据框中的每一行产生一个命名元组。元组的第一个元素将是行对应的索引值,而其余值是行的数值。

例如:

In [271]: for row in df.itertuples():
 .....:    print(row)
 .....: 
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c') 

此方法不会将行转换为 Series 对象;它只是返回命名元组中的值。因此,itertuples() 保留值的数据类型,并且通常比iterrows()更快。

注意

如果列名无效的 Python 标识符、重复或以下划线开头,则列名将重命名为位置名称。如果列数较多(>255),则返回常规元组。## .dt 访问器

Series有一个访问器,可以简洁地返回类似日期时间的属性,如果它是一个日期时间/周期类似的 Series。这将返回一个 Series,索引类似于现有的 Series。

# datetime
In [272]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))

In [273]: s
Out[273]: 
0   2013-01-01 09:10:12
1   2013-01-02 09:10:12
2   2013-01-03 09:10:12
3   2013-01-04 09:10:12
dtype: datetime64[ns]

In [274]: s.dt.hour
Out[274]: 
0    9
1    9
2    9
3    9
dtype: int32

In [275]: s.dt.second
Out[275]: 
0    12
1    12
2    12
3    12
dtype: int32

In [276]: s.dt.day
Out[276]: 
0    1
1    2
2    3
3    4
dtype: int32 

这使得可以使用如下的表达式:

In [277]: s[s.dt.day == 2]
Out[277]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns] 

您可以轻松地生成带有时区信息的转换:

In [278]: stz = s.dt.tz_localize("US/Eastern")

In [279]: stz
Out[279]: 
0   2013-01-01 09:10:12-05:00
1   2013-01-02 09:10:12-05:00
2   2013-01-03 09:10:12-05:00
3   2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]

In [280]: stz.dt.tz
Out[280]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD> 

您还可以链接这些类型的操作:

In [281]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]: 
0   2013-01-01 04:10:12-05:00
1   2013-01-02 04:10:12-05:00
2   2013-01-03 04:10:12-05:00
3   2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern] 

您还可以使用Series.dt.strftime()将日期时间值格式化为字符串,支持与标准strftime()相同的格式。

# DatetimeIndex
In [282]: s = pd.Series(pd.date_range("20130101", periods=4))

In [283]: s
Out[283]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: datetime64[ns]

In [284]: s.dt.strftime("%Y/%m/%d")
Out[284]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object 
# PeriodIndex
In [285]: s = pd.Series(pd.period_range("20130101", periods=4))

In [286]: s
Out[286]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [287]: s.dt.strftime("%Y/%m/%d")
Out[287]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object 

.dt 访问器适用于周期和时间增量数据类型。

# period
In [288]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))

In [289]: s
Out[289]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [290]: s.dt.year
Out[290]: 
0    2013
1    2013
2    2013
3    2013
dtype: int64

In [291]: s.dt.day
Out[291]: 
0    1
1    2
2    3
3    4
dtype: int64 
# timedelta
In [292]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))

In [293]: s
Out[293]: 
0   1 days 00:00:05
1   1 days 00:00:06
2   1 days 00:00:07
3   1 days 00:00:08
dtype: timedelta64[ns]

In [294]: s.dt.days
Out[294]: 
0    1
1    1
2    1
3    1
dtype: int64

In [295]: s.dt.seconds
Out[295]: 
0    5
1    6
2    7
3    8
dtype: int32

In [296]: s.dt.components
Out[296]: 
 days  hours  minutes  seconds  milliseconds  microseconds  nanoseconds
0     1      0        0        5             0             0            0
1     1      0        0        6             0             0            0
2     1      0        0        7             0             0            0
3     1      0        0        8             0             0            0 

注意

如果使用非日期时间类似的值访问,Series.dt将引发TypeError

向量化字符串方法

Series 配备了一组字符串处理方法,使得在数组的每个元素上操作变得容易。最重要的是,这些方法会自动排除缺失/NA 值。这些方法通过 Series 的str属性访问,通常与等效的(标量)内置字符串方法名称匹配。例如:

In [297]: s = pd.Series(
 .....:    ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
 .....: )
 .....: 

In [298]: s.str.lower()
Out[298]: 
0       a
1       b
2       c
3    aaba
4    baca
5    <NA>
6    caba
7     dog
8     cat
dtype: string 

还提供了强大的模式匹配方法,但请注意,模式匹配通常默认使用正则表达式(在某些情况下总是使用)。

注意

在 pandas 1.0 之前,字符串方法仅适用于object类型的Series。pandas 1.0 添加了StringDtype,专门用于字符串。更多信息请参见文本数据类型。

请参见矢量化字符串方法获取完整描述。

排序

pandas 支持三种排序方式:按索引标签排序、按列值排序以及按两者的组合排序。

按索引排序

Series.sort_index()DataFrame.sort_index()方法用于按其索引级别对 pandas 对象进行排序。

In [299]: df = pd.DataFrame(
 .....:    {
 .....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 .....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 .....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 .....:    }
 .....: )
 .....: 

In [300]: unsorted_df = df.reindex(
 .....:    index=["a", "d", "c", "b"], columns=["three", "two", "one"]
 .....: )
 .....: 

In [301]: unsorted_df
Out[301]: 
 three       two       one
a       NaN -1.152244  0.562973
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504

# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]: 
 three       two       one
a       NaN -1.152244  0.562973
b -0.098217  0.009797 -1.299504
c  1.273388 -0.167123  0.640382
d -0.252916 -0.109597       NaN

In [303]: unsorted_df.sort_index(ascending=False)
Out[303]: 
 three       two       one
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504
a       NaN -1.152244  0.562973

In [304]: unsorted_df.sort_index(axis=1)
Out[304]: 
 one     three       two
a  0.562973       NaN -1.152244
d       NaN -0.252916 -0.109597
c  0.640382  1.273388 -0.167123
b -1.299504 -0.098217  0.009797

# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]: 
a         NaN
b   -0.098217
c    1.273388
d   -0.252916
Name: three, dtype: float64 

按索引排序还支持一个key参数,该参数接受一个可调用函数,用于对要排序的索引应用。对于MultiIndex对象,该键会按照指定的level逐级应用到各级别。

In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
 .....:    list("ab")
 .....: )
 .....: 

In [307]: s1
Out[307]: 
 c
a b 
B 1  2
a 2  3
C 3  4 
In [308]: s1.sort_index(level="a")
Out[308]: 
 c
a b 
B 1  2
C 3  4
a 2  3

In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]: 
 c
a b 
a 2  3
B 1  2
C 3  4 

有关按值排序的键排序信息,请参见值排序。### 按值排序

Series.sort_values()方法用于按值对Series进行排序。DataFrame.sort_values()方法用于按其列或行值对DataFrame进行排序。可选的by参数可用于指定一个或多个列以确定排序顺序。

In [310]: df1 = pd.DataFrame(
 .....:    {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
 .....: )
 .....: 

In [311]: df1.sort_values(by="two")
Out[311]: 
 one  two  three
0    2    1      5
2    1    2      3
1    1    3      4
3    1    4      2 

by参数可以接受一个列名列表,例如:

In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]: 
 one  two  three
2    1    2      3
1    1    3      4
3    1    4      2
0    2    1      5 

这些方法通过na_position参数对 NA 值进行特殊处理:

In [313]: s[2] = np.nan

In [314]: s.sort_values()
Out[314]: 
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
2    <NA>
5    <NA>
dtype: string

In [315]: s.sort_values(na_position="first")
Out[315]: 
2    <NA>
5    <NA>
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
dtype: string 

排序还支持一个key参数,该参数接受一个可调用函数,用于对要排序的值应用。

In [316]: s1 = pd.Series(["B", "a", "C"]) 
In [317]: s1.sort_values()
Out[317]: 
0    B
2    C
1    a
dtype: object

In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]: 
1    a
0    B
2    C
dtype: object 

key将获得值的Series,并应返回具有相同形状的转换值的Series或数组。对于DataFrame对象,键会按列应用,因此键仍应期望一个 Series 并返回一个 Series,例如:

In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]}) 
In [320]: df.sort_values(by="a")
Out[320]: 
 a  b
0  B  1
2  C  3
1  a  2

In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]: 
 a  b
1  a  2
0  B  1
2  C  3 

每列的名称或类型可用于对不同列应用不同的函数。 ### 通过索引和值

传递给DataFrame.sort_values()by参数可能是列名或索引级别名称。

# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
 .....:    [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
 .....: )
 .....: 

In [323]: idx.names = ["first", "second"]

# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)

In [325]: df_multi
Out[325]: 
 A
first second 
a     1       6
 2       5
 2       4
b     2       3
 1       2
 1       1 

按‘second’(索引)和‘A’(列)排序

In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]: 
 A
first second 
b     1       1
 1       2
a     1       6
b     2       3
a     2       4
 2       5 

注意

如果一个字符串既匹配列名又匹配索引级别名称,则会发出警告,并且列优先。这将导致在将来版本中出现歧义错误。 ### searchsorted

Series 具有searchsorted()方法,其工作方式类似于numpy.ndarray.searchsorted()

In [327]: ser = pd.Series([1, 2, 3])

In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])

In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])

In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])

In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])

In [332]: ser = pd.Series([3, 1, 2])

In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2]) 
```  ### 最小/最大值

`Series`具有`nsmallest()`和`nlargest()`方法,它们返回最小或最大的\(n\)值。对于大型`Series`,这比对整个 Series 进行排序并在结果上调用`head(n)`要快得多。

```py
In [334]: s = pd.Series(np.random.permutation(10))

In [335]: s
Out[335]: 
0    2
1    0
2    3
3    7
4    1
5    5
6    9
7    6
8    8
9    4
dtype: int64

In [336]: s.sort_values()
Out[336]: 
1    0
4    1
0    2
2    3
9    4
5    5
7    6
3    7
8    8
6    9
dtype: int64

In [337]: s.nsmallest(3)
Out[337]: 
1    0
4    1
0    2
dtype: int64

In [338]: s.nlargest(3)
Out[338]: 
6    9
8    8
3    7
dtype: int64 

DataFrame还具有nlargestnsmallest方法。

In [339]: df = pd.DataFrame(
 .....:    {
 .....:        "a": [-2, -1, 1, 10, 8, 11, -1],
 .....:        "b": list("abdceff"),
 .....:        "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
 .....:    }
 .....: )
 .....: 

In [340]: df.nlargest(3, "a")
Out[340]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [341]: df.nlargest(5, ["a", "c"])
Out[341]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN
2   1  d  4.0
6  -1  f  4.0

In [342]: df.nsmallest(3, "a")
Out[342]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0

In [343]: df.nsmallest(5, ["a", "c"])
Out[343]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
2  1  d  4.0
4  8  e  NaN 
```  ### 按 MultiIndex 列排序

当列是 MultiIndex 时,必须明确排序,并完全指定所有级别为`by`。

```py
In [344]: df1.columns = pd.MultiIndex.from_tuples(
 .....:    [("a", "one"), ("a", "two"), ("b", "three")]
 .....: )
 .....: 

In [345]: df1.sort_values(by=("a", "two"))
Out[345]: 
 a         b
 one two three
0   2   1     5
2   1   2     3
1   1   3     4
3   1   4     2 

复制

pandas 对象上的copy()方法会复制底层数据(尽管不会复制轴索引,因为它们是不可变的)并返回一个新对象。请注意几乎不需要复制对象。例如,只有少数几种方法可以原地更改 DataFrame:

  • 插入、删除或修改列。

  • 分配给indexcolumns属性。

  • 对于同质数据,可以通过values属性或高级索引直接修改值。

明确指出,没有 pandas 方法会具有修改数据的副作用;几乎每个方法都会返回一个新对象,保持原始对象不变。如果数据被修改,那是因为您明确这样做了。

dtypes

在大多数情况下,pandas 使用 NumPy 数组和 dtype 来处理 Series 或 DataFrame 的单个列。NumPy 支持floatintbooltimedelta64[ns]datetime64[ns](请注意,NumPy 不支持时区感知的日期时间)。

pandas 和第三方库在一些地方扩展了 NumPy 的类型系统。本节描述了 pandas 内部所做的扩展。查看 扩展类型 了解如何编写适用于 pandas 的自定义扩展。查看 生态系统页面 查看已实现扩展的第三方库列表。

以下表格列出了所有 pandas 的扩展类型。对于需要 dtype 参数的方法,可以按照指示指定字符串。查看各自的文档部分了解更多关于每种类型的信息。

数据类型 数据类型 标量 数组 字符串别名
时区感知日期时间 DatetimeTZDtype Timestamp arrays.DatetimeArray 'datetime64[ns, <tz>]'
分类 CategoricalDtype (无) Categorical 'category'
周期(时间跨度) PeriodDtype Period arrays.PeriodArray 'Period[<freq>]' 'period[<freq>]',
稀疏 SparseDtype (无) arrays.SparseArray 'Sparse', 'Sparse[int]', 'Sparse[float]'
区间 IntervalDtype Interval arrays.IntervalArray 'interval', 'Interval', 'Interval[<numpy_dtype>]', 'Interval[datetime64[ns, <tz>]]', 'Interval[timedelta64[<freq>]]'
可空整数 Int64Dtype, … (无) arrays.IntegerArray 'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64'
可空浮点数 Float64Dtype, … (无) arrays.FloatingArray 'Float32', 'Float64'
字符串 StringDtype str arrays.StringArray 'string'
布尔值(带 NA) BooleanDtype bool arrays.BooleanArray 'boolean'

pandas 有两种存储字符串的方式。

  1. object 数据类型,可以保存任何 Python 对象,包括字符串。

  2. StringDtype,专门用于字符串。

通常,我们建议使用StringDtype。更多信息请参见文本数据类型。

最后,任意对象可以使用object数据类型存储,但应尽可能避免(出于性能和与其他库和方法的互操作性考虑。请参见对象转换)。

一个方便的dtypes属性用于返回 DataFrame 的每列的数据类型的 Series。

In [346]: dft = pd.DataFrame(
 .....:    {
 .....:        "A": np.random.rand(3),
 .....:        "B": 1,
 .....:        "C": "foo",
 .....:        "D": pd.Timestamp("20010102"),
 .....:        "E": pd.Series([1.0] * 3).astype("float32"),
 .....:        "F": False,
 .....:        "G": pd.Series([1] * 3, dtype="int8"),
 .....:    }
 .....: )
 .....: 

In [347]: dft
Out[347]: 
 A  B    C          D    E      F  G
0  0.035962  1  foo 2001-01-02  1.0  False  1
1  0.701379  1  foo 2001-01-02  1.0  False  1
2  0.281885  1  foo 2001-01-02  1.0  False  1

In [348]: dft.dtypes
Out[348]: 
A          float64
B            int64
C           object
D    datetime64[s]
E          float32
F             bool
G             int8
dtype: object 

Series对象上,使用dtype属性。

In [349]: dft["A"].dtype
Out[349]: dtype('float64') 

如果 pandas 对象包含具有多种数据类型在单个列中的数据,则列的数据类型将被选择以容纳所有数据类型(object是最通用的)。

# these ints are coerced to floats
In [350]: pd.Series([1, 2, 3, 4, 5, 6.0])
Out[350]: 
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    6.0
dtype: float64

# string data forces an ``object`` dtype
In [351]: pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]: 
0      1
1      2
2      3
3    6.0
4    foo
dtype: object 

每种类型的DataFrame的列数可以通过调用DataFrame.dtypes.value_counts()来找到。

In [352]: dft.dtypes.value_counts()
Out[352]: 
float64          1
int64            1
object           1
datetime64[s]    1
float32          1
bool             1
int8             1
Name: count, dtype: int64 

数值数据类型将传播并可以共存于数据框中。如果传递了数据类型(可以直接通过dtype关键字、传递的ndarray或传递的Series),那么它将在数据框操作中保留。此外,不同的数值数据类型将不会被合并。以下示例将让你一窥其中。

In [353]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")

In [354]: df1
Out[354]: 
 A
0  0.224364
1  1.890546
2  0.182879
3  0.787847
4 -0.188449
5  0.667715
6 -0.011736
7 -0.399073

In [355]: df1.dtypes
Out[355]: 
A    float32
dtype: object

In [356]: df2 = pd.DataFrame(
 .....:    {
 .....:        "A": pd.Series(np.random.randn(8), dtype="float16"),
 .....:        "B": pd.Series(np.random.randn(8)),
 .....:        "C": pd.Series(np.random.randint(0, 255, size=8), dtype="uint8"),  # [0,255] (range of uint8)
 .....:    }
 .....: )
 .....: 

In [357]: df2
Out[357]: 
 A         B    C
0  0.823242  0.256090   26
1  1.607422  1.426469   86
2 -0.333740 -0.416203   46
3 -0.063477  1.139976  212
4 -1.014648 -1.193477   26
5  0.678711  0.096706    7
6 -0.040863 -1.956850  184
7 -0.357422 -0.714337  206

In [358]: df2.dtypes
Out[358]: 
A    float16
B    float64
C      uint8
dtype: object 

默认值

默认情况下,整数类型为int64,浮点类型为float64不受平台(32 位或 64 位)影响。以下都将导致int64数据类型。

In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]: 
a    int64
dtype: object

In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]: 
a    int64
dtype: object

In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]: 
a    int64
dtype: object 

请注意,当创建数组时,Numpy 将选择依赖于平台的类型。在 32 位平台上,以下导致int32

In [362]: frame = pd.DataFrame(np.array([1, 2])) 

向上转换

当与其他类型组合时,类型可能会被向上转换,这意味着它们从当前类型(例如intfloat)提升。

In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

In [364]: df3
Out[364]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [365]: df3.dtypes
Out[365]: 
A    float32
B    float64
C    float64
dtype: object 

DataFrame.to_numpy()将返回数据类型的最低公共分母,即可以容纳结果同类数据类型的 NumPy 数组。这可能会强制进行一些向上转换

In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64') 

astype

您可以使用astype()方法将数据类型明确转换为另一种。默认情况下,即使数据类型未更改(通过传递copy=False来更改此行为),它们也将返回一个副本。此外,如果 astype 操作无效,它们将引发异常。

向上转换始终遵循NumPy规则。如果操作涉及两种不同的数据类型,则将使用更通用的数据类型作为操作的结果。

In [367]: df3
Out[367]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [368]: df3.dtypes
Out[368]: 
A    float32
B    float64
C    float64
dtype: object

# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]: 
A    float32
B    float32
C    float32
dtype: object 

使用astype()将列的子集转换为指定类型。

In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)

In [372]: dft
Out[372]: 
 a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

In [373]: dft.dtypes
Out[373]: 
a    uint8
b    uint8
c    int64
dtype: object 

通过将字典传递给astype()将某些列转换为特定数据类型。

In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})

In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})

In [376]: dft1
Out[376]: 
 a  b    c
0   True  4  7.0
1  False  5  8.0
2   True  6  9.0

In [377]: dft1.dtypes
Out[377]: 
a       bool
b      int64
c    float64
dtype: object 

注意

当尝试使用astype()loc()将列的子集转换为指定类型时,将发生向上转换。

loc()尝试适应我们分配给当前数据类型的内容,而[]将覆盖它们,从右侧获取数据类型。因此,以下代码片段会产生意外结果。

In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]: 
a    uint8
b    uint8
dtype: object

In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)

In [381]: dft.dtypes
Out[381]: 
a    int64
b    int64
c    int64
dtype: object 

对象转换

pandas 提供了各种函数来尝试将object数据类型转换为其他类型。在数据已经是正确类型但存储在object数组中的情况下,可以使用DataFrame.infer_objects()Series.infer_objects()方法进行软转换为正确类型。

In [382]: import datetime

In [383]: df = pd.DataFrame(
 .....:    [
 .....:        [1, 2],
 .....:        ["a", "b"],
 .....:        [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
 .....:    ]
 .....: )
 .....: 

In [384]: df = df.T

In [385]: df
Out[385]: 
 0  1                    2
0  1  a  2016-03-02 00:00:00
1  2  b  2016-03-02 00:00:00

In [386]: df.dtypes
Out[386]: 
0    object
1    object
2    object
dtype: object 

由于数据被转置,原始推断将所有列存储为对象,infer_objects将进行更正。

In [387]: df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object 

以下函数适用于一维对象数组或标量,以执行将对象硬转换为指定类型的操作:

  • to_numeric()(转换为数值数据类型)

    In [388]: m = ["1.1", 2, 3]
    
    In [389]: pd.to_numeric(m)
    Out[389]: array([1.1, 2\. , 3\. ]) 
    
  • to_datetime()(转换为日期时间对象)

    In [390]: import datetime
    
    In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
    
    In [392]: pd.to_datetime(m)
    Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None) 
    
  • to_timedelta()(转换为时间间隔对象)

    In [393]: m = ["5us", pd.Timedelta("1day")]
    
    In [394]: pd.to_timedelta(m)
    Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None) 
    

要强制转换,我们可以传入一个errors参数,指定 pandas 如何处理无法转换为所需数据类型或对象的元素。默认情况下,errors='raise',意味着在转换过程中遇到任何错误都将被引发。但是,如果errors='coerce',这些错误将被忽略,pandas 将把有问题的元素转换为pd.NaT(对于日期时间和时间间隔)或np.nan(对于数值)。如果您正在读取大部分是所需数据类型(例如数值、日期时间)的数据,但偶尔混有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:

In [395]: import datetime

In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]

In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

In [398]: m = ["apple", 2, 3]

In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan,  2.,  3.])

In [400]: m = ["apple", pd.Timedelta("1day")]

In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None) 

除了对象转换,to_numeric()还提供另一个参数downcast,该参数可以将新(或已有)的数值数据向下转换为较小的数据类型,以节省内存:

In [402]: m = ["1", 2, 3]

In [403]: pd.to_numeric(m, downcast="integer")  # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)

In [404]: pd.to_numeric(m, downcast="signed")  # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)

In [405]: pd.to_numeric(m, downcast="unsigned")  # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)

In [406]: pd.to_numeric(m, downcast="float")  # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32) 

由于这些方法仅适用于一维数组、列表或标量;不能直接用于多维对象,如 DataFrames。但是,通过apply(),我们可以高效地对每列应用函数:

In [407]: import datetime

In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")

In [409]: df
Out[409]: 
 0                    1
0  2016-07-09  2016-03-02 00:00:00
1  2016-07-09  2016-03-02 00:00:00

In [410]: df.apply(pd.to_datetime)
Out[410]: 
 0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")

In [412]: df
Out[412]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [413]: df.apply(pd.to_numeric)
Out[413]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")

In [415]: df
Out[415]: 
 0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

In [416]: df.apply(pd.to_timedelta)
Out[416]: 
 0      1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days 

注意事项

integer类型数据上执行选择操作很容易将数据向上转换为floating。在不引入nans的情况下,输入数据的数据类型将被保留。另请参阅对整数 NA 的支持。

In [417]: dfi = df3.astype("int32")

In [418]: dfi["E"] = 1

In [419]: dfi
Out[419]: 
 A  B    C  E
0  1  0   26  1
1  3  1   86  1
2  0  0   46  1
3  0  1  212  1
4 -1 -1   26  1
5  1  0    7  1
6  0 -1  184  1
7  0  0  206  1

In [420]: dfi.dtypes
Out[420]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

In [421]: casted = dfi[dfi > 0]

In [422]: casted
Out[422]: 
 A    B    C  E
0  1.0  NaN   26  1
1  3.0  1.0   86  1
2  NaN  NaN   46  1
3  NaN  1.0  212  1
4  NaN  NaN   26  1
5  1.0  NaN    7  1
6  NaN  NaN  184  1
7  NaN  NaN  206  1

In [423]: casted.dtypes
Out[423]: 
A    float64
B    float64
C      int32
E      int64
dtype: object 

浮点数数据类型保持不变。

In [424]: dfa = df3.copy()

In [425]: dfa["A"] = dfa["A"].astype("float32")

In [426]: dfa.dtypes
Out[426]: 
A    float32
B    float64
C    float64
dtype: object

In [427]: casted = dfa[df2 > 0]

In [428]: casted
Out[428]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2       NaN       NaN   46.0
3       NaN  1.139976  212.0
4       NaN       NaN   26.0
5  1.346426  0.096706    7.0
6       NaN       NaN  184.0
7       NaN       NaN  206.0

In [429]: casted.dtypes
Out[429]: 
A    float32
B    float64
C    float64
dtype: object 

基于dtype选择列

select_dtypes()方法实现基于dtype的列子集选择。

首先,让我们创建一个具有各种不同数据类型的DataFrame

In [430]: df = pd.DataFrame(
 .....:    {
 .....:        "string": list("abc"),
 .....:        "int64": list(range(1, 4)),
 .....:        "uint8": np.arange(3, 6).astype("u1"),
 .....:        "float64": np.arange(4.0, 7.0),
 .....:        "bool1": [True, False, True],
 .....:        "bool2": [False, True, False],
 .....:        "dates": pd.date_range("now", periods=3),
 .....:        "category": pd.Series(list("ABC")).astype("category"),
 .....:    }
 .....: )
 .....: 

In [431]: df["tdeltas"] = df.dates.diff()

In [432]: df["uint64"] = np.arange(3, 6).astype("u8")

In [433]: df["other_dates"] = pd.date_range("20130101", periods=3)

In [434]: df["tz_aware_dates"] = pd.date_range("20130101", periods=3, tz="US/Eastern")

In [435]: df
Out[435]: 
 string  int64  uint8  ...  uint64  other_dates            tz_aware_dates
0      a      1      3  ...       3   2013-01-01 2013-01-01 00:00:00-05:00
1      b      2      4  ...       4   2013-01-02 2013-01-02 00:00:00-05:00
2      c      3      5  ...       5   2013-01-03 2013-01-03 00:00:00-05:00

[3 rows x 12 columns] 

以及数据类型:

In [436]: df.dtypes
Out[436]: 
string                                object
int64                                  int64
uint8                                  uint8
float64                              float64
bool1                                   bool
bool2                                   bool
dates                         datetime64[ns]
category                            category
tdeltas                      timedelta64[ns]
uint64                                uint64
other_dates                   datetime64[ns]
tz_aware_dates    datetime64[ns, US/Eastern]
dtype: object 

select_dtypes()有两个参数includeexclude,允许您说“给我这些数据类型的列”(include)和/或“给我没有这些数据类型的列”(exclude)。

例如,要选择bool列:

In [437]: df.select_dtypes(include=[bool])
Out[437]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

您还可以在NumPy 数据类型层次结构中传递数据类型的名称:

In [438]: df.select_dtypes(include=["bool"])
Out[438]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

select_dtypes()也适用于通用数据类型。

例如,要选择所有数值和布尔列,同时排除无符号整数:

In [439]: df.select_dtypes(include=["number", "bool"], exclude=["unsignedinteger"])
Out[439]: 
 int64  float64  bool1  bool2 tdeltas
0      1      4.0   True  False     NaT
1      2      5.0  False   True  1 days
2      3      6.0   True  False  1 days 

要选择字符串列,必须使用object数据类型:

In [440]: df.select_dtypes(include=["object"])
Out[440]: 
 string
0      a
1      b
2      c 

要查看类似numpy.number这样的通用dtype的所有子数据类型,您可以定义一个返回子数据类型树的函数:

In [441]: def subdtypes(dtype):
 .....:    subs = dtype.__subclasses__()
 .....:    if not subs:
 .....:        return dtype
 .....:    return [dtype, [subdtypes(dt) for dt in subs]]
 .....: 

所有 NumPy 数据类型都是numpy.generic的子类:

In [442]: subdtypes(np.generic)
Out[442]: 
[numpy.generic,
 [[numpy.number,
 [[numpy.integer,
 [[numpy.signedinteger,
 [numpy.int8,
 numpy.int16,
 numpy.int32,
 numpy.int64,
 numpy.longlong,
 numpy.timedelta64]],
 [numpy.unsignedinteger,
 [numpy.uint8,
 numpy.uint16,
 numpy.uint32,
 numpy.uint64,
 numpy.ulonglong]]]],
 [numpy.inexact,
 [[numpy.floating,
 [numpy.float16, numpy.float32, numpy.float64, numpy.longdouble]],
 [numpy.complexfloating,
 [numpy.complex64, numpy.complex128, numpy.clongdouble]]]]]],
 [numpy.flexible,
 [[numpy.character, [numpy.bytes_, numpy.str_]],
 [numpy.void, [numpy.record]]]],
 numpy.bool_,
 numpy.datetime64,
 numpy.object_]] 

注意

pandas 还定义了类型categorydatetime64[ns, tz],它们没有集成到正常的 NumPy 层次结构中,不会显示在上述函数中。

Head 和 tail

要查看 Series 或 DataFrame 对象的小样本,请使用head()tail()方法。默认显示的元素数量为五个,但您可以传递自定义数量。

In [4]: long_series = pd.Series(np.random.randn(1000))

In [5]: long_series.head()
Out[5]: 
0   -1.157892
1   -1.344312
2    0.844885
3    1.075770
4   -0.109050
dtype: float64

In [6]: long_series.tail(3)
Out[6]: 
997   -0.289388
998   -1.020544
999    0.589993
dtype: float64 

属性和底层数据

pandas 对象具有许多属性,使您能够访问元数据

  • shape:给出对象的轴维度,与 ndarray 一致

  • 轴标签

    • Series索引(仅轴)

    • DataFrame索引(行)和

注意,这些属性可以安全地分配给

In [7]: df[:2]
Out[7]: 
 A         B         C
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929

In [8]: df.columns = [x.lower() for x in df.columns]

In [9]: df
Out[9]: 
 a         b         c
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03  1.071804  0.721555 -0.706771
2000-01-04 -1.039575  0.271860 -0.424972
2000-01-05  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427
2000-01-07  0.524988  0.404705  0.577046
2000-01-08 -1.715002 -1.039268 -0.370647 

pandas 对象(IndexSeriesDataFrame)可以被视为数组的容器,其中保存实际数据并执行实际计算。对于许多类型,底层数组是一个numpy.ndarray。但是,pandas 和第三方库可能会扩展 NumPy 的类型系统以支持自定义数组(请参阅 dtypes)。

要获取IndexSeries内的实际数据,请使用.array属性

In [10]: s.array
Out[10]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64

In [11]: s.index.array
Out[11]: 
<NumpyExtensionArray>
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object 

array将始终是一个ExtensionArray。关于ExtensionArray的确切细节以及 pandas 为什么使用它们略微超出了本介绍的范围。更多信息请参见 dtypes。

如果您知道您需要一个 NumPy 数组,请使用to_numpy()numpy.asarray()

In [12]: s.to_numpy()
Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

In [13]: np.asarray(s)
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121]) 

当 Series 或 Index 由ExtensionArray支持时,to_numpy()可能涉及复制数据和强制值。更多信息请参见 dtypes。

to_numpy()可以对生成的numpy.ndarraydtype进行一些控制。例如,考虑带有时区的日期时间。NumPy 没有一种 dtype 来表示带时区的日期时间,因此有两种可能有用的表示方式:

  1. 一个带有Timestamp对象的对象数据类型numpy.ndarray,每个对象都具有正确的tz

  2. 一个datetime64[ns] -dtype numpy.ndarray,其中值已转换为 UTC 并且时区已丢弃

时区可能会被保留为dtype=object

In [14]: ser = pd.Series(pd.date_range("2000", periods=2, tz="CET"))

In [15]: ser.to_numpy(dtype=object)
Out[15]: 
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
 Timestamp('2000-01-02 00:00:00+0100', tz='CET')], dtype=object) 

或者使用dtype='datetime64[ns]'丢弃

In [16]: ser.to_numpy(dtype="datetime64[ns]")
Out[16]: 
array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'],
 dtype='datetime64[ns]') 

获取DataFrame内的“原始数据”可能会更加复杂。当您的DataFrame所有列只有一个数据类型时,DataFrame.to_numpy()将返回底层数据:

In [17]: df.to_numpy()
Out[17]: 
array([[-0.1732,  0.1192, -1.0442],
 [-0.8618, -2.1046, -0.4949],
 [ 1.0718,  0.7216, -0.7068],
 [-1.0396,  0.2719, -0.425 ],
 [ 0.567 ,  0.2762, -1.0874],
 [-0.6737,  0.1136, -1.4784],
 [ 0.525 ,  0.4047,  0.577 ],
 [-1.715 , -1.0393, -0.3706]]) 

如果 DataFrame 包含同质类型数据,ndarray 实际上可以就地修改,并且更改将反映在数据结构中。对于异构数据(例如 DataFrame 的某些列不全是相同 dtype),情况并非如此。与轴标签不同,值属性本身不能被赋值。

注意

当处理异构数据时,生成的 ndarray 的 dtype 将被选择以容纳所有涉及的数据。例如,如果涉及字符串,则结果将是对象 dtype。如果只有浮点数和整数,则生成的数组将是浮点 dtype。

在过去,pandas 推荐使用Series.valuesDataFrame.values来从 Series 或 DataFrame 中提取数据。您仍然会在旧代码库和在线上找到对这些的引用。未来,我们建议避免使用.values,而是使用.array.to_numpy().values具有以下缺点:

  1. 当您的 Series 包含扩展类型时,不清楚Series.values是返回 NumPy 数组还是扩展数组。Series.array将始终返回一个ExtensionArray,并且永远不会复制数据。Series.to_numpy()将始终返回一个 NumPy 数组,可能会以复制/强制转换值为代价。

  2. 当您的 DataFrame 包含不同数据类型时,DataFrame.values可能涉及复制数据并将值强制转换为公共 dtype,这是一个相对昂贵的操作。作为一个方法,DataFrame.to_numpy()更清晰地表明返回的 NumPy 数组可能不是 DataFrame 中相同数据的视图。

加速操作

pandas 支持使用numexpr库和bottleneck库加速某些类型的二进制数值和布尔运算。

当处理大型数据集时,这些库特别有用,并提供大幅加速。numexpr使用智能分块、缓存和多核。bottleneck是一组专门的 cython 例程,当处理具有nans的数组时特别快。

这里是一个示例(使用 100 列 x 100,000 行的DataFrames):

操作 0.11.0(毫秒) 之前版本(毫秒) 相对于之前的比率
df1 > df2 13.32 125.35 0.1063
df1 * df2 21.71 36.63 0.5928
df1 + df2 22.04 36.50 0.6039

你强烈建议安装这两个库。查看推荐依赖项部分获取更多安装信息。

这两者默认启用,您可以通过设置选项来控制:

pd.set_option("compute.use_bottleneck", False)
pd.set_option("compute.use_numexpr", False) 

灵活的二进制操作

在 pandas 数据结构之间进行二进制操作时,有两个关键点值得关注:

  • 高维(例如 DataFrame)和低维(例如 Series)对象之间的广播行为。

  • 计算中的缺失数据。

我们将演示如何独立处理这些问题,尽管它们可以同时处理。

匹配/广播行为

DataFrame 具有方法add()sub()mul()div()和相关函数radd()rsub()等用于执行二进制操作。对于广播行为,Series 输入是主要关注点。使用这些函数,您可以通过 axis 关键字来匹配 索引

In [18]: df = pd.DataFrame(
 ....:    {
 ....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 ....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 ....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 ....:    }
 ....: )
 ....: 

In [19]: df
Out[19]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [20]: row = df.iloc[1]

In [21]: column = df["two"]

In [22]: df.sub(row, axis="columns")
Out[22]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [23]: df.sub(row, axis=1)
Out[23]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [24]: df.sub(column, axis="index")
Out[24]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516

In [25]: df.sub(column, axis=0)
Out[25]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516 

此外,您可以将 MultiIndexed DataFrame 的一个级别与 Series 对齐。

In [26]: dfmi = df.copy()

In [27]: dfmi.index = pd.MultiIndex.from_tuples(
 ....:    [(1, "a"), (1, "b"), (1, "c"), (2, "a")], names=["first", "second"]
 ....: )
 ....: 

In [28]: dfmi.sub(column, axis=0, level="second")
Out[28]: 
 one       two     three
first second 
1     a      -0.377535  0.000000       NaN
 b      -1.569069  0.000000 -1.962513
 c      -0.783123  0.000000 -0.250933
2     a            NaN -1.493173 -2.385688 

Series 和 Index 也支持divmod()内置函数。该函数同时进行地板除法和取模运算,返回与左侧相同类型的两元组。例如:

In [29]: s = pd.Series(np.arange(10))

In [30]: s
Out[30]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [31]: div, rem = divmod(s, 3)

In [32]: div
Out[32]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [33]: rem
Out[33]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [34]: idx = pd.Index(np.arange(10))

In [35]: idx
Out[35]: Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [36]: div, rem = divmod(idx, 3)

In [37]: div
Out[37]: Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [38]: rem
Out[38]: Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64') 

我们也可以逐元素进行divmod()操作:

In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [40]: div
Out[40]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [41]: rem
Out[41]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64 

缺失数据/使用填充值的操作

在 Series 和 DataFrame 中,算术函数有一个 fill_value 选项,即在某个位置的值中至多有一个缺失时要替换的值。例如,当添加两个 DataFrame 对象时,您可能希望将 NaN 视为 0,除非两个 DataFrame 都缺少该值,此时结果将为 NaN(如果需要,您可以稍后使用 fillna 将 NaN 替换为其他值)。

In [42]: df2 = df.copy()

In [43]: df2.loc["a", "three"] = 1.0

In [44]: df
Out[44]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [45]: df2
Out[45]: 
 one       two     three
a  1.394981  1.772517  1.000000
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [46]: df + df2
Out[46]: 
 one       two     three
a  2.789963  3.545034       NaN
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343

In [47]: df.add(df2, fill_value=0)
Out[47]: 
 one       two     three
a  2.789963  3.545034  1.000000
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343 

灵活的比较

Series 和 DataFrame 具有二进制比较方法 eqneltgtlege,其行为类似于上述二进制算术操作:

In [48]: df.gt(df2)
Out[48]: 
 one    two  three
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [49]: df2.ne(df)
Out[49]: 
 one    two  three
a  False  False   True
b  False  False  False
c  False  False  False
d   True  False  False 

这些操作产生与左侧输入相同类型的 dtype 为 bool 的 pandas 对象。这些 boolean 对象可以用于索引操作,请参阅 布尔索引 部分。### 布尔规约

您可以应用以下规约:emptyany()all()bool() 来提供一种总结布尔结果的方式。

In [50]: (df > 0).all()
Out[50]: 
one      False
two       True
three    False
dtype: bool

In [51]: (df > 0).any()
Out[51]: 
one      True
two      True
three    True
dtype: bool 

您可以将其减少为最终布尔值。

In [52]: (df > 0).any().any()
Out[52]: True 

您可以通过 empty 属性来测试 pandas 对象是否为空。

In [53]: df.empty
Out[53]: False

In [54]: pd.DataFrame(columns=list("ABC")).empty
Out[54]: True 

警告

断言 pandas 对象的真实性将引发错误,因为对空值或值的测试是模棱两可的。

In [55]: if df:
 ....:    print(True)
 ....: 
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-55-318d08b2571a> in ?()
----> 1 if df:
  2     print(True)

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 
In [56]: df and df2
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-56-b241b64bb471> in ?()
----> 1 df and df2

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 

有关更详细讨论,请参阅 陷阱。### 比较对象是否等价

通常您可能会发现有多种方法可以计算相同的结果。举个简单的例子,考虑 df + dfdf * 2。为了测试这两个计算是否产生相同的结果,根据上面展示的工具,您可以想象使用 (df + df == df * 2).all()。但事实上,这个表达式是 False:

In [57]: df + df == df * 2
Out[57]: 
 one   two  three
a   True  True  False
b   True  True   True
c   True  True   True
d  False  True   True

In [58]: (df + df == df * 2).all()
Out[58]: 
one      False
two       True
three    False
dtype: bool 

注意到布尔型 DataFrame df + df == df * 2 包含一些 False 值!这是因为 NaN 不会被视为相等:

In [59]: np.nan == np.nan
Out[59]: False 

因此,NDFrames(如 Series 和 DataFrames)具有一个用于测试相等性的 equals() 方法,其中对应位置的 NaN 被视为相等。

In [60]: (df + df).equals(df * 2)
Out[60]: True 

请注意,为了使相等性为 True,Series 或 DataFrame 索引需要按相同顺序排列:

In [61]: df1 = pd.DataFrame({"col": ["foo", 0, np.nan]})

In [62]: df2 = pd.DataFrame({"col": [np.nan, 0, "foo"]}, index=[2, 1, 0])

In [63]: df1.equals(df2)
Out[63]: False

In [64]: df1.equals(df2.sort_index())
Out[64]: True 

比较类似数组的对象

当将 pandas 数据结构与标量值进行比较时,您可以方便地执行逐元素比较:

In [65]: pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]: 
0     True
1    False
2    False
dtype: bool

In [66]: pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False]) 

pandas 还处理了长度相同的不同类似数组对象之间的逐元素比较:

In [67]: pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]: 
0     True
1     True
2    False
dtype: bool

In [68]: pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]: 
0     True
1     True
2    False
dtype: bool 

尝试比较不同长度的 IndexSeries 对象将引发 ValueError:

In [69]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[69], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

In [70]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[70], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects 

合并重叠的数据集

一个偶尔出现的问题是合并两个相似的数据集,其中一个数据集中的值优先于另一个。一个例子是代表特定经济指标的两个数据系列,其中一个被认为是“更高质量”的。然而,较低质量的系列可能在历史上延伸得更长,或者数据覆盖更完整。因此,我们希望将两个 DataFrame 对象合并,其中一个 DataFrame 中的缺失值有条件地用另一个 DataFrame 中的相同标签值填充。实现此操作的函数是combine_first(),我们进行如下说明:

In [71]: df1 = pd.DataFrame(
 ....:    {"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
 ....: )
 ....: 

In [72]: df2 = pd.DataFrame(
 ....:    {
 ....:        "A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
 ....:        "B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
 ....:    }
 ....: )
 ....: 

In [73]: df1
Out[73]: 
 A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [74]: df2
Out[74]: 
 A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [75]: df1.combine_first(df2)
Out[75]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 

通用 DataFrame 合并

上述 combine_first() 方法调用更一般的 DataFrame.combine()。此方法接受另一个 DataFrame 和一个组合函数,对齐输入 DataFrame,然后传递组合函数的 Series 对(即,列名相同的列)。

因此,例如,要重现上述 combine_first()

In [76]: def combiner(x, y):
 ....:    return np.where(pd.isna(x), y, x)
 ....: 

In [77]: df1.combine(df2, combiner)
Out[77]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 

匹配 / 广播行为

DataFrame 有方法 add()sub()mul()div() 和相关函数 radd()rsub(),… 用于执行二进制操作。对广播行为,Series 输入是主要关注的。使用这些函数,您可以通过 axis 关键字匹配 indexcolumns

In [18]: df = pd.DataFrame(
 ....:    {
 ....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 ....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 ....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 ....:    }
 ....: )
 ....: 

In [19]: df
Out[19]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [20]: row = df.iloc[1]

In [21]: column = df["two"]

In [22]: df.sub(row, axis="columns")
Out[22]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [23]: df.sub(row, axis=1)
Out[23]: 
 one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [24]: df.sub(column, axis="index")
Out[24]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516

In [25]: df.sub(column, axis=0)
Out[25]: 
 one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516 

此外,您可以将 MultiIndexed DataFrame 的一个级别与 Series 对齐。

In [26]: dfmi = df.copy()

In [27]: dfmi.index = pd.MultiIndex.from_tuples(
 ....:    [(1, "a"), (1, "b"), (1, "c"), (2, "a")], names=["first", "second"]
 ....: )
 ....: 

In [28]: dfmi.sub(column, axis=0, level="second")
Out[28]: 
 one       two     three
first second 
1     a      -0.377535  0.000000       NaN
 b      -1.569069  0.000000 -1.962513
 c      -0.783123  0.000000 -0.250933
2     a            NaN -1.493173 -2.385688 

Series 和 Index 也支持内置的 divmod()。此函数同时进行地板除法和模运算,返回与左侧相同类型的两个元组。例如:

In [29]: s = pd.Series(np.arange(10))

In [30]: s
Out[30]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [31]: div, rem = divmod(s, 3)

In [32]: div
Out[32]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [33]: rem
Out[33]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [34]: idx = pd.Index(np.arange(10))

In [35]: idx
Out[35]: Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [36]: div, rem = divmod(idx, 3)

In [37]: div
Out[37]: Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [38]: rem
Out[38]: Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64') 

我们还可以进行逐元素的 divmod()

In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [40]: div
Out[40]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [41]: rem
Out[41]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64 

缺失数据 / 使用填充值的操作

在 Series 和 DataFrame 中,算术函数有一个 fill_value 选项,即在一个位置的值中至多有一个缺失时要替换的值。例如,当添加两个 DataFrame 对象时,你可能希望将 NaN 视为 0,除非两个 DataFrame 都缺少该值,此时结果将是 NaN(如果你愿意,你可以稍后使用 fillna 将 NaN 替换为其他值)。

In [42]: df2 = df.copy()

In [43]: df2.loc["a", "three"] = 1.0

In [44]: df
Out[44]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [45]: df2
Out[45]: 
 one       two     three
a  1.394981  1.772517  1.000000
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [46]: df + df2
Out[46]: 
 one       two     three
a  2.789963  3.545034       NaN
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343

In [47]: df.add(df2, fill_value=0)
Out[47]: 
 one       two     three
a  2.789963  3.545034  1.000000
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343 

灵活的比较

Series 和 DataFrame 有二进制比较方法 eqneltgtlege,其行为类似于上面描述的二进制算术操作:

In [48]: df.gt(df2)
Out[48]: 
 one    two  three
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [49]: df2.ne(df)
Out[49]: 
 one    two  three
a  False  False   True
b  False  False  False
c  False  False  False
d   True  False  False 

这些操作会产生与左侧输入相同类型的 dtype 为 bool 的 pandas 对象。这些 boolean 对象可以在索引操作中使用,请参阅布尔索引一节。

布尔归约

你可以应用归约:emptyany()all()bool() 来提供一种总结布尔结果的方法。

In [50]: (df > 0).all()
Out[50]: 
one      False
two       True
three    False
dtype: bool

In [51]: (df > 0).any()
Out[51]: 
one      True
two      True
three    True
dtype: bool 

你可以归约到最终的布尔值。

In [52]: (df > 0).any().any()
Out[52]: True 

你可以通过 empty 属性测试 pandas 对象是否为空。

In [53]: df.empty
Out[53]: False

In [54]: pd.DataFrame(columns=list("ABC")).empty
Out[54]: True 

警告

断言 pandas 对象的真实性会引发错误,因为空值或值的测试是模糊的。

In [55]: if df:
 ....:    print(True)
 ....: 
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-55-318d08b2571a> in ?()
----> 1 if df:
  2     print(True)

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 
In [56]: df and df2
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
<ipython-input-56-b241b64bb471> in ?()
----> 1 df and df2

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
  1575     @final
  1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
  1578             f"The truth value of a {type(self).__name__} is ambiguous. "
  1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
  1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 

详细讨论请参阅陷阱。

比较对象是否等价

常常你会发现有多种方法可以计算相同的结果。举个简单的例子,考虑df + dfdf * 2。为了测试这两个计算是否产生相同的结果,根据上面展示的工具,你可能会想象使用(df + df == df * 2).all()。但实际上,这个表达式是错误的:

In [57]: df + df == df * 2
Out[57]: 
 one   two  three
a   True  True  False
b   True  True   True
c   True  True   True
d  False  True   True

In [58]: (df + df == df * 2).all()
Out[58]: 
one      False
two       True
three    False
dtype: bool 

注意,布尔 DataFrame df + df == df * 2 包含一些 False 值!这是因为 NaN 不会比较相等:

In [59]: np.nan == np.nan
Out[59]: False 

因此,NDFrames(如 Series 和 DataFrames)具有一个 equals() 方法,用于测试相等性,其中相应位置的 NaN 被视为相等。

In [60]: (df + df).equals(df * 2)
Out[60]: True 

请注意,要使相等为真,Series 或 DataFrame 索引需要按相同顺序排列:

In [61]: df1 = pd.DataFrame({"col": ["foo", 0, np.nan]})

In [62]: df2 = pd.DataFrame({"col": [np.nan, 0, "foo"]}, index=[2, 1, 0])

In [63]: df1.equals(df2)
Out[63]: False

In [64]: df1.equals(df2.sort_index())
Out[64]: True 

比较类数组对象

当比较一个 pandas 数据结构与一个标量值时,你可以方便地执行逐元素比较:

In [65]: pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]: 
0     True
1    False
2    False
dtype: bool

In [66]: pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False]) 

pandas 还处理同长度的不同类数组对象之间的逐元素比较:

In [67]: pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]: 
0     True
1     True
2    False
dtype: bool

In [68]: pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]: 
0     True
1     True
2    False
dtype: bool 

尝试比较不同长度的 IndexSeries 对象会引发 ValueError:

In [69]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[69], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

In [70]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
---------------------------------------------------------------------------
ValueError  Traceback (most recent call last)
Cell In[70], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
  72             return NotImplemented
  74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
  38 @unpack_zerodim_and_defer("__eq__")
  39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6114, in Series._cmp_method(self, other, op)
  6111 res_name = ops.get_op_result_name(self, other)
  6113 if isinstance(other, Series) and not self._indexed_same(other):
-> 6114     raise ValueError("Can only compare identically-labeled Series objects")
  6116 lvalues = self._values
  6117 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects 

合并重叠数据集

有时会出现一个问题,即合并两个相似的数据集,其中一个数据集中的值优先于另一个。一个例子是代表特定经济指标的两个数据系列,其中一个被认为是“更高质量”的。然而,较低质量的系列可能在历史上延伸得更远,或者具有更完整的数据覆盖。因此,我们希望将两个 DataFrame 对象合并,其中一个 DataFrame 中的缺失值有条件地用另一个 DataFrame 中的类似标记值填充。实现此操作的函数是combine_first(),我们进行演示:

In [71]: df1 = pd.DataFrame(
 ....:    {"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
 ....: )
 ....: 

In [72]: df2 = pd.DataFrame(
 ....:    {
 ....:        "A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
 ....:        "B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
 ....:    }
 ....: )
 ....: 

In [73]: df1
Out[73]: 
 A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [74]: df2
Out[74]: 
 A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [75]: df1.combine_first(df2)
Out[75]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 

通用 DataFrame 合并

上面的combine_first()方法调用了更一般的DataFrame.combine()。此方法接受另一个 DataFrame 和一个合并函数,对齐输入 DataFrame,然后将组合器函数传递给一对 Series(即,列名称相同的列)。

因此,例如,要重现combine_first()如上所示:

In [76]: def combiner(x, y):
 ....:    return np.where(pd.isna(x), y, x)
 ....: 

In [77]: df1.combine(df2, combiner)
Out[77]: 
 A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0 

描述性统计

存在大量计算描述性统计和其他相关操作的方法,适用于 Series,DataFrame。其中大多数是聚合(因此生成较低维度的结果),如sum()mean()quantile(),但其中一些,如cumsum()cumprod(),生成相同大小的对象。一般来说,这些方法接受一个axis参数,就像ndarray.{sum, std, …}一样,但是轴可以通过名称或整数指定:

  • Series:不需要轴参数

  • DataFrame:“索引”(axis=0,默认),“列”(axis=1)

例如:

In [78]: df
Out[78]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [79]: df.mean(0)
Out[79]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [80]: df.mean(1)
Out[80]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64 

所有这些方法都有一个skipna选项,指示是否排除缺失数据(默认为True):

In [81]: df.sum(0, skipna=False)
Out[81]: 
one           NaN
two      5.442353
three         NaN
dtype: float64

In [82]: df.sum(axis=1, skipna=True)
Out[82]: 
a    3.167498
b    2.204786
c    3.401050
d   -0.333828
dtype: float64 

结合广播/算术行为,可以非常简洁地描述各种统计程序,比如标准化(使数据均值为零,标准差为 1):

In [83]: ts_stand = (df - df.mean()) / df.std()

In [84]: ts_stand.std()
Out[84]: 
one      1.0
two      1.0
three    1.0
dtype: float64

In [85]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

In [86]: xs_stand.std(1)
Out[86]: 
a    1.0
b    1.0
c    1.0
d    1.0
dtype: float64 

注意,像 cumsum()cumprod() 这样的方法会保留 NaN 值的位置。这与 expanding()rolling() 有些不同,因为 NaN 的行为还受 min_periods 参数的影响。

In [87]: df.cumsum()
Out[87]: 
 one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873 

这是常见函数的快速参考摘要表。每个函数还接受一个可选的 level 参数,该参数仅在对象具有分层索引时适用。

函数 描述
count 非 NA 观测数量
sum 值的总和
mean 值的均值
median 值的算术中位数
min 最小值
max 最大值
mode 众数
abs 绝对值
prod 值的乘积
std Bessel 校正的样本标准差
var 无偏方差
sem 均值的标准误差
skew 样本偏度(3 阶矩)
kurt 样本峰度(4 阶矩)
quantile 样本分位数(%处的值)
cumsum 累积和
cumprod 累积乘积
cummax 累积最大值
cummin 累积最小值

注意,一些 NumPy 方法,如 meanstdsum,默认情况下会在 Series 输入中排除 NA 值:

In [88]: np.mean(df["one"])
Out[88]: 0.8110935116651192

In [89]: np.mean(df["one"].to_numpy())
Out[89]: nan 

Series.nunique() 将返回 Series 中唯一非 NA 值的数量:

In [90]: series = pd.Series(np.random.randn(500))

In [91]: series[20:500] = np.nan

In [92]: series[10:20] = 5

In [93]: series.nunique()
Out[93]: 11 

数据汇总:describe

有一个方便的 describe() 函数,可以计算关于 Series 或 DataFrame 列的各种摘要统计信息(当然不包括 NA 值):

In [94]: series = pd.Series(np.random.randn(1000))

In [95]: series[::2] = np.nan

In [96]: series.describe()
Out[96]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
25%       -0.699070
50%       -0.069718
75%        0.714483
max        3.160915
dtype: float64

In [97]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])

In [98]: frame.iloc[::2] = np.nan

In [99]: frame.describe()
Out[99]: 
 a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.033387    0.030045   -0.043719   -0.051686    0.005979
std      1.017152    0.978743    1.025270    1.015988    1.006695
min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
25%     -0.647623   -0.576449   -0.712369   -0.691338   -0.691115
50%      0.047578   -0.021499   -0.023888   -0.032652   -0.025363
75%      0.729907    0.775880    0.618896    0.670047    0.649748
max      2.740139    2.752332    3.004229    2.728702    3.240991 

您可以选���在输出中包含特定的百分位数:

In [100]: series.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
Out[100]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
5%        -1.645423
25%       -0.699070
50%       -0.069718
75%        0.714483
95%        1.711409
max        3.160915
dtype: float64 

默认情况下,中位数始终包括在内。

对于非数值 Series 对象,describe() 将提供关于唯一值数量和最常出现值的简单摘要:

In [101]: s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])

In [102]: s.describe()
Out[102]: 
count     9
unique    4
top       a
freq      5
dtype: object 

注意,在混合类型的 DataFrame 对象上,describe() 将限制摘要仅包括数值列或者如果没有数值列,则仅包括分类列:

In [103]: frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})

In [104]: frame.describe()
Out[104]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000 

可以通过提供一个类型列表作为 include/exclude 参数来控制此行为。还可以使用特殊值 all

In [105]: frame.describe(include=["object"])
Out[105]: 
 a
count     4
unique    2
top     Yes
freq      2

In [106]: frame.describe(include=["number"])
Out[106]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [107]: frame.describe(include="all")
Out[107]: 
 a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000 

该功能依赖于 select_dtypes。有关接受的输入的详细信息,请参考那里。### 最小/最大值的索引

Series 和 DataFrame 上的idxmin()idxmax()函数计算具有最小和最大对应值的索引标签:

In [108]: s1 = pd.Series(np.random.randn(5))

In [109]: s1
Out[109]: 
0    1.118076
1   -0.352051
2   -1.242883
3   -1.277155
4   -0.641184
dtype: float64

In [110]: s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)

In [111]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])

In [112]: df1
Out[112]: 
 A         B         C
0 -0.327863 -0.946180 -0.137570
1 -0.186235 -0.257213 -0.486567
2 -0.507027 -0.871259 -0.111110
3  2.000339 -2.430505  0.089759
4 -0.321434 -0.033695  0.096271

In [113]: df1.idxmin(axis=0)
Out[113]: 
A    2
B    3
C    1
dtype: int64

In [114]: df1.idxmax(axis=1)
Out[114]: 
0    C
1    A
2    C
3    A
4    C
dtype: object 

当有多行(或列)匹配最小值或最大值时,idxmin()idxmax()返回第一个匹配的索引:

In [115]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))

In [116]: df3
Out[116]: 
 A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [117]: df3["A"].idxmin()
Out[117]: 'd' 

注意

idxminidxmax 在 NumPy 中被称为 argminargmax。### 值计数(直方图)/ 众数

value_counts() Series 方法计算值的 1D 数组的直方图。它也可以用作常规数组的函数:

In [118]: data = np.random.randint(0, 7, size=50)

In [119]: data
Out[119]: 
array([6, 6, 2, 3, 5, 3, 2, 5, 4, 5, 4, 3, 4, 5, 0, 2, 0, 4, 2, 0, 3, 2,
 2, 5, 6, 5, 3, 4, 6, 4, 3, 5, 6, 4, 3, 6, 2, 6, 6, 2, 3, 4, 2, 1,
 6, 2, 6, 1, 5, 4])

In [120]: s = pd.Series(data)

In [121]: s.value_counts()
Out[121]: 
6    10
2    10
4     9
3     8
5     8
0     3
1     2
Name: count, dtype: int64 

value_counts()方法可用于计算跨多列的组合。默认情况下使用所有列,但可以使用 subset 参数选择子集。

In [122]: data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}

In [123]: frame = pd.DataFrame(data)

In [124]: frame.value_counts()
Out[124]: 
a  b
1  x    1
2  x    1
3  y    1
4  y    1
Name: count, dtype: int64 

类似地,您可以获取 Series 或 DataFrame 中值的出现频率最高的值,即众数:

In [125]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [126]: s5.mode()
Out[126]: 
0    3
1    7
dtype: int64

In [127]: df5 = pd.DataFrame(
 .....:    {
 .....:        "A": np.random.randint(0, 7, size=50),
 .....:        "B": np.random.randint(-10, 15, size=50),
 .....:    }
 .....: )
 .....: 

In [128]: df5.mode()
Out[128]: 
 A   B
0  1.0  -9
1  NaN  10
2  NaN  13 

离散化和分位数

连续值可以使用cut()(基于值的箱)和qcut()(基于样本分位数的箱)函数进行离散化:

In [129]: arr = np.random.randn(20)

In [130]: factor = pd.cut(arr, 4)

In [131]: factor
Out[131]: 
[(-0.251, 0.464], (-0.968, -0.251], (0.464, 1.179], (-0.251, 0.464], (-0.968, -0.251], ..., (-0.251, 0.464], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251]]
Length: 20
Categories (4, interval[float64, right]): [(-0.968, -0.251] < (-0.251, 0.464] < (0.464, 1.179] <
 (1.179, 1.893]]

In [132]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [133]: factor
Out[133]: 
[(0, 1], (-1, 0], (0, 1], (0, 1], (-1, 0], ..., (-1, 0], (-1, 0], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64, right]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]] 

qcut()计算样本分位数。例如,我们可以将一些正态分布数据切片成相等大小的四分位数:

In [134]: arr = np.random.randn(30)

In [135]: factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])

In [136]: factor
Out[136]: 
[(0.569, 1.184], (-2.278, -0.301], (-2.278, -0.301], (0.569, 1.184], (0.569, 1.184], ..., (-0.301, 0.569], (1.184, 2.346], (1.184, 2.346], (-0.301, 0.569], (-2.278, -0.301]]
Length: 30
Categories (4, interval[float64, right]): [(-2.278, -0.301] < (-0.301, 0.569] < (0.569, 1.184] <
 (1.184, 2.346]] 

我们也可以传入无限值来定义箱子:

In [137]: arr = np.random.randn(20)

In [138]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [139]: factor
Out[139]: 
[(-inf, 0.0], (0.0, inf], (0.0, inf], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (0.0, inf], (0.0, inf]]
Length: 20
Categories (2, interval[float64, right]): [(-inf, 0.0] < (0.0, inf]] 

数据汇总:描述

有一个方便的describe()函数,它计算关于 Series 或 DataFrame 的列的各种摘要统计信息(当然不包括 NAs):

In [94]: series = pd.Series(np.random.randn(1000))

In [95]: series[::2] = np.nan

In [96]: series.describe()
Out[96]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
25%       -0.699070
50%       -0.069718
75%        0.714483
max        3.160915
dtype: float64

In [97]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])

In [98]: frame.iloc[::2] = np.nan

In [99]: frame.describe()
Out[99]: 
 a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.033387    0.030045   -0.043719   -0.051686    0.005979
std      1.017152    0.978743    1.025270    1.015988    1.006695
min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
25%     -0.647623   -0.576449   -0.712369   -0.691338   -0.691115
50%      0.047578   -0.021499   -0.023888   -0.032652   -0.025363
75%      0.729907    0.775880    0.618896    0.670047    0.649748
max      2.740139    2.752332    3.004229    2.728702    3.240991 

你可以选择特定的百分位数包含在输出中:

In [100]: series.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
Out[100]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
5%        -1.645423
25%       -0.699070
50%       -0.069718
75%        0.714483
95%        1.711409
max        3.160915
dtype: float64 

默认情况下,中位数始终包含在内。

对于非数值 Series 对象,describe()将给出关于唯一值数量和最常出现值的简单摘要:

In [101]: s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])

In [102]: s.describe()
Out[102]: 
count     9
unique    4
top       a
freq      5
dtype: object 

请注意,在混合类型的 DataFrame 对象上,describe()将限制摘要仅包括数值列或者如果没有数值列,则仅包括分类列:

In [103]: frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})

In [104]: frame.describe()
Out[104]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000 

可以通过提供类型列表作为include/exclude参数来控制此行为。还可以使用特殊值all

In [105]: frame.describe(include=["object"])
Out[105]: 
 a
count     4
unique    2
top     Yes
freq      2

In [106]: frame.describe(include=["number"])
Out[106]: 
 b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [107]: frame.describe(include="all")
Out[107]: 
 a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000 

该功能依赖于 select_dtypes。有关接受的输入的详细信息,请参阅那里。

最小/最大值的索引

Series 和 DataFrame 上的idxmin()idxmax()函数计算具有最小和最大对应值的索引标签:

In [108]: s1 = pd.Series(np.random.randn(5))

In [109]: s1
Out[109]: 
0    1.118076
1   -0.352051
2   -1.242883
3   -1.277155
4   -0.641184
dtype: float64

In [110]: s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)

In [111]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])

In [112]: df1
Out[112]: 
 A         B         C
0 -0.327863 -0.946180 -0.137570
1 -0.186235 -0.257213 -0.486567
2 -0.507027 -0.871259 -0.111110
3  2.000339 -2.430505  0.089759
4 -0.321434 -0.033695  0.096271

In [113]: df1.idxmin(axis=0)
Out[113]: 
A    2
B    3
C    1
dtype: int64

In [114]: df1.idxmax(axis=1)
Out[114]: 
0    C
1    A
2    C
3    A
4    C
dtype: object 

当有多行(或列)匹配最小值或最大值时,idxmin()idxmax()返回第一个匹配的索引:

In [115]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))

In [116]: df3
Out[116]: 
 A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [117]: df3["A"].idxmin()
Out[117]: 'd' 

注意

在 NumPy 中,idxminidxmax被称为argminargmax

值计数(直方图)/ 众数

value_counts() Series 方法计算值数组的直方图。它也可以用作常规数组的函数:

In [118]: data = np.random.randint(0, 7, size=50)

In [119]: data
Out[119]: 
array([6, 6, 2, 3, 5, 3, 2, 5, 4, 5, 4, 3, 4, 5, 0, 2, 0, 4, 2, 0, 3, 2,
 2, 5, 6, 5, 3, 4, 6, 4, 3, 5, 6, 4, 3, 6, 2, 6, 6, 2, 3, 4, 2, 1,
 6, 2, 6, 1, 5, 4])

In [120]: s = pd.Series(data)

In [121]: s.value_counts()
Out[121]: 
6    10
2    10
4     9
3     8
5     8
0     3
1     2
Name: count, dtype: int64 

value_counts() 方法可用于计算多列之间的组合。默认情况下会使用所有列,但可以使用subset参数选择子集。

In [122]: data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}

In [123]: frame = pd.DataFrame(data)

In [124]: frame.value_counts()
Out[124]: 
a  b
1  x    1
2  x    1
3  y    1
4  y    1
Name: count, dtype: int64 

同样,您可以获取 Series 或 DataFrame 中值的出现频率最高的值(即众数):

In [125]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [126]: s5.mode()
Out[126]: 
0    3
1    7
dtype: int64

In [127]: df5 = pd.DataFrame(
 .....:    {
 .....:        "A": np.random.randint(0, 7, size=50),
 .....:        "B": np.random.randint(-10, 15, size=50),
 .....:    }
 .....: )
 .....: 

In [128]: df5.mode()
Out[128]: 
 A   B
0  1.0  -9
1  NaN  10
2  NaN  13 

离散化和分位数

连续值可以使用cut()(基于值的区间)和qcut()(基于样本分位数的区间)函数进行离散化:

In [129]: arr = np.random.randn(20)

In [130]: factor = pd.cut(arr, 4)

In [131]: factor
Out[131]: 
[(-0.251, 0.464], (-0.968, -0.251], (0.464, 1.179], (-0.251, 0.464], (-0.968, -0.251], ..., (-0.251, 0.464], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251]]
Length: 20
Categories (4, interval[float64, right]): [(-0.968, -0.251] < (-0.251, 0.464] < (0.464, 1.179] <
 (1.179, 1.893]]

In [132]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [133]: factor
Out[133]: 
[(0, 1], (-1, 0], (0, 1], (0, 1], (-1, 0], ..., (-1, 0], (-1, 0], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64, right]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]] 

qcut() 计算样本分位数。例如,我们可以将一些正态分布数据切分成相等大小的四分位数:

In [134]: arr = np.random.randn(30)

In [135]: factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])

In [136]: factor
Out[136]: 
[(0.569, 1.184], (-2.278, -0.301], (-2.278, -0.301], (0.569, 1.184], (0.569, 1.184], ..., (-0.301, 0.569], (1.184, 2.346], (1.184, 2.346], (-0.301, 0.569], (-2.278, -0.301]]
Length: 30
Categories (4, interval[float64, right]): [(-2.278, -0.301] < (-0.301, 0.569] < (0.569, 1.184] <
 (1.184, 2.346]] 

我们还可以传入无限值来定义区间:

In [137]: arr = np.random.randn(20)

In [138]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [139]: factor
Out[139]: 
[(-inf, 0.0], (0.0, inf], (0.0, inf], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (0.0, inf], (0.0, inf]]
Length: 20
Categories (2, interval[float64, right]): [(-inf, 0.0] < (0.0, inf]] 

函数应用

要将您自己或其他库的函数应用于 pandas 对象,您应该了解下面的三种方法。要使用的适当方法取决于您的函数是否希望在整个DataFrameSeries、按行或按列,或按元素进行操作。

  1. 按表应用函数: pipe()

  2. 按行或列应用函数: apply()

  3. 聚合 API:agg()transform()

  4. 应用逐元素函数:map()

表格级函数应用

DataFramesSeries 可以被传递给函数。但是,如果函数需要在链式调用中调用,请考虑使用 pipe() 方法。

首先进行一些设置:

In [140]: def extract_city_name(df):
 .....: """
 .....:    Chicago, IL -> Chicago for city_name column
 .....:    """
 .....:    df["city_name"] = df["city_and_code"].str.split(",").str.get(0)
 .....:    return df
 .....: 

In [141]: def add_country_name(df, country_name=None):
 .....: """
 .....:    Chicago -> Chicago-US for city_name column
 .....:    """
 .....:    col = "city_name"
 .....:    df["city_and_country"] = df[col] + country_name
 .....:    return df
 .....: 

In [142]: df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]}) 

extract_city_nameadd_country_name 是接受并返回 DataFrames 的函数。

现在比较以下内容:

In [143]: add_country_name(extract_city_name(df_p), country_name="US")
Out[143]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

等同于:

In [144]: df_p.pipe(extract_city_name).pipe(add_country_name, country_name="US")
Out[144]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

pandas 鼓励第二种风格,即称为方法链。pipe 可以轻松地在方法链中使用您自己或另一个库的函数,与 pandas 的方法并列使用。

在上面的示例中,函数 extract_city_nameadd_country_name 分别预期将 DataFrame 作为第一个位置参数。如果您希望应用的函数将其数据作为,例如,第二个参数呢?在这种情况下,提供一个 (callable, data_keyword) 元组给 pipe.pipe 将路由 DataFrame 到元组中指定的参数。

例如,我们可以使用 statsmodels 拟合回归。他们的 API 首先期望公式,然后是第二个参数 DataFrame,即 data。我们将函数、关键字对 (sm.ols, 'data') 传递给 pipe

In [147]: import statsmodels.formula.api as sm

In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")

In [149]: (
 .....:    bb.query("h > 0")
 .....:    .assign(ln_h=lambda df: np.log(df.h))
 .....:    .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
 .....:    .fit()
 .....:    .summary()
 .....: )
 .....:
Out[149]:
<class 'statsmodels.iolib.summary.Summary'>
"""
 OLS Regression Results
==============================================================================
Dep. Variable:                     hr   R-squared:                       0.685
Model:                            OLS   Adj. R-squared:                  0.665
Method:                 Least Squares   F-statistic:                     34.28
Date:                Tue, 22 Nov 2022   Prob (F-statistic):           3.48e-15
Time:                        05:34:17   Log-Likelihood:                -205.92
No. Observations:                  68   AIC:                             421.8
Df Residuals:                      63   BIC:                             432.9
Df Model:                           4
Covariance Type:            nonrobust
===============================================================================
 coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept   -8484.7720   4664.146     -1.819      0.074   -1.78e+04     835.780
C(lg)[T.NL]    -2.2736      1.325     -1.716      0.091      -4.922       0.375
ln_h           -1.3542      0.875     -1.547      0.127      -3.103       0.395
year            4.2277      2.324      1.819      0.074      -0.417       8.872
g               0.1841      0.029      6.258      0.000       0.125       0.243
==============================================================================
Omnibus:                       10.875   Durbin-Watson:                   1.999
Prob(Omnibus):                  0.004   Jarque-Bera (JB):               17.298
Skew:                           0.537   Prob(JB):                     0.000175
Kurtosis:                       5.225   Cond. No.                     1.49e+07
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
""" 

pipe 方法受 Unix 管道的启发,最近也受到了 dplyrmagrittr 的影响,它们引入了流行的 (%>%)(读作 pipe)运算符用于 R。这里的 pipe 实现非常简洁,并且在 Python 中感觉很合适。我们鼓励您查看 pipe() 的源代码。

按行或按列应用函数

可以使用 apply() 方法沿着 DataFrame 的轴应用任意函数,与描述性统计方法一样,它接受一个可选的 axis 参数:

In [145]: df.apply(lambda x: np.mean(x))
Out[145]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [146]: df.apply(lambda x: np.mean(x), axis=1)
Out[146]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

In [147]: df.apply(lambda x: x.max() - x.min())
Out[147]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

In [148]: df.apply(np.cumsum)
Out[148]: 
 one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873

In [149]: df.apply(np.exp)
Out[149]: 
 one       two     three
a  4.034899  5.885648       NaN
b  1.409244  6.767440  0.950858
c  2.004201  4.385785  3.412466
d       NaN  1.322262  0.541630 

apply() 方法也会根据字符串方法名称进行调度。

In [150]: df.apply("mean")
Out[150]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [151]: df.apply("mean", axis=1)
Out[151]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64 

传递给 apply() 的函数的返回类型会影响 DataFrame.apply 的最终输出类型,默认行为如下:

  • 如果应用的函数返回一个 Series,最终输出是一个 DataFrame。列与应用函数返回的 Series 的索引匹配。

  • 如果应用的函数返回任何其他类型,最终输出是一个Series

可以使用result_type覆盖此默认行为,它接受三个选项:reducebroadcastexpand。这将决定类似列表的返回值如何扩展(或不扩展)为一个DataFrame

apply()结合一些巧妙的技巧可以用来回答关于数据集的许多问题。例如,假设我们想要提取每列的最大值发生的日期:

In [152]: tsdf = pd.DataFrame(
 .....:    np.random.randn(1000, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=1000),
 .....: )
 .....: 

In [153]: tsdf.apply(lambda x: x.idxmax())
Out[153]: 
A   2000-08-06
B   2001-01-18
C   2001-07-18
dtype: datetime64[ns] 

您还可以向apply()方法传递其他参数和关键字参数。

In [154]: def subtract_and_divide(x, sub, divide=1):
 .....:    return (x - sub) / divide
 .....: 

In [155]: df_udf = pd.DataFrame(np.ones((2, 2)))

In [156]: df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]: 
 0         1
0 -1.333333 -1.333333
1 -1.333333 -1.333333 

另一个有用的功能是能够传递 Series 方法来对每列或每行执行一些 Series 操作:

In [157]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [158]: tsdf.iloc[3:7] = np.nan

In [159]: tsdf
Out[159]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950

In [160]: tsdf.apply(pd.Series.interpolate)
Out[160]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 -1.098598 -0.889659  0.092225
2000-01-05 -0.987349 -0.622526  0.321243
2000-01-06 -0.876100 -0.355392  0.550262
2000-01-07 -0.764851 -0.088259  0.779280
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950 

最后,apply()接受一个默认为 False 的参数raw,在应用函数之前将每行或每列转换为一个 Series。当设置为 True 时,传递的函数将接收一个 ndarray 对象,如果您不需要索引功能,则具有积极的性能影响。

聚合 API

聚合 API 允许以一种简洁的方式表达可能的多个聚合操作。这个 API 在 pandas 对象中是相似的,参见 groupby API、window API 和 resample API。聚合的入口点是DataFrame.aggregate(),或别名DataFrame.agg()

我们将使用与上面类似的起始框架:

In [161]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [162]: tsdf.iloc[3:7] = np.nan

In [163]: tsdf
Out[163]: 
 A         B         C
2000-01-01  1.257606  1.004194  0.167574
2000-01-02 -0.749892  0.288112 -0.757304
2000-01-03 -0.207550 -0.298599  0.116018
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.814347 -0.257623  0.869226
2000-01-09 -0.250663 -1.206601  0.896839
2000-01-10  2.169758 -1.333363  0.283157 

使用单个函数等同于apply()。您还可以将命名方法作为字符串传递。这些将返回聚合输出的Series

In [164]: tsdf.agg(lambda x: np.sum(x))
Out[164]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

In [165]: tsdf.agg("sum")
Out[165]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

# these are equivalent to a ``.sum()`` because we are aggregating
# on a single function
In [166]: tsdf.sum()
Out[166]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64 

Series进行单个聚合将返回一个标量值:

In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146 

使用多个函数进行聚合

您可以将多个聚合参数作为列表传递。每个传递函数的结果将成为结果DataFrame中的一行。这些自然地从聚合函数命名。

In [168]: tsdf.agg(["sum"])
Out[168]: 
 A         B        C
sum  3.033606 -1.803879  1.57551 

多个函数产生多行:

In [169]: tsdf.agg(["sum", "mean"])
Out[169]: 
 A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585 

Series上,多个函数返回一个由函数名称索引的Series

In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64 

传递一个lambda函数将产生一个名为<lambda>的行:

In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64 

传递一个命名函数将为该行产生该名称:

In [172]: def mymean(x):
 .....:    return x.mean()
 .....: 

In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64 

使用字典进行聚合

将列名的字典传递给标量或标量列表,以便将它们传递给 DataFrame.agg,允许您自定义将哪些函数应用于哪些列。请注意,结果不以任何特定顺序排列,您可以使用 OrderedDict 来保证顺序。

In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64 

传递类似列表的将生成一个 DataFrame 输出。您将获得所有聚合器的矩阵式输出。输出将由所有唯一的函数组成。那些未在特定列中注意到的将为 NaN

In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]: 
 A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879 

自定义描述

使用 .agg() 可以轻松创建自定义描述函数,类似于内置的描述函数。

In [176]: from functools import partial

In [177]: q_25 = partial(pd.Series.quantile, q=0.25)

In [178]: q_25.__name__ = "25%"

In [179]: q_75 = partial(pd.Series.quantile, q=0.75)

In [180]: q_75.__name__ = "75%"

In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]: 
 A         B         C
count   6.000000  6.000000  6.000000
mean    0.505601 -0.300647  0.262585
std     1.103362  0.887508  0.606860
min    -0.749892 -1.333363 -0.757304
25%    -0.239885 -0.979600  0.128907
median  0.303398 -0.278111  0.225365
75%     1.146791  0.151678  0.722709
max     2.169758  1.004194  0.896839 
```  ### Transform API

`transform()` 方法返回一个与原始索引相同(大小相同)的对象。此 API 允许您一次提供*多个*操作,而不是一个接一个地提供。它的 API 与 `.agg` API 非常相似。

我们创建了一个类似于上述部分中使用的框架。

```py
In [182]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [183]: tsdf.iloc[3:7] = np.nan

In [184]: tsdf
Out[184]: 
 A         B         C
2000-01-01 -0.428759 -0.864890 -0.675341
2000-01-02 -0.168731  1.338144 -1.279321
2000-01-03 -1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -1.240447 -0.201052
2000-01-09 -0.157795  0.791197 -1.144209
2000-01-10 -0.030876  0.371900  0.061932 

对整个框架进行转换。.transform() 允许输入函数为:NumPy 函数、字符串函数名称或用户定义的函数。

In [185]: tsdf.transform(np.abs)
Out[185]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [186]: tsdf.transform("abs")
Out[186]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [187]: tsdf.transform(lambda x: x.abs())
Out[187]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

这里 transform() 接收了一个函数;这相当于应用 ufunc

In [188]: np.abs(tsdf)
Out[188]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

将单个函数传递给 .transform()Series 将产生一个返回的单个 Series

In [189]: tsdf["A"].transform(np.abs)
Out[189]: 
2000-01-01    0.428759
2000-01-02    0.168731
2000-01-03    1.621034
2000-01-04         NaN
2000-01-05         NaN
2000-01-06         NaN
2000-01-07         NaN
2000-01-08    0.254374
2000-01-09    0.157795
2000-01-10    0.030876
Freq: D, Name: A, dtype: float64 

使用多个函数进行转换

传递多个函数将生成一个列 MultiIndexed DataFrame。第一级将是原始框架的列名;第二级将是转换函数的名称。

In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]: 
 A                   B                   C 
 absolute  <lambda>  absolute  <lambda>  absolute  <lambda>
2000-01-01  0.428759  0.571241  0.864890  0.135110  0.675341  0.324659
2000-01-02  0.168731  0.831269  1.338144  2.338144  1.279321 -0.279321
2000-01-03  1.621034 -0.621034  0.438107  1.438107  0.903794  1.903794
2000-01-04       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-08  0.254374  1.254374  1.240447 -0.240447  0.201052  0.798948
2000-01-09  0.157795  0.842205  0.791197  1.791197  1.144209 -0.144209
2000-01-10  0.030876  0.969124  0.371900  1.371900  0.061932  1.061932 

将多个函数传递给 Series 将产生一个 DataFrame。结果的列名将是转换函数。

In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]: 
 absolute  <lambda>
2000-01-01  0.428759  0.571241
2000-01-02  0.168731  0.831269
2000-01-03  1.621034 -0.621034
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374  1.254374
2000-01-09  0.157795  0.842205
2000-01-10  0.030876  0.969124 

使用字典进行转换

传递一个函数字典将允许按列进行选择性转换。

In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]: 
 A         B
2000-01-01  0.428759  0.135110
2000-01-02  0.168731  2.338144
2000-01-03  1.621034  1.438107
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374 -0.240447
2000-01-09  0.157795  1.791197
2000-01-10  0.030876  1.371900 

传递函数列表的字典将生成一个具有这些选择性转换的 MultiIndexed DataFrame。

In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]: 
 A         B 
 absolute  <lambda>      sqrt
2000-01-01  0.428759  0.135110       NaN
2000-01-02  0.168731  2.338144  1.156782
2000-01-03  1.621034  1.438107  0.661897
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -0.240447       NaN
2000-01-09  0.157795  1.791197  0.889493
2000-01-10  0.030876  1.371900  0.609836 
```  ### 逐元素应用函数

由于并非所有函数都可以进行矢量化(接受 NumPy 数组并返回另一个数组或值),因此 DataFrame 上的方法 `map()` 和类似地 Series 上的方法 `map()` 接受任何 Python 函数,该函数接受一个值并返回一个值。例如:

```py
In [194]: df4 = df.copy()

In [195]: df4
Out[195]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [196]: def f(x):
 .....:    return len(str(x))
 .....: 

In [197]: df4["one"].map(f)
Out[197]: 
a    18
b    19
c    18
d     3
Name: one, dtype: int64

In [198]: df4.map(f)
Out[198]: 
 one  two  three
a   18   17      3
b   19   18     20
c   18   18     16
d    3   19     19 

Series.map() 还有一个附加功能;它可以用于轻松“链接”或“映射”由辅助序列定义的值。这与合并/连接功能密切相关:

In [199]: s = pd.Series(
 .....:    ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
 .....: )
 .....: 

In [200]: t = pd.Series({"six": 6.0, "seven": 7.0})

In [201]: s
Out[201]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

In [202]: s.map(t)
Out[202]: 
a    6.0
b    7.0
c    6.0
d    7.0
e    6.0
dtype: float64 
```  ### 表格级别的函数应用

可以将 `DataFrames` 和 `Series` 传递给函数。但是,如果函数需要在链中调用,请考虑使用 `pipe()` 方法。

首先进行一些设置:

```py
In [140]: def extract_city_name(df):
 .....: """
 .....:    Chicago, IL -> Chicago for city_name column
 .....:    """
 .....:    df["city_name"] = df["city_and_code"].str.split(",").str.get(0)
 .....:    return df
 .....: 

In [141]: def add_country_name(df, country_name=None):
 .....: """
 .....:    Chicago -> Chicago-US for city_name column
 .....:    """
 .....:    col = "city_name"
 .....:    df["city_and_country"] = df[col] + country_name
 .....:    return df
 .....: 

In [142]: df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]}) 

extract_city_nameadd_country_name 是接受并返回 DataFrames 的函数。

现在比较以下内容:

In [143]: add_country_name(extract_city_name(df_p), country_name="US")
Out[143]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

等同于:

In [144]: df_p.pipe(extract_city_name).pipe(add_country_name, country_name="US")
Out[144]: 
 city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS 

pandas 鼓励第二种风格,即方法链。pipe 让您可以在方法链中轻松使用自己或另一个库的函数,与 pandas 的方法一起使用。

在上面的示例中,函数 extract_city_nameadd_country_name 分别期望 DataFrame 作为第一个位置参数。如果您希望应用的函数将其数据作为,比如说,第二个参数呢?在这种情况下,提供一个元组 (callable, data_keyword)pipe.pipe 将把 DataFrame 路由到元组中指定的参数。

例如,我们可以使用 statsmodels 进行回归拟合。他们的 API 首先期望一个公式,然后是第二个参数 dataDataFrame。我们将函数、关键字对 (sm.ols, 'data') 传递给 pipe

In [147]: import statsmodels.formula.api as sm

In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")

In [149]: (
 .....:    bb.query("h > 0")
 .....:    .assign(ln_h=lambda df: np.log(df.h))
 .....:    .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
 .....:    .fit()
 .....:    .summary()
 .....: )
 .....:
Out[149]:
<class 'statsmodels.iolib.summary.Summary'>
"""
 OLS Regression Results
==============================================================================
Dep. Variable:                     hr   R-squared:                       0.685
Model:                            OLS   Adj. R-squared:                  0.665
Method:                 Least Squares   F-statistic:                     34.28
Date:                Tue, 22 Nov 2022   Prob (F-statistic):           3.48e-15
Time:                        05:34:17   Log-Likelihood:                -205.92
No. Observations:                  68   AIC:                             421.8
Df Residuals:                      63   BIC:                             432.9
Df Model:                           4
Covariance Type:            nonrobust
===============================================================================
 coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept   -8484.7720   4664.146     -1.819      0.074   -1.78e+04     835.780
C(lg)[T.NL]    -2.2736      1.325     -1.716      0.091      -4.922       0.375
ln_h           -1.3542      0.875     -1.547      0.127      -3.103       0.395
year            4.2277      2.324      1.819      0.074      -0.417       8.872
g               0.1841      0.029      6.258      0.000       0.125       0.243
==============================================================================
Omnibus:                       10.875   Durbin-Watson:                   1.999
Prob(Omnibus):                  0.004   Jarque-Bera (JB):               17.298
Skew:                           0.537   Prob(JB):                     0.000175
Kurtosis:                       5.225   Cond. No.                     1.49e+07
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
""" 

管道方法受到 Unix 管道以及最近的 dplyrmagrittr 的启发,它们引入了流行的 (%>%)(读取管道)操作符用于 R。这里的 pipe 实现非常干净,感觉就像在 Python 中本来就应该有的。我们鼓励您查看 pipe() 的源代码。

行或列的函数应用

可以使用 apply() 方法沿着 DataFrame 的轴应用任意函数,该方法与描述性统计方法类似,都接受一个可选的 axis 参数:

In [145]: df.apply(lambda x: np.mean(x))
Out[145]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [146]: df.apply(lambda x: np.mean(x), axis=1)
Out[146]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

In [147]: df.apply(lambda x: x.max() - x.min())
Out[147]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

In [148]: df.apply(np.cumsum)
Out[148]: 
 one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873

In [149]: df.apply(np.exp)
Out[149]: 
 one       two     three
a  4.034899  5.885648       NaN
b  1.409244  6.767440  0.950858
c  2.004201  4.385785  3.412466
d       NaN  1.322262  0.541630 

apply() 方法还可以根据字符串方法名进行分派。

In [150]: df.apply("mean")
Out[150]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [151]: df.apply("mean", axis=1)
Out[151]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64 

传递给 apply() 的函数的返回类型会影响默认行为下 DataFrame.apply 的最终输出类型:

  • 如果应用的函数返回一个 Series,最终输出是一个 DataFrame。列匹配应用函数返回的 Series 的索引。

  • 如果应用的函数返回其他任何类型,则最终输出是一个 Series

可以使用 result_type 覆盖此默认行为,它接受三个选项:reducebroadcastexpand。这些选项将决定类似列表的返回值如何扩展(或不扩展)为 DataFrame

apply()结合一些巧妙的方法可以回答关于数据集的许多问题。例如,假设我们想要提取每列中最大值出现的日期:

In [152]: tsdf = pd.DataFrame(
 .....:    np.random.randn(1000, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=1000),
 .....: )
 .....: 

In [153]: tsdf.apply(lambda x: x.idxmax())
Out[153]: 
A   2000-08-06
B   2001-01-18
C   2001-07-18
dtype: datetime64[ns] 

您还可以将额外的参数和关键字参数传递给apply()方法。

In [154]: def subtract_and_divide(x, sub, divide=1):
 .....:    return (x - sub) / divide
 .....: 

In [155]: df_udf = pd.DataFrame(np.ones((2, 2)))

In [156]: df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]: 
 0         1
0 -1.333333 -1.333333
1 -1.333333 -1.333333 

另一个有用的功能是能够传递 Series 方法来对每列或每行执行一些 Series 操作:

In [157]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [158]: tsdf.iloc[3:7] = np.nan

In [159]: tsdf
Out[159]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950

In [160]: tsdf.apply(pd.Series.interpolate)
Out[160]: 
 A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 -1.098598 -0.889659  0.092225
2000-01-05 -0.987349 -0.622526  0.321243
2000-01-06 -0.876100 -0.355392  0.550262
2000-01-07 -0.764851 -0.088259  0.779280
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950 

最后,apply()接受一个默认为 False 的参数raw,在应用函数之前将每行或每列转换为一个 Series。当设置为 True 时,传递的函数将收到一个 ndarray 对象,如果您不需要索引功能,则具有积极的性能影响。

聚合 API

聚合 API 允许以一种简洁的方式表达可能的多个聚合操作。这个 API 在 pandas 对象中是相似的,参见 groupby API,window API,以及 resample API。聚合的入口点是DataFrame.aggregate(),或别名DataFrame.agg()

我们将使用与上面类似的起始框架:

In [161]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [162]: tsdf.iloc[3:7] = np.nan

In [163]: tsdf
Out[163]: 
 A         B         C
2000-01-01  1.257606  1.004194  0.167574
2000-01-02 -0.749892  0.288112 -0.757304
2000-01-03 -0.207550 -0.298599  0.116018
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.814347 -0.257623  0.869226
2000-01-09 -0.250663 -1.206601  0.896839
2000-01-10  2.169758 -1.333363  0.283157 

使用单个函数等同于apply()。您还可以将命名方法作为字符串传递。这些将返回聚合输出的Series

In [164]: tsdf.agg(lambda x: np.sum(x))
Out[164]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

In [165]: tsdf.agg("sum")
Out[165]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

# these are equivalent to a ``.sum()`` because we are aggregating
# on a single function
In [166]: tsdf.sum()
Out[166]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64 

Series进行单个聚合将返回一个标量值:

In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146 

使用多个函数进行聚合

您可以将多个聚合参数作为列表传递。每个传递函数的结果将成为结果DataFrame中的一行。这些自然地从聚合函数命名。

In [168]: tsdf.agg(["sum"])
Out[168]: 
 A         B        C
sum  3.033606 -1.803879  1.57551 

多个函数产生多个行:

In [169]: tsdf.agg(["sum", "mean"])
Out[169]: 
 A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585 

Series上,多个函数返回一个Series,由函数名称索引:

In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64 

传递一个lambda函数将产生一个<lambda>命名行:

In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64 

传递一个命名函数将为该行产生该名称:

In [172]: def mymean(x):
 .....:    return x.mean()
 .....: 

In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64 

使用字典进行聚合

将列名的字典传递给标量或标量列表,以便DataFrame.agg允许您自定义应用于哪些列的函数。请注意,结果不按任何特定顺序排列,您可以使用OrderedDict来保证顺序。

In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64 

传递类似列表将生成一个DataFrame输出。您将获得所有聚合器的类似矩阵的输出。输出将包含所有唯一的函数。那些没有针对特定列指定的函数将是NaN

In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]: 
 A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879 

自定义描述

使用 .agg() 可以轻松创建自定义描述函数,类似于内置的描述函数。

In [176]: from functools import partial

In [177]: q_25 = partial(pd.Series.quantile, q=0.25)

In [178]: q_25.__name__ = "25%"

In [179]: q_75 = partial(pd.Series.quantile, q=0.75)

In [180]: q_75.__name__ = "75%"

In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]: 
 A         B         C
count   6.000000  6.000000  6.000000
mean    0.505601 -0.300647  0.262585
std     1.103362  0.887508  0.606860
min    -0.749892 -1.333363 -0.757304
25%    -0.239885 -0.979600  0.128907
median  0.303398 -0.278111  0.225365
75%     1.146791  0.151678  0.722709
max     2.169758  1.004194  0.896839 

使用多个函数进行聚合

您可以将多个聚合参数作为列表传递。每个传递函数的结果将成为生成的 DataFrame 中的一行。这些自然地从聚合函数命名。

In [168]: tsdf.agg(["sum"])
Out[168]: 
 A         B        C
sum  3.033606 -1.803879  1.57551 

多个函数产生多行:

In [169]: tsdf.agg(["sum", "mean"])
Out[169]: 
 A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585 

对于 Series,多个函数返回一个由函数名称索引的 Series

In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64 

传递 lambda 函数将产生一个命名为 <lambda> 的行:

In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64 

传递命名函数将产生该行的名称:

In [172]: def mymean(x):
 .....:    return x.mean()
 .....: 

In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64 

使用字典进行聚合

将列名称的字典传递给标量或标量列表,以便 DataFrame.agg 允许您自定义应用于哪些列的函数。请注意,结果没有任何特定顺序,您可以改用 OrderedDict 以保证顺序。

In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64 

传递类似列表将生成一个 DataFrame 输出。您将获得所有聚合器的矩阵样式输出。输出将由所有唯一函数组成。那些未特定于特定列的函数将是 NaN

In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]: 
 A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879 

自定义描述

使用 .agg() 可以轻松创建自定义描述函数,类似于内置的描述函数。

In [176]: from functools import partial

In [177]: q_25 = partial(pd.Series.quantile, q=0.25)

In [178]: q_25.__name__ = "25%"

In [179]: q_75 = partial(pd.Series.quantile, q=0.75)

In [180]: q_75.__name__ = "75%"

In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]: 
 A         B         C
count   6.000000  6.000000  6.000000
mean    0.505601 -0.300647  0.262585
std     1.103362  0.887508  0.606860
min    -0.749892 -1.333363 -0.757304
25%    -0.239885 -0.979600  0.128907
median  0.303398 -0.278111  0.225365
75%     1.146791  0.151678  0.722709
max     2.169758  1.004194  0.896839 

转换 API

transform() 方法返回一个与原始对象(大小相同)索引相同的对象。该 API 允许您一次性提供多个操作,而不是一个接一个的操作。其 API 与 .agg API 非常相似。

我们创建了一个类似于上述部分中使用的框架。

In [182]: tsdf = pd.DataFrame(
 .....:    np.random.randn(10, 3),
 .....:    columns=["A", "B", "C"],
 .....:    index=pd.date_range("1/1/2000", periods=10),
 .....: )
 .....: 

In [183]: tsdf.iloc[3:7] = np.nan

In [184]: tsdf
Out[184]: 
 A         B         C
2000-01-01 -0.428759 -0.864890 -0.675341
2000-01-02 -0.168731  1.338144 -1.279321
2000-01-03 -1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -1.240447 -0.201052
2000-01-09 -0.157795  0.791197 -1.144209
2000-01-10 -0.030876  0.371900  0.061932 

转换整个框架。.transform() 允许输入函数为:NumPy 函数、字符串函数名称或用户定义的函数。

In [185]: tsdf.transform(np.abs)
Out[185]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [186]: tsdf.transform("abs")
Out[186]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [187]: tsdf.transform(lambda x: x.abs())
Out[187]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

这里 transform() 接收到一个单个函数;这相当于应用 ufunc

In [188]: np.abs(tsdf)
Out[188]: 
 A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932 

将单个函数传递给 .transform()Series 将返回单个 Series

In [189]: tsdf["A"].transform(np.abs)
Out[189]: 
2000-01-01    0.428759
2000-01-02    0.168731
2000-01-03    1.621034
2000-01-04         NaN
2000-01-05         NaN
2000-01-06         NaN
2000-01-07         NaN
2000-01-08    0.254374
2000-01-09    0.157795
2000-01-10    0.030876
Freq: D, Name: A, dtype: float64 

使用多个函数进行转换

传递多个函数将产生一个列 MultiIndexed DataFrame。第一级将是原始框架列名称;第二级将是转换函数的名称。

In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]: 
 A                   B                   C 
 absolute  <lambda>  absolute  <lambda>  absolute  <lambda>
2000-01-01  0.428759  0.571241  0.864890  0.135110  0.675341  0.324659
2000-01-02  0.168731  0.831269  1.338144  2.338144  1.279321 -0.279321
2000-01-03  1.621034 -0.621034  0.438107  1.438107  0.903794  1.903794
2000-01-04       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-08  0.254374  1.254374  1.240447 -0.240447  0.201052  0.798948
2000-01-09  0.157795  0.842205  0.791197  1.791197  1.144209 -0.144209
2000-01-10  0.030876  0.969124  0.371900  1.371900  0.061932  1.061932 

对 Series 传递多个函数将产生一个 DataFrame。生成的列名称将是转换函数。

In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]: 
 absolute  <lambda>
2000-01-01  0.428759  0.571241
2000-01-02  0.168731  0.831269
2000-01-03  1.621034 -0.621034
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374  1.254374
2000-01-09  0.157795  0.842205
2000-01-10  0.030876  0.969124 

使用字典进行转换

传递函数字典将允许按列选择性转换。

In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]: 
 A         B
2000-01-01  0.428759  0.135110
2000-01-02  0.168731  2.338144
2000-01-03  1.621034  1.438107
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374 -0.240447
2000-01-09  0.157795  1.791197
2000-01-10  0.030876  1.371900 

传递列表的字典将生成一个具有这些选择性转换的 MultiIndexed DataFrame。

In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]: 
 A         B 
 absolute  <lambda>      sqrt
2000-01-01  0.428759  0.135110       NaN
2000-01-02  0.168731  2.338144  1.156782
2000-01-03  1.621034  1.438107  0.661897
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -0.240447       NaN
2000-01-09  0.157795  1.791197  0.889493
2000-01-10  0.030876  1.371900  0.609836 

使用多个函数进行转换

传递多个函数将生成一个列 MultiIndexed DataFrame。第一级将是原始帧列名;第二级将是变换函数的名称。

In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]: 
 A                   B                   C 
 absolute  <lambda>  absolute  <lambda>  absolute  <lambda>
2000-01-01  0.428759  0.571241  0.864890  0.135110  0.675341  0.324659
2000-01-02  0.168731  0.831269  1.338144  2.338144  1.279321 -0.279321
2000-01-03  1.621034 -0.621034  0.438107  1.438107  0.903794  1.903794
2000-01-04       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-08  0.254374  1.254374  1.240447 -0.240447  0.201052  0.798948
2000-01-09  0.157795  0.842205  0.791197  1.791197  1.144209 -0.144209
2000-01-10  0.030876  0.969124  0.371900  1.371900  0.061932  1.061932 

传递多个函数给一个 Series 将产生一个 DataFrame。结果列名将是变换函数。

In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]: 
 absolute  <lambda>
2000-01-01  0.428759  0.571241
2000-01-02  0.168731  0.831269
2000-01-03  1.621034 -0.621034
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374  1.254374
2000-01-09  0.157795  0.842205
2000-01-10  0.030876  0.969124 

使用字典进行转换

传递一个函数的字典将允许按列进行选择性变换。

In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]: 
 A         B
2000-01-01  0.428759  0.135110
2000-01-02  0.168731  2.338144
2000-01-03  1.621034  1.438107
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374 -0.240447
2000-01-09  0.157795  1.791197
2000-01-10  0.030876  1.371900 

传递一个列表的字典将生成一个具有这些选择性转换的 MultiIndexed DataFrame。

In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]: 
 A         B 
 absolute  <lambda>      sqrt
2000-01-01  0.428759  0.135110       NaN
2000-01-02  0.168731  2.338144  1.156782
2000-01-03  1.621034  1.438107  0.661897
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -0.240447       NaN
2000-01-09  0.157795  1.791197  0.889493
2000-01-10  0.030876  1.371900  0.609836 

应用逐元素函数

由于并非所有函数都可以矢量化(接受 NumPy 数组并返回另一个数组或值),因此 DataFrame 上的方法 map() 和类似地 Series 上的 map() 接受任何接受单个值并返回单个值的 Python 函数。例如:

In [194]: df4 = df.copy()

In [195]: df4
Out[195]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [196]: def f(x):
 .....:    return len(str(x))
 .....: 

In [197]: df4["one"].map(f)
Out[197]: 
a    18
b    19
c    18
d     3
Name: one, dtype: int64

In [198]: df4.map(f)
Out[198]: 
 one  two  three
a   18   17      3
b   19   18     20
c   18   18     16
d    3   19     19 

Series.map() 还具有额外的功能;它可以用于轻松“链接”或“映射”由次级系列定义的值。这与合并/连接功能密切相关:

In [199]: s = pd.Series(
 .....:    ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
 .....: )
 .....: 

In [200]: t = pd.Series({"six": 6.0, "seven": 7.0})

In [201]: s
Out[201]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

In [202]: s.map(t)
Out[202]: 
a    6.0
b    7.0
c    6.0
d    7.0
e    6.0
dtype: float64 

重新索引和更改标签

reindex() 是 pandas 中的基本数据对齐方法。它用于实现几乎所有依赖标签对齐功能的其他功能。重新索引意味着使数据符合与特定轴上的给定标签集匹配的数据。这可以实现几个目标:

  • 重新排列现有数据以匹配新的标签集

  • 在不存在该标签的标签位置插入缺失值(NA)标记

  • 如果指定了,可以使用逻辑填充缺失标签的数据(与处理时间序列数据高度相关)

这里是一个简单的例子:

In [203]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [204]: s
Out[204]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
e   -1.326508
dtype: float64

In [205]: s.reindex(["e", "b", "f", "d"])
Out[205]: 
e   -1.326508
b    1.328614
f         NaN
d   -0.385845
dtype: float64 

在这里,f 标签未包含在 Series 中,因此在结果中显示为 NaN

对于 DataFrame,您可以同时重新索引索引和列:

In [206]: df
Out[206]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [207]: df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
Out[207]: 
 three       two       one
c  1.227435  1.478369  0.695246
f       NaN       NaN       NaN
b -0.050390  1.912123  0.343054 

注意,包含实际轴标签的 Index 对象可以在对象之间共享。因此,如果我们有一个 Series 和一个 DataFrame,则可以执行以下操作:

In [208]: rs = s.reindex(df.index)

In [209]: rs
Out[209]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
dtype: float64

In [210]: rs.index is df.index
Out[210]: True 

这意味着重新索引的 Series 的索引与 DataFrame 的索引是相同的 Python 对象。

DataFrame.reindex() 还支持一种“轴样式”调用约定,其中您指定单个 labels 参数以及它适用的 axis

In [211]: df.reindex(["c", "f", "b"], axis="index")
Out[211]: 
 one       two     three
c  0.695246  1.478369  1.227435
f       NaN       NaN       NaN
b  0.343054  1.912123 -0.050390

In [212]: df.reindex(["three", "two", "one"], axis="columns")
Out[212]: 
 three       two       one
a       NaN  1.772517  1.394981
b -0.050390  1.912123  0.343054
c  1.227435  1.478369  0.695246
d -0.613172  0.279344       NaN 

另请参阅

多索引 / 高级索引 是进行重新索引的更简洁方式。

注意

在编写对性能敏感的代码时,有充分的理由花一些时间成为一个重新索引的忍者:许多操作在预对齐数据上更快。添加两个不对齐的 DataFrame 内部会触发重新索引步骤。对于探索性分析,你几乎不会注意到差异(因为reindex已经经过了大量优化),但是当 CPU 周期很重要时,偶尔在某些地方添加一些显式的reindex调用可能会产生影响。

重新索引以与另一个对象对齐

你可能希望取一个对象并重新索引其轴,使其标签与另一个对象相同。虽然这个操作的语法虽然冗长但简单,但它是一个常见的操作,因此reindex_like() 方法可用于简化此操作:

In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])

In [214]: df3 = df2 - df2.mean()

In [215]: df2
Out[215]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

In [216]: df3
Out[216]: 
 one       two
a  0.583888  0.051514
b -0.468040  0.191120
c -0.115848 -0.242634

In [217]: df.reindex_like(df2)
Out[217]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369 
```  ### 使用`align`将对象与其他对象对齐

`align()` 方法是同时对齐两个对象的最快方法。它支持一个`join`参数(与连接和合并相关):

> +   `join='outer'`:取索引的并集(默认)
> +   
> +   `join='left'`:使用调用对象的索引
> +   
> +   `join='right'`:使用传入对象的索引
> +   
> +   `join='inner'`:交集索引

返回一个包含重新索引的两个 Series 的元组:

```py
In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [219]: s1 = s[:4]

In [220]: s2 = s[1:]

In [221]: s1.align(s2)
Out[221]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e         NaN
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e    1.114285
 dtype: float64)

In [222]: s1.align(s2, join="inner")
Out[222]: 
(b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64)

In [223]: s1.align(s2, join="left")
Out[223]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64) 

对于 DataFrames,默认情况下,连接方法将应用于索引和列:

In [224]: df.align(df2, join="inner")
Out[224]: 
(        one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

你还可以传递一个axis选项,只在指定的轴上对齐:

In [225]: df.align(df2, join="inner", axis=0)
Out[225]: 
(        one       two     three
 a  1.394981  1.772517       NaN
 b  0.343054  1.912123 -0.050390
 c  0.695246  1.478369  1.227435,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

如果你将一个 Series 传递给DataFrame.align(),你可以选择使用axis参数在 DataFrame 的索引或列上同时对齐两个对象:

In [226]: df.align(df2.iloc[0], axis=1)
Out[226]: 
(        one     three       two
 a  1.394981       NaN  1.772517
 b  0.343054 -0.050390  1.912123
 c  0.695246  1.227435  1.478369
 d       NaN -0.613172  0.279344,
 one      1.394981
 three         NaN
 two      1.772517
 Name: a, dtype: float64) 
```  ### 重新索引时填充

`reindex()` 接受一个可选参数`method`,该参数是从以下表中选择的填充方法:

| 方法 | 动作 |
| --- | --- |
| pad / ffill | 向前填充值 |
| bfill / backfill | 向后填充值 |
| nearest | 从最近的索引值填充 |

我们在一个简单的 Series 上演示这些填充方法:

```py
In [227]: rng = pd.date_range("1/3/2000", periods=8)

In [228]: ts = pd.Series(np.random.randn(8), index=rng)

In [229]: ts2 = ts.iloc[[0, 3, 6]]

In [230]: ts
Out[230]: 
2000-01-03    0.183051
2000-01-04    0.400528
2000-01-05   -0.015083
2000-01-06    2.395489
2000-01-07    1.414806
2000-01-08    0.118428
2000-01-09    0.733639
2000-01-10   -0.936077
Freq: D, dtype: float64

In [231]: ts2
Out[231]: 
2000-01-03    0.183051
2000-01-06    2.395489
2000-01-09    0.733639
Freq: 3D, dtype: float64

In [232]: ts2.reindex(ts.index)
Out[232]: 
2000-01-03    0.183051
2000-01-04         NaN
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07         NaN
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]: 
2000-01-03    0.183051
2000-01-04    2.395489
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    0.733639
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

这些方法要求索引是有序递增或递减的。

请注意,使用 ffill(除了method='nearest')或 interpolate 也可以达到相同的结果:

In [236]: ts2.reindex(ts.index).ffill()
Out[236]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

当索引不是单调递增或递减时,reindex() 会引发 ValueError。fillna()interpolate() 不会对索引的顺序执行任何检查。### 重新索引时的填充限制

limittolerance 参数提供了在重新索引时填充的额外控制。Limit 指定连续匹配的最大计数:

In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

相反,容差指定索引和索引器值之间的最大距离:

In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

请注意,当在 DatetimeIndexTimedeltaIndexPeriodIndex 上使用时,如果可能,tolerance 将被强制转换为 Timedelta。这允许您使用适当的字符串指定容差。### 从轴中删除标签

reindex 密切相关的方法是 drop() 函数。它从轴中删除一组标签:

In [239]: df
Out[239]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [240]: df.drop(["a", "d"], axis=0)
Out[240]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435

In [241]: df.drop(["one"], axis=1)
Out[241]: 
 two     three
a  1.772517       NaN
b  1.912123 -0.050390
c  1.478369  1.227435
d  0.279344 -0.613172 

请注意,以下方法也有效,但不够明显/干净:

In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435 
```  ### 重命名 / 映射标签

`rename()` 方法允许您根据某些映射(字典或 Series)或任意函数重新标记轴。

```py
In [243]: s
Out[243]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
dtype: float64

In [244]: s.rename(str.upper)
Out[244]: 
A   -0.186646
B   -1.692424
C   -0.303893
D   -1.425662
E    1.114285
dtype: float64 

如果传递一个函数,当使用任何标签调用时,它必须返回一个值(并且必须生成一组唯一值)。也可以使用字典或 Series:

In [245]: df.rename(
 .....:    columns={"one": "foo", "two": "bar"},
 .....:    index={"a": "apple", "b": "banana", "d": "durian"},
 .....: )
 .....: 
Out[245]: 
 foo       bar     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

如果映射不包括列/索引标签,则不会重命名。请注意,映射中的额外标签不会引发错误。

DataFrame.rename() 还支持“轴样式”调用约定,您可以指定单个 mapper 和要应用该映射的 axis

In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]: 
 foo       bar     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]: 
 one       two     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

最后,rename() 还接受标量或类似列表以修改 Series.name 属性。

In [248]: s.rename("scalar-name")
Out[248]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
Name: scalar-name, dtype: float64 

DataFrame.rename_axis()Series.rename_axis() 方法允许更改 MultiIndex 的特定名称(而不是标签)。

In [249]: df = pd.DataFrame(
 .....:    {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
 .....:    index=pd.MultiIndex.from_product(
 .....:        [["a", "b", "c"], [1, 2]], names=["let", "num"]
 .....:    ),
 .....: )
 .....: 

In [250]: df
Out[250]: 
 x   y
let num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [251]: df.rename_axis(index={"let": "abc"})
Out[251]: 
 x   y
abc num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [252]: df.rename_axis(index=str.upper)
Out[252]: 
 x   y
LET NUM 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60 
```  ### 重新索引以与另一个对象对齐

你可能希望取一个对象,并将其轴重新索引为与另一个对象相同的标签。虽然这个语法是简单明了的,但它是一个常见的操作,`reindex_like()` 方法可用于简化这个操作:

```py
In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])

In [214]: df3 = df2 - df2.mean()

In [215]: df2
Out[215]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

In [216]: df3
Out[216]: 
 one       two
a  0.583888  0.051514
b -0.468040  0.191120
c -0.115848 -0.242634

In [217]: df.reindex_like(df2)
Out[217]: 
 one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369 

使用 align 将对象与其他对象对齐

align() 方法是同时对齐两个对象的最快方法。它支持一个 join 参数(与 joining and merging 相关):

  • join='outer':取索引的并集(默认)
  • join='left':使用调用对象的索引
  • join='right':使用传递对象的索引
  • join='inner':交集索引

它返回一个包含重新索引的两个 Series 的元组:

In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [219]: s1 = s[:4]

In [220]: s2 = s[1:]

In [221]: s1.align(s2)
Out[221]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e         NaN
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e    1.114285
 dtype: float64)

In [222]: s1.align(s2, join="inner")
Out[222]: 
(b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64)

In [223]: s1.align(s2, join="left")
Out[223]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64) 

对于 DataFrame,默认情况下 join 方法将应用于索引和列:

In [224]: df.align(df2, join="inner")
Out[224]: 
(        one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

你也可以传递一个 axis 选项,只在指定的轴上对齐:

In [225]: df.align(df2, join="inner", axis=0)
Out[225]: 
(        one       two     three
 a  1.394981  1.772517       NaN
 b  0.343054  1.912123 -0.050390
 c  0.695246  1.478369  1.227435,
 one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369) 

如果你将一个 Series 传递给DataFrame.align(),你可以选择使用 axis 参数来对齐 DataFrame 的索引或列:

In [226]: df.align(df2.iloc[0], axis=1)
Out[226]: 
(        one     three       two
 a  1.394981       NaN  1.772517
 b  0.343054 -0.050390  1.912123
 c  0.695246  1.227435  1.478369
 d       NaN -0.613172  0.279344,
 one      1.394981
 three         NaN
 two      1.772517
 Name: a, dtype: float64) 

重新索引时进行填充

reindex() 接受一个可选参数 method,这是从以下表中选择的填充方法:

方法 动作
pad / ffill 向前填充值
bfill / backfill 向后填充值
nearest 从最近的索引值填充

我们在一个简单的 Series 上说明这些填充方法:

In [227]: rng = pd.date_range("1/3/2000", periods=8)

In [228]: ts = pd.Series(np.random.randn(8), index=rng)

In [229]: ts2 = ts.iloc[[0, 3, 6]]

In [230]: ts
Out[230]: 
2000-01-03    0.183051
2000-01-04    0.400528
2000-01-05   -0.015083
2000-01-06    2.395489
2000-01-07    1.414806
2000-01-08    0.118428
2000-01-09    0.733639
2000-01-10   -0.936077
Freq: D, dtype: float64

In [231]: ts2
Out[231]: 
2000-01-03    0.183051
2000-01-06    2.395489
2000-01-09    0.733639
Freq: 3D, dtype: float64

In [232]: ts2.reindex(ts.index)
Out[232]: 
2000-01-03    0.183051
2000-01-04         NaN
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07         NaN
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]: 
2000-01-03    0.183051
2000-01-04    2.395489
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    0.733639
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

这些方法要求索引是有序的,递增或递减的。

注意,可以使用 ffill(除了 method='nearest')或 interpolate 来实现相同的结果:

In [236]: ts2.reindex(ts.index).ffill()
Out[236]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

当索引不是单调递增或递减时,reindex() 会引发 ValueError。fillna()interpolate() 不会对索引的顺序进行任何检查。

重新索引时填充的限制

limittolerance 参数提供了在重新索引时填充的额外控制。Limit 指定连续匹配的最大计数:

In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

相反,容差指定索引和索引器值之间的最大距离:

In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64 

请注意,当用于 DatetimeIndexTimedeltaIndexPeriodIndex 时,如果可能的话,tolerance 会被转换为 Timedelta。这允许您使用适当的字符串指定容差。

从轴中删除标签

reindex 密切相关的方法是 drop() 函数。它从轴中删除一组标签:

In [239]: df
Out[239]: 
 one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [240]: df.drop(["a", "d"], axis=0)
Out[240]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435

In [241]: df.drop(["one"], axis=1)
Out[241]: 
 two     three
a  1.772517       NaN
b  1.912123 -0.050390
c  1.478369  1.227435
d  0.279344 -0.613172 

注意,以下方法也有效,但不够明显/清晰:

In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]: 
 one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435 

重命名/映射标签

rename() 方法允许您基于一些映射(dict 或 Series)或任意函数重新标记轴。

In [243]: s
Out[243]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
dtype: float64

In [244]: s.rename(str.upper)
Out[244]: 
A   -0.186646
B   -1.692424
C   -0.303893
D   -1.425662
E    1.114285
dtype: float64 

如果传递一个函数,调用任何标签时必须返回一个值(并且必须产生一组唯一值)。也可以使用 dict 或 Series:

In [245]: df.rename(
 .....:    columns={"one": "foo", "two": "bar"},
 .....:    index={"a": "apple", "b": "banana", "d": "durian"},
 .....: )
 .....: 
Out[245]: 
 foo       bar     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

如果映射不包括列/索引标签,则不会重命名。请注意,映射中的额外标签不会引发错误。

DataFrame.rename() 还支持“轴样式”调用约定,您可以指定单个 mapper 和要应用该映射的 axis

In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]: 
 foo       bar     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]: 
 one       two     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172 

最后,rename() 也接受标量或类似列表以更改 Series.name 属性。

In [248]: s.rename("scalar-name")
Out[248]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
Name: scalar-name, dtype: float64 

方法 DataFrame.rename_axis()Series.rename_axis() 允许更改 MultiIndex 的特定名称(而不是标签)。

In [249]: df = pd.DataFrame(
 .....:    {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
 .....:    index=pd.MultiIndex.from_product(
 .....:        [["a", "b", "c"], [1, 2]], names=["let", "num"]
 .....:    ),
 .....: )
 .....: 

In [250]: df
Out[250]: 
 x   y
let num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [251]: df.rename_axis(index={"let": "abc"})
Out[251]: 
 x   y
abc num 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60

In [252]: df.rename_axis(index=str.upper)
Out[252]: 
 x   y
LET NUM 
a   1    1  10
 2    2  20
b   1    3  30
 2    4  40
c   1    5  50
 2    6  60 

迭代

对于 pandas 对象的基本迭代行为取决于类型。当迭代 Series 时,它被视为类似数组,基本迭代会产生数值。DataFrame 遵循字典的惯例,迭代对象的“键”。

简而言之,基本迭代(for i in object)会产生:

  • Series:数值

  • DataFrame:列标签

因此,例如,迭代 DataFrame 将为您提供列名:

In [253]: df = pd.DataFrame(
 .....:    {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
 .....: )
 .....: 

In [254]: for col in df:
 .....:    print(col)
 .....: 
col1
col2 

pandas 对象还具有类似字典的 items() 方法,用于迭代(键,值)对。

要迭代 DataFrame 的行,可以使用以下方法:

  • iterrows():将 DataFrame 的行作为(索引,Series)对进行迭代。这会将行转换为 Series 对象,这可能会改变 dtypes 并具有一些性能影响。

  • itertuples():将 DataFrame 的行作为命名元组的值进行迭代。这比iterrows()快得多,并且在大多数情况下,最好使用它来迭代 DataFrame 的值。

警告

通过 pandas 对象进行迭代通常较慢。在许多情况下,不需要手动遍历行,并且可以通过以下方法之一避免:

  • 寻找矢量化解决方案:许多操作可以使用内置方法或 NumPy 函数(布尔)索引等来执行,…

  • 当您有一个无法一次处理完整 DataFrame/Series 的函数时,最好使用apply()而不是迭代值。请参阅函数应用部分的文档。

  • 如果需要对值进行迭代操作但性能很重要,请考虑使用 cython 或 numba 编写内部循环。请参阅提高性能部分,了解一些此方法的示例。

警告

永远不应该修改您正在迭代的内容。这并不保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是视图,对其进行写入将不会产生任何效果!

例如,在以下情况中设置值没有效果:

In [255]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})

In [256]: for index, row in df.iterrows():
 .....:    row["a"] = 10
 .....: 

In [257]: df
Out[257]: 
 a  b
0  1  a
1  2  b
2  3  c 

items

与类似字典的接口一致,items()遍历键值对:

  • Series:(索引,标量值)对

  • DataFrame:(列,Series)对

例如:

In [258]: for label, ser in df.items():
 .....:    print(label)
 .....:    print(ser)
 .....: 
a
0    1
1    2
2    3
Name: a, dtype: int64
b
0    a
1    b
2    c
Name: b, dtype: object 

iterrows

iterrows()允许您将 DataFrame 的行作为 Series 对象进行迭代。它返回一个迭代器,产生每个索引值以及包含每行数据的 Series:

In [259]: for row_index, row in df.iterrows():
 .....:    print(row_index, row, sep="\n")
 .....: 
0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object 

注意

因为iterrows()为每行返回一个 Series,它不会在行之间保留数据类型(数据类型在 DataFrame 的列之间保留)。例如,

In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])

In [261]: df_orig.dtypes
Out[261]: 
int        int64
float    float64
dtype: object

In [262]: row = next(df_orig.iterrows())[1]

In [263]: row
Out[263]: 
int      1.0
float    1.5
Name: 0, dtype: float64 

row中的所有值,作为一个 Series 返回,现在都被转换为浮点数,包括列x中的原始整数值:

In [264]: row["int"].dtype
Out[264]: dtype('float64')

In [265]: df_orig["int"].dtype
Out[265]: dtype('int64') 

为了在迭代行时保留数据类型,最好使用itertuples(),它返回值的命名元组,通常比iterrows()快得多。

例如,转置 DataFrame 的一种构造方法是:

In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})

In [267]: print(df2)
 x  y
0  1  4
1  2  5
2  3  6

In [268]: print(df2.T)
 0  1  2
x  1  2  3
y  4  5  6

In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})

In [270]: print(df2_t)
 0  1  2
x  1  2  3
y  4  5  6 

itertuples

itertuples() 方法将返回一个迭代器,为 DataFrame 中的每一行生成一个命名元组。元组的第一个元素将是行的相应索引值,而其余值是行值。

例如:

In [271]: for row in df.itertuples():
 .....:    print(row)
 .....: 
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c') 

此方法不会将行转换为 Series 对象;它仅返回命名元组中的值。因此,itertuples() 保留值的数据类型,并且通常比iterrows() 快得多。

注意

如果列名是无效的 Python 标识符、重复的或以下划线开头,则列名将重命名为位置名称。如果列数较多(>255),则返回常规元组。

项目

与类似字典的接口一致,items() 迭代遍历键值对:

  • Series:(索引,标量值) 对

  • DataFrame:(列,Series) 对

例如:

In [258]: for label, ser in df.items():
 .....:    print(label)
 .....:    print(ser)
 .....: 
a
0    1
1    2
2    3
Name: a, dtype: int64
b
0    a
1    b
2    c
Name: b, dtype: object 

遍历行

iterrows() 允许您遍历 DataFrame 的行作为 Series 对象。它返回一个迭代器,每个索引值以及包含每行数据的 Series:

In [259]: for row_index, row in df.iterrows():
 .....:    print(row_index, row, sep="\n")
 .....: 
0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object 

注意

因为iterrows() 为每行返回一个 Series,它不会在行之间保留 dtype(对于 DataFrame,dtype 在列之间保留)。例如,

In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])

In [261]: df_orig.dtypes
Out[261]: 
int        int64
float    float64
dtype: object

In [262]: row = next(df_orig.iterrows())[1]

In [263]: row
Out[263]: 
int      1.0
float    1.5
Name: 0, dtype: float64 

返回为 Series 的row中的所有值现在都被转换为浮点数,包括列x中的原始整数值:

In [264]: row["int"].dtype
Out[264]: dtype('float64')

In [265]: df_orig["int"].dtype
Out[265]: dtype('int64') 

在遍历行时保留 dtypes,最好使用itertuples(),它返回值的命名元组,通常比iterrows()快得多。

例如,转置 DataFrame 的一种构造方法是:

In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})

In [267]: print(df2)
 x  y
0  1  4
1  2  5
2  3  6

In [268]: print(df2.T)
 0  1  2
x  1  2  3
y  4  5  6

In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})

In [270]: print(df2_t)
 0  1  2
x  1  2  3
y  4  5  6 

itertuples

itertuples() 方法将返回一个迭代器,为 DataFrame 中的每一行生成一个命名元组。元组的第一个元素将是行的相应索引值,而其余值是行值。

例如:

In [271]: for row in df.itertuples():
 .....:    print(row)
 .....: 
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c') 

此方法不会将行转换为 Series 对象;它只是返回命名元组内的值。因此,itertuples() 保留了值的数据类型,并且通常比 iterrows() 更快。

注意

如果列名无效的 Python 标识符、重复或以下划线开头,则列名将重命名为位置名称。当列数较多(>255)时,将返回常规元组。

.dt 访问器

如果 Series 是日期时间/周期类别的 Series,则 Series 具有一种访问器,以简洁地返回 Series的日期时间类属性。这将返回一个 Series,其索引类似于现有的 Series

# datetime
In [272]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))

In [273]: s
Out[273]: 
0   2013-01-01 09:10:12
1   2013-01-02 09:10:12
2   2013-01-03 09:10:12
3   2013-01-04 09:10:12
dtype: datetime64[ns]

In [274]: s.dt.hour
Out[274]: 
0    9
1    9
2    9
3    9
dtype: int32

In [275]: s.dt.second
Out[275]: 
0    12
1    12
2    12
3    12
dtype: int32

In [276]: s.dt.day
Out[276]: 
0    1
1    2
2    3
3    4
dtype: int32 

这使得可以像这样进行美观的表达:

In [277]: s[s.dt.day == 2]
Out[277]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns] 

您可以轻松地生成带时区的转换:

In [278]: stz = s.dt.tz_localize("US/Eastern")

In [279]: stz
Out[279]: 
0   2013-01-01 09:10:12-05:00
1   2013-01-02 09:10:12-05:00
2   2013-01-03 09:10:12-05:00
3   2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]

In [280]: stz.dt.tz
Out[280]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD> 

您还可以链接这些类型的操作:

In [281]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]: 
0   2013-01-01 04:10:12-05:00
1   2013-01-02 04:10:12-05:00
2   2013-01-03 04:10:12-05:00
3   2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern] 

您还可以使用 Series.dt.strftime() 将日期时间值格式化为字符串,其支持与标准 strftime() 相同的格式。

# DatetimeIndex
In [282]: s = pd.Series(pd.date_range("20130101", periods=4))

In [283]: s
Out[283]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: datetime64[ns]

In [284]: s.dt.strftime("%Y/%m/%d")
Out[284]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object 
# PeriodIndex
In [285]: s = pd.Series(pd.period_range("20130101", periods=4))

In [286]: s
Out[286]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [287]: s.dt.strftime("%Y/%m/%d")
Out[287]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object 

.dt 访问器适用于周期和时间增量数据类型。

# period
In [288]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))

In [289]: s
Out[289]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [290]: s.dt.year
Out[290]: 
0    2013
1    2013
2    2013
3    2013
dtype: int64

In [291]: s.dt.day
Out[291]: 
0    1
1    2
2    3
3    4
dtype: int64 
# timedelta
In [292]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))

In [293]: s
Out[293]: 
0   1 days 00:00:05
1   1 days 00:00:06
2   1 days 00:00:07
3   1 days 00:00:08
dtype: timedelta64[ns]

In [294]: s.dt.days
Out[294]: 
0    1
1    1
2    1
3    1
dtype: int64

In [295]: s.dt.seconds
Out[295]: 
0    5
1    6
2    7
3    8
dtype: int32

In [296]: s.dt.components
Out[296]: 
 days  hours  minutes  seconds  milliseconds  microseconds  nanoseconds
0     1      0        0        5             0             0            0
1     1      0        0        6             0             0            0
2     1      0        0        7             0             0            0
3     1      0        0        8             0             0            0 

注意

如果您使用非日期时间类似值访问 Series.dt,将引发 TypeError

向量化字符串方法

Series 配备了一组字符串处理方法,使得操作数组的每个元素变得容易。最重要的是,这些方法会自动排除丢失/NA 值。通过 Seriesstr 属性访问这些方法,通常名称与等效的(标量)内置字符串方法匹配。例如:

In [297]: s = pd.Series(
 .....:    ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
 .....: )
 .....: 

In [298]: s.str.lower()
Out[298]: 
0       a
1       b
2       c
3    aaba
4    baca
5    <NA>
6    caba
7     dog
8     cat
dtype: string 

还提供了强大的模式匹配方法,但请注意,模式匹配通常默认使用正则表达式(有些情况下总是使用)。

注意

在 pandas 1.0 之前,字符串方法仅适用于 object -dtype Series。pandas 1.0 添加了 StringDtype,专门用于字符串。有关更多信息,请参阅文本数据类型。

请参阅向量化字符串方法以获取完整的描述。

排序

pandas 支持三种排序方式:按索引标签排序、按列值排序以及按两者组合排序。

按索引

Series.sort_index()DataFrame.sort_index() 方法用于按索引级别对 pandas 对象进行排序。

In [299]: df = pd.DataFrame(
 .....:    {
 .....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 .....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 .....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 .....:    }
 .....: )
 .....: 

In [300]: unsorted_df = df.reindex(
 .....:    index=["a", "d", "c", "b"], columns=["three", "two", "one"]
 .....: )
 .....: 

In [301]: unsorted_df
Out[301]: 
 three       two       one
a       NaN -1.152244  0.562973
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504

# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]: 
 three       two       one
a       NaN -1.152244  0.562973
b -0.098217  0.009797 -1.299504
c  1.273388 -0.167123  0.640382
d -0.252916 -0.109597       NaN

In [303]: unsorted_df.sort_index(ascending=False)
Out[303]: 
 three       two       one
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504
a       NaN -1.152244  0.562973

In [304]: unsorted_df.sort_index(axis=1)
Out[304]: 
 one     three       two
a  0.562973       NaN -1.152244
d       NaN -0.252916 -0.109597
c  0.640382  1.273388 -0.167123
b -1.299504 -0.098217  0.009797

# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]: 
a         NaN
b   -0.098217
c    1.273388
d   -0.252916
Name: three, dtype: float64 

按索引排序还支持一个 key 参数,该参数接受一个可调用函数,应用于正在排序的索引。对于 MultiIndex 对象,key 会按级别应用到由 level 指定的级别。

In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
 .....:    list("ab")
 .....: )
 .....: 

In [307]: s1
Out[307]: 
 c
a b 
B 1  2
a 2  3
C 3  4 
In [308]: s1.sort_index(level="a")
Out[308]: 
 c
a b 
B 1  2
C 3  4
a 2  3

In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]: 
 c
a b 
a 2  3
B 1  2
C 3  4 

有关按值排序的关键信息,请参见 value sorting。 ### 按值排序

Series.sort_values() 方法用于按其值对 Series 进行排序。DataFrame.sort_values() 方法用于按其列或行值对 DataFrame 进行排序。可选的 by 参数可用于指定一个或多个列以确定排序顺序。

In [310]: df1 = pd.DataFrame(
 .....:    {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
 .....: )
 .....: 

In [311]: df1.sort_values(by="two")
Out[311]: 
 one  two  three
0    2    1      5
2    1    2      3
1    1    3      4
3    1    4      2 

by 参数可以接受列名的列表,例如:

In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]: 
 one  two  three
2    1    2      3
1    1    3      4
3    1    4      2
0    2    1      5 

这些方法通过 na_position 参数对 NA 值进行特殊处理:

In [313]: s[2] = np.nan

In [314]: s.sort_values()
Out[314]: 
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
2    <NA>
5    <NA>
dtype: string

In [315]: s.sort_values(na_position="first")
Out[315]: 
2    <NA>
5    <NA>
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
dtype: string 

排序还支持一个 key 参数,该参数接受一个可调用函数,应用于正在排序的值。

In [316]: s1 = pd.Series(["B", "a", "C"]) 
In [317]: s1.sort_values()
Out[317]: 
0    B
2    C
1    a
dtype: object

In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]: 
1    a
0    B
2    C
dtype: object 

key 将获得值的 Series 并应返回具有相同形状的转换值的 Series 或数组。对于 DataFrame 对象,key 会按列应用,因此 key 仍应期望一个 Series 并返回一个 Series,例如

In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]}) 
In [320]: df.sort_values(by="a")
Out[320]: 
 a  b
0  B  1
2  C  3
1  a  2

In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]: 
 a  b
1  a  2
0  B  1
2  C  3 

每列的名称或类型可用于对不同列应用不同函数。 ### 按索引和值排序

作为 by 参数传递给 DataFrame.sort_values() 的字符串可以指代列或索引级别名称。

# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
 .....:    [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
 .....: )
 .....: 

In [323]: idx.names = ["first", "second"]

# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)

In [325]: df_multi
Out[325]: 
 A
first second 
a     1       6
 2       5
 2       4
b     2       3
 1       2
 1       1 

按‘second’(索引)和‘A’(列)排序

In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]: 
 A
first second 
b     1       1
 1       2
a     1       6
b     2       3
a     2       4
 2       5 

注意

如果一个字符串既匹配列名又匹配索引级别名称,则会发出警告并且列优先。这将导致在将来版本中出现歧义错误。 ### searchsorted

Series 有 searchsorted() 方法,其工作方式类似于 numpy.ndarray.searchsorted()

In [327]: ser = pd.Series([1, 2, 3])

In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])

In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])

In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])

In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])

In [332]: ser = pd.Series([3, 1, 2])

In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2]) 
```  ### 最小/最大值

`Series` 有 `nsmallest()` 和 `nlargest()` 方法,返回最小或最大的 \(n\) 值。对于大型 `Series`,这比对整个 Series 进行排序并在结果上调用 `head(n)` 要快得多。

```py
In [334]: s = pd.Series(np.random.permutation(10))

In [335]: s
Out[335]: 
0    2
1    0
2    3
3    7
4    1
5    5
6    9
7    6
8    8
9    4
dtype: int64

In [336]: s.sort_values()
Out[336]: 
1    0
4    1
0    2
2    3
9    4
5    5
7    6
3    7
8    8
6    9
dtype: int64

In [337]: s.nsmallest(3)
Out[337]: 
1    0
4    1
0    2
dtype: int64

In [338]: s.nlargest(3)
Out[338]: 
6    9
8    8
3    7
dtype: int64 

DataFrame 也有 nlargestnsmallest 方法。

In [339]: df = pd.DataFrame(
 .....:    {
 .....:        "a": [-2, -1, 1, 10, 8, 11, -1],
 .....:        "b": list("abdceff"),
 .....:        "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
 .....:    }
 .....: )
 .....: 

In [340]: df.nlargest(3, "a")
Out[340]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [341]: df.nlargest(5, ["a", "c"])
Out[341]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN
2   1  d  4.0
6  -1  f  4.0

In [342]: df.nsmallest(3, "a")
Out[342]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0

In [343]: df.nsmallest(5, ["a", "c"])
Out[343]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
2  1  d  4.0
4  8  e  NaN 
```  ### 按 MultiIndex 列排序

当列是 MultiIndex 时,必须明确指定排序,并完全指定所有级别到 `by`。

```py
In [344]: df1.columns = pd.MultiIndex.from_tuples(
 .....:    [("a", "one"), ("a", "two"), ("b", "three")]
 .....: )
 .....: 

In [345]: df1.sort_values(by=("a", "two"))
Out[345]: 
 a         b
 one two three
0   2   1     5
2   1   2     3
1   1   3     4
3   1   4     2 
```  ### 按索引

`Series.sort_index()` 和 `DataFrame.sort_index()` 方法用于按其索引级别对 pandas 对象进行排序。

```py
In [299]: df = pd.DataFrame(
 .....:    {
 .....:        "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
 .....:        "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
 .....:        "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
 .....:    }
 .....: )
 .....: 

In [300]: unsorted_df = df.reindex(
 .....:    index=["a", "d", "c", "b"], columns=["three", "two", "one"]
 .....: )
 .....: 

In [301]: unsorted_df
Out[301]: 
 three       two       one
a       NaN -1.152244  0.562973
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504

# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]: 
 three       two       one
a       NaN -1.152244  0.562973
b -0.098217  0.009797 -1.299504
c  1.273388 -0.167123  0.640382
d -0.252916 -0.109597       NaN

In [303]: unsorted_df.sort_index(ascending=False)
Out[303]: 
 three       two       one
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504
a       NaN -1.152244  0.562973

In [304]: unsorted_df.sort_index(axis=1)
Out[304]: 
 one     three       two
a  0.562973       NaN -1.152244
d       NaN -0.252916 -0.109597
c  0.640382  1.273388 -0.167123
b -1.299504 -0.098217  0.009797

# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]: 
a         NaN
b   -0.098217
c    1.273388
d   -0.252916
Name: three, dtype: float64 

按索引排序还支持一个 key 参数,该参数接受一个可调用函数,应用于要排序的索引。对于 MultiIndex 对象,key 会按指定的 level 对每个级别应用。

In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
 .....:    list("ab")
 .....: )
 .....: 

In [307]: s1
Out[307]: 
 c
a b 
B 1  2
a 2  3
C 3  4 
In [308]: s1.sort_index(level="a")
Out[308]: 
 c
a b 
B 1  2
C 3  4
a 2  3

In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]: 
 c
a b 
a 2  3
B 1  2
C 3  4 

有关按值排序的键排序信息,请参阅 值排序。

按数值排序

使用 Series.sort_values() 方法可以按其数值对 Series 进行排序。使用 DataFrame.sort_values() 方法可以按其列或行数值对 DataFrame 进行排序。可选的 by 参数用于指定一个或多个列以确定排序顺序。

In [310]: df1 = pd.DataFrame(
 .....:    {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
 .....: )
 .....: 

In [311]: df1.sort_values(by="two")
Out[311]: 
 one  two  three
0    2    1      5
2    1    2      3
1    1    3      4
3    1    4      2 

by 参数可以接受列名的列表,例如:

In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]: 
 one  two  three
2    1    2      3
1    1    3      4
3    1    4      2
0    2    1      5 

这些方法对 NA 值有特殊处理,通过 na_position 参数:

In [313]: s[2] = np.nan

In [314]: s.sort_values()
Out[314]: 
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
2    <NA>
5    <NA>
dtype: string

In [315]: s.sort_values(na_position="first")
Out[315]: 
2    <NA>
5    <NA>
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
dtype: string 

排序还支持一个 key 参数,该参数接受一个可调用函数,应用于要排序的值。

In [316]: s1 = pd.Series(["B", "a", "C"]) 
In [317]: s1.sort_values()
Out[317]: 
0    B
2    C
1    a
dtype: object

In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]: 
1    a
0    B
2    C
dtype: object 

key 将给出 Series 的值,并应返回一个形状相同的 Series 或数组,带有转换后的值。对于 DataFrame 对象,key 会按列应用,因此 key 仍应期望一个 Series,并返回一个 Series,例如:

In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]}) 
In [320]: df.sort_values(by="a")
Out[320]: 
 a  b
0  B  1
2  C  3
1  a  2

In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]: 
 a  b
1  a  2
0  B  1
2  C  3 

每列的名称或类型可用于对不同列应用不同的函数。

通过索引和数值

作为 DataFrame.sort_values()by 参数传递的字符串可以是列名,也可以是索引级别名称。

# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
 .....:    [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
 .....: )
 .....: 

In [323]: idx.names = ["first", "second"]

# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)

In [325]: df_multi
Out[325]: 
 A
first second 
a     1       6
 2       5
 2       4
b     2       3
 1       2
 1       1 

按‘second’(索引)和‘A’(列)排序

In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]: 
 A
first second 
b     1       1
 1       2
a     1       6
b     2       3
a     2       4
 2       5 

注意

如果一个字符串既匹配列名又匹配索引级别名称,那么会发出警告,并且列优先。这将导致将来版本中的歧义错误。

searchsorted

Series 有 searchsorted() 方法,工作方式类似于 numpy.ndarray.searchsorted()

In [327]: ser = pd.Series([1, 2, 3])

In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])

In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])

In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])

In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])

In [332]: ser = pd.Series([3, 1, 2])

In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2]) 

最小值 / 最大值

Series具有nsmallest()nlargest()方法,返回最小或最大的(n)个值。对于大型Series,这比对整个 Series 进行排序并在结果上调用head(n)要快得多。

In [334]: s = pd.Series(np.random.permutation(10))

In [335]: s
Out[335]: 
0    2
1    0
2    3
3    7
4    1
5    5
6    9
7    6
8    8
9    4
dtype: int64

In [336]: s.sort_values()
Out[336]: 
1    0
4    1
0    2
2    3
9    4
5    5
7    6
3    7
8    8
6    9
dtype: int64

In [337]: s.nsmallest(3)
Out[337]: 
1    0
4    1
0    2
dtype: int64

In [338]: s.nlargest(3)
Out[338]: 
6    9
8    8
3    7
dtype: int64 

DataFrame也有nlargestnsmallest方法。

In [339]: df = pd.DataFrame(
 .....:    {
 .....:        "a": [-2, -1, 1, 10, 8, 11, -1],
 .....:        "b": list("abdceff"),
 .....:        "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
 .....:    }
 .....: )
 .....: 

In [340]: df.nlargest(3, "a")
Out[340]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [341]: df.nlargest(5, ["a", "c"])
Out[341]: 
 a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN
2   1  d  4.0
6  -1  f  4.0

In [342]: df.nsmallest(3, "a")
Out[342]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0

In [343]: df.nsmallest(5, ["a", "c"])
Out[343]: 
 a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
2  1  d  4.0
4  8  e  NaN 

按多级索引列排序

当列是多级索引时,必须明确排序,并完全指定所有级别到by

In [344]: df1.columns = pd.MultiIndex.from_tuples(
 .....:    [("a", "one"), ("a", "two"), ("b", "three")]
 .....: )
 .....: 

In [345]: df1.sort_values(by=("a", "two"))
Out[345]: 
 a         b
 one two three
0   2   1     5
2   1   2     3
1   1   3     4
3   1   4     2 

复制

pandas 对象上的copy()方法会复制基础数据(尽管不会复制轴索引,因为它们是不可变的),并返回一个新对象。请注意很少需要复制对象。例如,只有少数几种方法可以原地修改 DataFrame:

  • 插入、删除或修改列。

  • 分配给indexcolumns属性。

  • 对于同质数据,可以通过values属性或高级索引直接修改值。

明确一点,没有任何 pandas 方法会具有修改数据的副作用;几乎每个方法都会返回一个新对象,保持原始对象不变。如果数据被修改,那是因为你明确这样做了。

数据类型

在大多数情况下,pandas 使用 NumPy 数组和数据类型来处理 Series 或 DataFrame 的单个列。NumPy 支持floatintbooltimedelta64[ns]datetime64[ns](请注意,NumPy 不支持时区感知的日期时间)。

pandas 和第三方库扩展了 NumPy 的类型系统的几个地方。本节描述了 pandas 在内部所做的扩展。请参阅扩展类型了解如何编写自己的扩展以与 pandas 一起使用。请参阅生态系统页面查看已实现扩展的第三方库列表。

以下表列出了所有 pandas 扩展类型。对于需要dtype参数的方法,可以按照指示指定字符串。有关每种类型的更多信息,请参阅相应的文档部分。

数据类型 数据类型 标量 数组 字符串别名
时区感知日期时间 DatetimeTZDtype Timestamp arrays.DatetimeArray 'datetime64[ns, <tz>]'
类别 CategoricalDtype (none) Categorical 'category'
期间(时间跨度) PeriodDtype Period arrays.PeriodArray 'Period[<freq>]' 'period[<freq>]',
稀疏 SparseDtype (none) arrays.SparseArray 'Sparse', 'Sparse[int]', 'Sparse[float]'
间隔 IntervalDtype Interval arrays.IntervalArray 'interval', 'Interval', 'Interval[<numpy_dtype>]', 'Interval[datetime64[ns, <tz>]]', 'Interval[timedelta64[<freq>]]'
可空整数 Int64Dtype, … (none) arrays.IntegerArray 'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64'
可空浮点数 Float64Dtype, … (none) arrays.FloatingArray 'Float32', 'Float64'
字符串 StringDtype str arrays.StringArray 'string'
布尔(带 NA) BooleanDtype bool arrays.BooleanArray 'boolean'

pandas 有两种存储字符串的方式。

  1. object数据类型,可以保存任何 Python 对象,包括字符串。

  2. StringDtype,专门用于字符串。

通常,我们建议使用StringDtype。更多信息请参阅文本数据类型。

最后,可以使用object数据类型存储任意对象,但应尽可能避免(出于性能和与其他库和方法的互操作性考虑。请参阅对象转换)。

一个方便的dtypes属性用于 DataFrame 返回一个 Series,其中包含每列的数据类型。

In [346]: dft = pd.DataFrame(
 .....:    {
 .....:        "A": np.random.rand(3),
 .....:        "B": 1,
 .....:        "C": "foo",
 .....:        "D": pd.Timestamp("20010102"),
 .....:        "E": pd.Series([1.0] * 3).astype("float32"),
 .....:        "F": False,
 .....:        "G": pd.Series([1] * 3, dtype="int8"),
 .....:    }
 .....: )
 .....: 

In [347]: dft
Out[347]: 
 A  B    C          D    E      F  G
0  0.035962  1  foo 2001-01-02  1.0  False  1
1  0.701379  1  foo 2001-01-02  1.0  False  1
2  0.281885  1  foo 2001-01-02  1.0  False  1

In [348]: dft.dtypes
Out[348]: 
A          float64
B            int64
C           object
D    datetime64[s]
E          float32
F             bool
G             int8
dtype: object 

Series对象上,使用dtype属性。

In [349]: dft["A"].dtype
Out[349]: dtype('float64') 

如果 pandas 对象包含具有多种数据类型在单个列中的数据,则将选择列的数据类型以容纳所有数据类型(object是最通用的)。

# these ints are coerced to floats
In [350]: pd.Series([1, 2, 3, 4, 5, 6.0])
Out[350]: 
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    6.0
dtype: float64

# string data forces an ``object`` dtype
In [351]: pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]: 
0      1
1      2
2      3
3    6.0
4    foo
dtype: object 

可以通过调用DataFrame.dtypes.value_counts()来查找DataFrame中每种类型的列数。

In [352]: dft.dtypes.value_counts()
Out[352]: 
float64          1
int64            1
object           1
datetime64[s]    1
float32          1
bool             1
int8             1
Name: count, dtype: int64 

数值数据类型将传播并可以共存于数据框中。如果传递了数据类型(可以直接通过dtype关键字、传递的ndarray或传递的Series),那么它将在数据框操作中保留。此外,不同的数值数据类型不会被合并。以下示例将让你有所了解。

In [353]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")

In [354]: df1
Out[354]: 
 A
0  0.224364
1  1.890546
2  0.182879
3  0.787847
4 -0.188449
5  0.667715
6 -0.011736
7 -0.399073

In [355]: df1.dtypes
Out[355]: 
A    float32
dtype: object

In [356]: df2 = pd.DataFrame(
 .....:    {
 .....:        "A": pd.Series(np.random.randn(8), dtype="float16"),
 .....:        "B": pd.Series(np.random.randn(8)),
 .....:        "C": pd.Series(np.random.randint(0, 255, size=8), dtype="uint8"),  # [0,255] (range of uint8)
 .....:    }
 .....: )
 .....: 

In [357]: df2
Out[357]: 
 A         B    C
0  0.823242  0.256090   26
1  1.607422  1.426469   86
2 -0.333740 -0.416203   46
3 -0.063477  1.139976  212
4 -1.014648 -1.193477   26
5  0.678711  0.096706    7
6 -0.040863 -1.956850  184
7 -0.357422 -0.714337  206

In [358]: df2.dtypes
Out[358]: 
A    float16
B    float64
C      uint8
dtype: object 

默认值

默认情况下,整数类型为int64,浮点类型为float64不受平台(32 位或 64 位)的影响。以下都将导致int64数据类型。

In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]: 
a    int64
dtype: object

In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]: 
a    int64
dtype: object

In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]: 
a    int64
dtype: object 

请注意,当创建数组时,Numpy 将选择依赖于平台的类型。以下将在 32 位平台上导致int32

In [362]: frame = pd.DataFrame(np.array([1, 2])) 

向上转换

当与其他类型组合时,类型可能会向上转换,这意味着它们从当前类型(例如intfloat)提升。

In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

In [364]: df3
Out[364]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [365]: df3.dtypes
Out[365]: 
A    float32
B    float64
C    float64
dtype: object 

DataFrame.to_numpy()将返回较低的公共分母,意味着可以容纳结果同质化的 NumPy 数组中的所有类型的数据类型。这可能会导致一些向上转换

In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64') 

astype

您可以使用astype()方法显式地将 dtype 从一种转换为另一种。即使 dtype 未更改,它们默认也会返回一个副本(传递copy=False以更改此行为)。此外,如果 astype 操作无效,它们将引发异常。

向上转型始终遵循NumPy规则。如果操作涉及两种不同的 dtype,则将使用更通用的 dtype 作为操作的结果。

In [367]: df3
Out[367]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [368]: df3.dtypes
Out[368]: 
A    float32
B    float64
C    float64
dtype: object

# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]: 
A    float32
B    float32
C    float32
dtype: object 

使用astype()将一部分列转换为指定类型。

In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)

In [372]: dft
Out[372]: 
 a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

In [373]: dft.dtypes
Out[373]: 
a    uint8
b    uint8
c    int64
dtype: object 

通过将字典传递给astype(),将某些列转换为特定的 dtype。

In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})

In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})

In [376]: dft1
Out[376]: 
 a  b    c
0   True  4  7.0
1  False  5  8.0
2   True  6  9.0

In [377]: dft1.dtypes
Out[377]: 
a       bool
b      int64
c    float64
dtype: object 

注意

当尝试使用astype()loc()将一部分列转换为指定类型时,会发生向上转型。

loc()尝试适应我们分配给当前 dtype 的内容,而[]将覆盖它们,从右侧获取 dtype。因此,以下代码片段会产生意外结果。

In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]: 
a    uint8
b    uint8
dtype: object

In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)

In [381]: dft.dtypes
Out[381]: 
a    int64
b    int64
c    int64
dtype: object 

对象转换

pandas 提供各种函数,尝试强制将类型从object dtype 转换为其他类型。在数据已经是正确类型但存储在object数组中的情况下,可以使用DataFrame.infer_objects()Series.infer_objects()方法进行软转换为正确类型。

In [382]: import datetime

In [383]: df = pd.DataFrame(
 .....:    [
 .....:        [1, 2],
 .....:        ["a", "b"],
 .....:        [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
 .....:    ]
 .....: )
 .....: 

In [384]: df = df.T

In [385]: df
Out[385]: 
 0  1                    2
0  1  a  2016-03-02 00:00:00
1  2  b  2016-03-02 00:00:00

In [386]: df.dtypes
Out[386]: 
0    object
1    object
2    object
dtype: object 

由于数据被转置,原始推断将所有列存储为对象,infer_objects将进行更正。

In [387]: df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object 

下列函数可用于一维对象数组或标量,执行将对象硬转换为指定类型:

  • to_numeric()(转换为数值 dtype)

    In [388]: m = ["1.1", 2, 3]
    
    In [389]: pd.to_numeric(m)
    Out[389]: array([1.1, 2\. , 3\. ]) 
    
  • to_datetime()(转换为日期时间对象)

    In [390]: import datetime
    
    In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
    
    In [392]: pd.to_datetime(m)
    Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None) 
    
  • to_timedelta()(转换为时间增量对象)

    In [393]: m = ["5us", pd.Timedelta("1day")]
    
    In [394]: pd.to_timedelta(m)
    Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None) 
    

要强制转换,我们可以传入一个errors参数,该参数指定 pandas 如何处理无法转换为所需数据类型或对象的元素。默认情况下,errors='raise',意味着在转换过程中遇到的任何错误都将被引发。但是,如果errors='coerce',这些错误将被忽略,pandas 将把有问题的元素转换为pd.NaT(对于日期时间和时间增量)或np.nan(对于数值)。如果您正在读取大部分为所需数据类型(例如数值、日期时间)的数据,但偶尔混有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:

In [395]: import datetime

In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]

In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

In [398]: m = ["apple", 2, 3]

In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan,  2.,  3.])

In [400]: m = ["apple", pd.Timedelta("1day")]

In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None) 

除了对象转换,to_numeric()还提供另一个参数downcast,该参数可以将新(或已有)数值数据向下转换为较小的数据类型,从而节省内存:

In [402]: m = ["1", 2, 3]

In [403]: pd.to_numeric(m, downcast="integer")  # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)

In [404]: pd.to_numeric(m, downcast="signed")  # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)

In [405]: pd.to_numeric(m, downcast="unsigned")  # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)

In [406]: pd.to_numeric(m, downcast="float")  # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32) 

由于这些方法仅适用于一维数组、列表或标量;它们不能直接用于多维对象,如 DataFrames。但是,通过apply(),我们可以高效地对每列“应用”函数:

In [407]: import datetime

In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")

In [409]: df
Out[409]: 
 0                    1
0  2016-07-09  2016-03-02 00:00:00
1  2016-07-09  2016-03-02 00:00:00

In [410]: df.apply(pd.to_datetime)
Out[410]: 
 0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")

In [412]: df
Out[412]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [413]: df.apply(pd.to_numeric)
Out[413]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")

In [415]: df
Out[415]: 
 0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

In [416]: df.apply(pd.to_timedelta)
Out[416]: 
 0      1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days 

注意事项

整数类型数据执行选择操作可能会轻松将数据向上转型为浮点数。在不引入nans的情况下,输入数据的数据类型将被保留。另请参阅对整数 NA 的支持。

In [417]: dfi = df3.astype("int32")

In [418]: dfi["E"] = 1

In [419]: dfi
Out[419]: 
 A  B    C  E
0  1  0   26  1
1  3  1   86  1
2  0  0   46  1
3  0  1  212  1
4 -1 -1   26  1
5  1  0    7  1
6  0 -1  184  1
7  0  0  206  1

In [420]: dfi.dtypes
Out[420]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

In [421]: casted = dfi[dfi > 0]

In [422]: casted
Out[422]: 
 A    B    C  E
0  1.0  NaN   26  1
1  3.0  1.0   86  1
2  NaN  NaN   46  1
3  NaN  1.0  212  1
4  NaN  NaN   26  1
5  1.0  NaN    7  1
6  NaN  NaN  184  1
7  NaN  NaN  206  1

In [423]: casted.dtypes
Out[423]: 
A    float64
B    float64
C      int32
E      int64
dtype: object 

浮点数数据类型不会改变。

In [424]: dfa = df3.copy()

In [425]: dfa["A"] = dfa["A"].astype("float32")

In [426]: dfa.dtypes
Out[426]: 
A    float32
B    float64
C    float64
dtype: object

In [427]: casted = dfa[df2 > 0]

In [428]: casted
Out[428]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2       NaN       NaN   46.0
3       NaN  1.139976  212.0
4       NaN       NaN   26.0
5  1.346426  0.096706    7.0
6       NaN       NaN  184.0
7       NaN       NaN  206.0

In [429]: casted.dtypes
Out[429]: 
A    float32
B    float64
C    float64
dtype: object 

默认值

默认情况下,整数类型为int64,浮点数类型为float64不受平台(32 位或 64 位)影响。以下操作将全部导致int64数据类型。

In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]: 
a    int64
dtype: object

In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]: 
a    int64
dtype: object

In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]: 
a    int64
dtype: object 

请注意,当创建数组时,Numpy 将选择平台相关类型。以下操作在 32 位平台上将导致int32

In [362]: frame = pd.DataFrame(np.array([1, 2])) 

向上转型

当与其他类型组合时,类型可能会向上转型,这意味着它们从当前类型(例如int)提升为float

In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

In [364]: df3
Out[364]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [365]: df3.dtypes
Out[365]: 
A    float32
B    float64
C    float64
dtype: object 

DataFrame.to_numpy()将返回数据类型的最低公共分母,即可以容纳结果中所有类型的同类数据类型 NumPy 数组。这可能会强制进行一些向上转型

In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64') 

astype

您可以使用astype()方法将数据类型明确转换为另一种数据类型。即使数据类型未更改,这些方法默认也会返回一个副本(如果要更改此行为,请传递copy=False)。此外,如果 astype 操作无效,它们将引发异常。

向上转型始终遵循NumPy规则。如果操作涉及两种不同的数据类型,则将使用更通用的数据类型作为操作的结果。

In [367]: df3
Out[367]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [368]: df3.dtypes
Out[368]: 
A    float32
B    float64
C    float64
dtype: object

# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]: 
A    float32
B    float32
C    float32
dtype: object 

使用astype()将一部分列转换为指定类型。

In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)

In [372]: dft
Out[372]: 
 a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

In [373]: dft.dtypes
Out[373]: 
a    uint8
b    uint8
c    int64
dtype: object 

通过将字典传递给astype()将某些列转换为特定数据类型。

In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})

In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})

In [376]: dft1
Out[376]: 
 a  b    c
0   True  4  7.0
1  False  5  8.0
2   True  6  9.0

In [377]: dft1.dtypes
Out[377]: 
a       bool
b      int64
c    float64
dtype: object 

注意

当尝试使用astype()loc()将一部分列转换为指定类型时,会发生向上转换。

loc() 尝试适应我们正在分配的当前数据类型,而[]将覆盖它们,从右侧获取数据类型。因此,以下代码片段会产生意外结果。

In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]: 
a    uint8
b    uint8
dtype: object

In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)

In [381]: dft.dtypes
Out[381]: 
a    int64
b    int64
c    int64
dtype: object 

对象转换

pandas 提供各种函数来尝试强制将object数据类型转换为其他类型。在数据已经是正确类型但存储在object数组中的情况下,可以使用DataFrame.infer_objects()Series.infer_objects()方法进行软转换为正确类型。

In [382]: import datetime

In [383]: df = pd.DataFrame(
 .....:    [
 .....:        [1, 2],
 .....:        ["a", "b"],
 .....:        [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
 .....:    ]
 .....: )
 .....: 

In [384]: df = df.T

In [385]: df
Out[385]: 
 0  1                    2
0  1  a  2016-03-02 00:00:00
1  2  b  2016-03-02 00:00:00

In [386]: df.dtypes
Out[386]: 
0    object
1    object
2    object
dtype: object 

由于数据被转置,原始推断将所有列存储为对象,infer_objects���进行更正。

In [387]: df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object 

以下函数可用于一维对象数组或标量,以执行将对象硬转换为指定类型:

  • to_numeric()(转换为数值数据类型)

    In [388]: m = ["1.1", 2, 3]
    
    In [389]: pd.to_numeric(m)
    Out[389]: array([1.1, 2\. , 3\. ]) 
    
  • to_datetime()(转换为日期时间对象)

    In [390]: import datetime
    
    In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
    
    In [392]: pd.to_datetime(m)
    Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None) 
    
  • to_timedelta()(转换为时间间隔对象)

    In [393]: m = ["5us", pd.Timedelta("1day")]
    
    In [394]: pd.to_timedelta(m)
    Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None) 
    

要强制转换,我们可以传入一个errors参数,指定 pandas 如何处理无法转换为所需数据类型或对象的元素。默认情况下,errors='raise',意味着在转换过程中遇到任何错误都将被引发。但是,如果errors='coerce',这些错误将被忽略,pandas 将把有问题的元素转换为pd.NaT(对于日期时间和时间间隔)或np.nan(对于数值)。如果您正在读取大部分为所需数据类型(例如数值、日期时间)的数据,但偶尔混合有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:

In [395]: import datetime

In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]

In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

In [398]: m = ["apple", 2, 3]

In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan,  2.,  3.])

In [400]: m = ["apple", pd.Timedelta("1day")]

In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None) 

除了对象转换外,to_numeric()还提供另一个参数downcast,该参数可以选择将新的(或已有的)数值数据向下转换为较小的数据类型,从而节省内存:

In [402]: m = ["1", 2, 3]

In [403]: pd.to_numeric(m, downcast="integer")  # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)

In [404]: pd.to_numeric(m, downcast="signed")  # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)

In [405]: pd.to_numeric(m, downcast="unsigned")  # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)

In [406]: pd.to_numeric(m, downcast="float")  # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32) 

由于这些方法仅适用于一维数组、列表或标量;不能直接用于诸如数据框之类的多维对象。但是,通过apply(),我们可以高效地“应用”函数到每一列:

In [407]: import datetime

In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")

In [409]: df
Out[409]: 
 0                    1
0  2016-07-09  2016-03-02 00:00:00
1  2016-07-09  2016-03-02 00:00:00

In [410]: df.apply(pd.to_datetime)
Out[410]: 
 0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")

In [412]: df
Out[412]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [413]: df.apply(pd.to_numeric)
Out[413]: 
 0  1  2
0  1.1  2  3
1  1.1  2  3

In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")

In [415]: df
Out[415]: 
 0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

In [416]: df.apply(pd.to_timedelta)
Out[416]: 
 0      1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days 

注意事项

integer类型数据执行选择操作可以轻松地将数据向上转换为floating。在不引入nans的情况下,输入数据的数据类型将被保留。另请参阅对整数 NA 的支持。

In [417]: dfi = df3.astype("int32")

In [418]: dfi["E"] = 1

In [419]: dfi
Out[419]: 
 A  B    C  E
0  1  0   26  1
1  3  1   86  1
2  0  0   46  1
3  0  1  212  1
4 -1 -1   26  1
5  1  0    7  1
6  0 -1  184  1
7  0  0  206  1

In [420]: dfi.dtypes
Out[420]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

In [421]: casted = dfi[dfi > 0]

In [422]: casted
Out[422]: 
 A    B    C  E
0  1.0  NaN   26  1
1  3.0  1.0   86  1
2  NaN  NaN   46  1
3  NaN  1.0  212  1
4  NaN  NaN   26  1
5  1.0  NaN    7  1
6  NaN  NaN  184  1
7  NaN  NaN  206  1

In [423]: casted.dtypes
Out[423]: 
A    float64
B    float64
C      int32
E      int64
dtype: object 

浮点数数据类型保持不变。

In [424]: dfa = df3.copy()

In [425]: dfa["A"] = dfa["A"].astype("float32")

In [426]: dfa.dtypes
Out[426]: 
A    float32
B    float64
C    float64
dtype: object

In [427]: casted = dfa[df2 > 0]

In [428]: casted
Out[428]: 
 A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2       NaN       NaN   46.0
3       NaN  1.139976  212.0
4       NaN       NaN   26.0
5  1.346426  0.096706    7.0
6       NaN       NaN  184.0
7       NaN       NaN  206.0

In [429]: casted.dtypes
Out[429]: 
A    float32
B    float64
C    float64
dtype: object 

基于dtype选择列

select_dtypes()方法实现了基于dtype的列子集。

首先,让我们创建一个具有各种不同数据类型的DataFrame

In [430]: df = pd.DataFrame(
 .....:    {
 .....:        "string": list("abc"),
 .....:        "int64": list(range(1, 4)),
 .....:        "uint8": np.arange(3, 6).astype("u1"),
 .....:        "float64": np.arange(4.0, 7.0),
 .....:        "bool1": [True, False, True],
 .....:        "bool2": [False, True, False],
 .....:        "dates": pd.date_range("now", periods=3),
 .....:        "category": pd.Series(list("ABC")).astype("category"),
 .....:    }
 .....: )
 .....: 

In [431]: df["tdeltas"] = df.dates.diff()

In [432]: df["uint64"] = np.arange(3, 6).astype("u8")

In [433]: df["other_dates"] = pd.date_range("20130101", periods=3)

In [434]: df["tz_aware_dates"] = pd.date_range("20130101", periods=3, tz="US/Eastern")

In [435]: df
Out[435]: 
 string  int64  uint8  ...  uint64  other_dates            tz_aware_dates
0      a      1      3  ...       3   2013-01-01 2013-01-01 00:00:00-05:00
1      b      2      4  ...       4   2013-01-02 2013-01-02 00:00:00-05:00
2      c      3      5  ...       5   2013-01-03 2013-01-03 00:00:00-05:00

[3 rows x 12 columns] 

并且数据类型:

In [436]: df.dtypes
Out[436]: 
string                                object
int64                                  int64
uint8                                  uint8
float64                              float64
bool1                                   bool
bool2                                   bool
dates                         datetime64[ns]
category                            category
tdeltas                      timedelta64[ns]
uint64                                uint64
other_dates                   datetime64[ns]
tz_aware_dates    datetime64[ns, US/Eastern]
dtype: object 

select_dtypes()有两个参数includeexclude,允许你说“给我这些数据类型的列”(include)和/或“给我不包含这些数据类型的列”(exclude)。

例如,要选择bool列:

In [437]: df.select_dtypes(include=[bool])
Out[437]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

你也可以在NumPy 数据类型层次结构中传递数据类型的名称:

In [438]: df.select_dtypes(include=["bool"])
Out[438]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

select_dtypes()也适用于通用数据类型。

例如,要选择所有数值和布尔列,同时排除无符号整数:

In [439]: df.select_dtypes(include=["number", "bool"], exclude=["unsignedinteger"])
Out[439]: 
 int64  float64  bool1  bool2 tdeltas
0      1      4.0   True  False     NaT
1      2      5.0  False   True  1 days
2      3      6.0   True  False  1 days 

要选择字符串列,必须使用object数据类型:

In [440]: df.select_dtypes(include=["object"])
Out[440]: 
 string
0      a
1      b
2      c 

要查看numpy.number等通用dtype的所有子数据类型,可以定义一个返回子数据类型树的函数:

In [441]: def subdtypes(dtype):
 .....:    subs = dtype.__subclasses__()
 .....:    if not subs:
 .....:        return dtype
 .....:    return [dtype, [subdtypes(dt) for dt in subs]]
 .....: 

所有 NumPy 数据类型都是numpy.generic的子类:

In [442]: subdtypes(np.generic)
Out[442]: 
[numpy.generic,
 [[numpy.number,
 [[numpy.integer,
 [[numpy.signedinteger,
 [numpy.int8,
 numpy.int16,
 numpy.int32,
 numpy.int64,
 numpy.longlong,
 numpy.timedelta64]],
 [numpy.unsignedinteger,
 [numpy.uint8,
 numpy.uint16,
 numpy.uint32,
 numpy.uint64,
 numpy.ulonglong]]]],
 [numpy.inexact,
 [[numpy.floating,
 [numpy.float16, numpy.float32, numpy.float64, numpy.longdouble]],
 [numpy.complexfloating,
 [numpy.complex64, numpy.complex128, numpy.clongdouble]]]]]],
 [numpy.flexible,
 [[numpy.character, [numpy.bytes_, numpy.str_]],
 [numpy.void, [numpy.record]]]],
 numpy.bool_,
 numpy.datetime64,
 numpy.object_]] 

注意

pandas 还定义了categorydatetime64[ns, tz]类型,它们没有集成到正常的 NumPy 层次结构中,并且不会显示在上述函数中。

posted @ 2024-06-26 10:32  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报