Pandas 2.2 中文官方教程和指南(九·一)

2024-05-24 16:23:17 浏览数 (1)

原文:pandas.pydata.org/docs/

基本功能

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

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

代码语言:javascript复制
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() 方法。默认显示的元素数量为五个,但您可以传递自定义数量。

代码语言:javascript复制
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 属性

代码语言:javascript复制
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()

代码语言:javascript复制
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

代码语言:javascript复制
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]' 丢弃

代码语言:javascript复制
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() 将返回底层数据:

代码语言:javascript复制
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

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

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

代码语言:javascript复制
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 对齐。

代码语言:javascript复制
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()。此函数同时执行地板除法和取模运算��返回与左侧相同类型的两元组。例如:

代码语言:javascript复制
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() 操作:

代码语言:javascript复制
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 替换为其他值)。

代码语言:javascript复制
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,其行为类似于上述二进制算术操作:

代码语言:javascript复制
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(),以提供一种总结布尔结果的方式。

代码语言:javascript复制
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 

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

代码语言:javascript复制
In [52]: (df > 0).any().any()
Out[52]: True 

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

代码语言:javascript复制
In [53]: df.empty
Out[53]: False

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

警告

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

代码语言:javascript复制
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(). 
代码语言:javascript复制
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:

代码语言:javascript复制
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 不会被视为相等:

代码语言:javascript复制
In [59]: np.nan == np.nan
Out[59]: False 

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

代码语言:javascript复制
In [60]: (df   df).equals(df * 2)
Out[60]: True 

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

代码语言:javascript复制
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 数据结构与标量值进行比较时,您可以方便地执行逐元素比较:

代码语言:javascript复制
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 还处理相同长度的不同类似数组对象之间的逐元素比较:

代码语言:javascript复制
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:

代码语言:javascript复制
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(),我们进行演示:

代码语言:javascript复制
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()如上所示:

代码语言:javascript复制
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):

代码语言:javascript复制
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):

代码语言:javascript复制
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参数的影响。

代码语言:javascript复制
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:

代码语言:javascript复制
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 值的数量:

代码语言:javascript复制
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):

代码语言:javascript复制
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 

您可以选择要包含在输出中的特定百��位数:

代码语言:javascript复制
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() 将提供关于唯一值数量和最频繁出现值的简单摘要:

代码语言:javascript复制
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() 将限制摘要仅包括数值列或(如果没有)仅包括分类列:

代码语言:javascript复制
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

代码语言:javascript复制
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 上计算具有最小和最大对应值的索引标签:

代码语言:javascript复制
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() 返回第一个匹配的索引:

代码语言:javascript复制
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 数组的直方图。它也可以用作常规数组的函数:

代码语言:javascript复制
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 参数选择子集。

代码语言:javascript复制
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 中值的最频繁出现的值(即众数):

代码语言:javascript复制
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()(基于样本分位数的区间)函数进行离散化:

代码语言:javascript复制
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() 计算样本分位数。例如,我们可以将一些正态分布数据切分成相等大小的四分位数:

代码语言:javascript复制
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]] 

我们也可以传入无限值来定义区间:

代码语言:javascript复制
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 的函数。

现在比较以下内容:

代码语言:javascript复制
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 

等同于:

代码语言:javascript复制
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

代码语言:javascript复制
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 管道以及更近期的dplyr和magrittr的启发,它们为R引入了流行的(%>%)(读作 pipe)运算符。这里的pipe实现非常干净,感觉就像在 Python 中很合适。我们鼓励您查看pipe()的源代码。

按行或列应用函数

可以使用apply()方法沿着 DataFrame 的轴应用任意函数,类似描述性统计方法,该方法接受一个可选的axis参数:

代码语言:javascript复制
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()方法还可以根据字符串方法名进行分派。

代码语言:javascript复制
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()结合一些巧妙的技巧可以用来回答关于数据集的许多问题。例如,假设我们想要提取每列中最大值出现的日期:

代码语言:javascript复制
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()方法传递额外的参数和关键字参数。

代码语言:javascript复制
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 

另一个有用的功能是能够传递序列方法来对每一列或行执行一些序列操作:

代码语言:javascript复制
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()

我们将使用类似上面的起始框架:

代码语言:javascript复制
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

代码语言:javascript复制
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进行单个聚合将返回一个标量值:

代码语言:javascript复制
In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146 
使用多个函数进行聚合

您可以将多个聚合参数作为列表传递。每个传递函数的结果将成为结果DataFrame中的一行。这些自然地从聚合函数命名。

代码语言:javascript复制
In [168]: tsdf.agg(["sum"])
Out[168]: 
 A         B        C
sum  3.033606 -1.803879  1.57551 

多个函数产生多个行:

代码语言:javascript复制
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,由函数名称索引:

代码语言:javascript复制
In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64 

传递一个lambda函数将产生一个<lambda>命名的行:

代码语言:javascript复制
In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64 

传递命名函数将为该行产生该名称:

代码语言:javascript复制
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来保证顺序。

代码语言:javascript复制
In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64 

传递类似列表的内容将生成一个DataFrame输出。您将获得所有聚合器的类似矩阵的输出。输出将包含所有唯一的函数。那些没有针对特定列注明的函数将为NaN

代码语言:javascript复制
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 函数的自定义描述函数。

代码语言:javascript复制
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 函数、字符串函数名称或用户定义的函数。

代码语言:javascript复制
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应用。

代码语言:javascript复制
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

代码语言:javascript复制
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。第一级将是原始框架列名称;第二级将是转换函数的名称。

代码语言:javascript复制
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。生成的列名将是转换函数。

代码语言:javascript复制
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 
使用字典进行转换

通过传递函数字典将允许每列进行选择性转换。

代码语言:javascript复制
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。

代码语言:javascript复制
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() 还具有一个额外的功能;它可以用于轻松“链接”或“映射”由辅助系列定义的值。这与合并/连接功能密切相关:

代码语言:javascript复制
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,您可以同时重新索引索引和列:

代码语言:javascript复制
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,则可以执行以下操作:

代码语言:javascript复制
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

代码语言:javascript复制
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()方法来简化这个操作:

代码语言:javascript复制
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 方法将应用于索引和列:

代码语言:javascript复制
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选项,只在指定的轴上对齐:

代码语言:javascript复制
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 的索引或列上同时对齐两个对象:

代码语言:javascript复制
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 来实现相同的结果:

代码语言:javascript复制
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 指定连续匹配的最大计数:

代码语言:javascript复制
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 

相反,容差指定索引和索引器值之间的最大距离:

代码语言:javascript复制
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()函数。它从轴中删除一组标签:

代码语言:javascript复制
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 

请注意,以下方法也有效,但不太明显/干净:

代码语言:javascript复制
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:

代码语言:javascript复制
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

代码语言:javascript复制
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属性。

代码语言:javascript复制
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的特定名称(而不是标签)。

代码语言:javascript复制
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 编写内部循环。请参阅提高性能部分,了解一些此方法的示例。

警告

您绝对不应该修改正在迭代的内容。这并不保证在所有情况下都有效。根据数据类型,迭代器返回的是副本而不是视图,对其进行写入将不起作用!

例如,在以下情况中设置值没有效果:

代码语言:javascript复制
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)对

例如:

代码语言:javascript复制
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:

代码语言:javascript复制
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,它不会在行之间保留数据类型(对于数据框的列,数据类型是保留的)。例如,

代码语言:javascript复制
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中的原始整数值:

代码语言:javascript复制
In [264]: row["int"].dtype
Out[264]: dtype('float64')

In [265]: df_orig["int"].dtype
Out[265]: dtype('int64') 

在迭代行时保留数据类型,最好使用itertuples(),它返回值的命名元组,通常比iterrows()快得多。

例如,转置数据框的一种构造性方法可能是:

代码语言:javascript复制
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() 方法将返回一个迭代器,为数据框中的每一行产生一个命名元组。元组的第一个元素将是行对应的索引值,而其余值是行的数值。

例如:

代码语言:javascript复制
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。

代码语言:javascript复制
# 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 

这使得可以使用如下的表达式:

代码语言:javascript复制
In [277]: s[s.dt.day == 2]
Out[277]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns] 

您可以轻松地生成带有时区信息的转换:

代码语言:javascript复制
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> 

您还可以链接这些类型的操作:

代码语言:javascript复制
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()相同的格式。

代码语言:javascript复制
# 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 
代码语言:javascript复制
# 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 访问器适用于周期和时间增量数据类型。

代码语言:javascript复制
# 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 
代码语言:javascript复制
# 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属性访问,通常与等效的(标量)内置字符串方法名称匹配。例如:

代码语言:javascript复制
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 对象进行排序。

代码语言:javascript复制
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逐级应用到各级别。

代码语言:javascript复制
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 
代码语言:javascript复制
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参数可用于指定一个或多个列以确定排序顺序。

代码语言:javascript复制
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参数可以接受一个列名列表,例如:

代码语言:javascript复制
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 值进行特殊处理:

代码语言:javascript复制
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参数,该参数接受一个可调用函数,用于对要排序的值应用。

代码语言:javascript复制
In [316]: s1 = pd.Series(["B", "a", "C"]) 
代码语言:javascript复制
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,例如:

代码语言:javascript复制
In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]}) 
代码语言:javascript复制
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参数可能是列名或索引级别名称。

代码语言:javascript复制
# 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’(列)排序

代码语言:javascript复制
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()

代码语言:javascript复制
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方法。

代码语言:javascript复制
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。

代码语言:javascript复制
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属性。

代码语言:javascript复制
In [349]: dft["A"].dtype
Out[349]: dtype('float64') 

如果 pandas 对象包含具有多种数据类型在单个列中的数据,则列的数据类型将被选择以容纳所有数据类型(object是最通用的)。

代码语言:javascript复制
# 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()来找到。

代码语言:javascript复制
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),那么它将在数据框操作中保留。此外,不同的数值数据类型将不会被合并。以下示例将让你一窥其中。

代码语言:javascript复制
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数据类型。

代码语言:javascript复制
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

代码语言:javascript复制
In [362]: frame = pd.DataFrame(np.array([1, 2])) 
向上转换

当与其他类型组合时,类型可能会被向上转换,这意味着它们从当前类型(例如intfloat)提升。

代码语言:javascript复制
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 数组。这可能会强制进行一些向上转换

代码语言:javascript复制
In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64') 
astype

您可以使用astype()方法将数据类型明确转换为另一种。默认情况下,即使数据类型未更改(通过传递copy=False来更改此行为),它们也将返回一个副本。此外,如果 astype 操作无效,它们将引发异常。

向上转换始终遵循NumPy规则。如果操作涉及两种不同的数据类型,则将使用更通用的数据类型作为操作的结果。

代码语言:javascript复制
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()将列的子集转换为指定类型。

代码语言:javascript复制
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()将某些列转换为特定数据类型。

代码语言:javascript复制
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()尝试适应我们分配给当前数据类型的内容,而[]将覆盖它们,从右侧获取数据类型。因此,以下代码片段会产生意外结果。

代码语言:javascript复制
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()方法进行软转换为正确类型。

代码语言:javascript复制
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将进行更正。

代码语言:javascript复制
In [387]: df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object 

以下函数适用于一维对象数组或标量,以执行将对象硬转换为指定类型的操作:

to_numeric()(转换为数值数据类型)

代码语言:javascript复制
In [388]: m = ["1.1", 2, 3]

In [389]: pd.to_numeric(m)
Out[389]: array([1.1, 2. , 3. ]) 

to_datetime()(转换为日期时间对象)

代码语言:javascript复制
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()(转换为时间间隔对象)

代码语言:javascript复制
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(对于数值)。如果您正在读取大部分是所需数据类型(例如数值、日期时间)的数据,但偶尔混有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:

代码语言:javascript复制
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,该参数可以将新(或已有)的数值数据向下转换为较小的数据类型,以节省内存:

代码语言:javascript复制
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(),我们可以高效地对每列应用函数:

代码语言:javascript复制
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 的支持。

代码语言:javascript复制
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 

浮点数数据类型保持不变。

代码语言:javascript复制
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

代码语言:javascript复制
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] 

以及数据类型:

代码语言:javascript复制
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列:

代码语言:javascript复制
In [437]: df.select_dtypes(include=[bool])
Out[437]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

您还可以在NumPy 数据类型层次结构中传递数据类型的名称:

代码语言:javascript复制
In [438]: df.select_dtypes(include=["bool"])
Out[438]: 
 bool1  bool2
0   True  False
1  False   True
2   True  False 

select_dtypes()也适用于通用数据类型。

例如,要选择所有数值和布尔列,同时排除无符号整数:

代码语言:javascript复制
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数据类型:

代码语言:javascript复制
In [440]: df.select_dtypes(include=["object"])
Out[440]: 
 string
0      a
1      b
2      c 

要查看类似numpy.number这样的通用dtype的所有子数据类型,您可以定义一个返回子数据类型树的函数:

代码语言:javascript复制
In [441]: def subdtypes(dtype):
 .....:    subs = dtype.__subclasses__()
 .....:    if not subs:
 .....:        return dtype
 .....:    return [dtype, [subdtypes(dt) for dt in subs]]
 .....: 

所有 NumPy 数据类型都是numpy.generic的子类:

代码语言:javascript复制
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()方法。默认显示的元素数量为五个,但您可以传递自定义数量。

代码语言:javascript复制
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索引(行)和

注意,这些属性可以安全地分配给

代码语言:javascript复制
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属性

代码语言:javascript复制
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()

代码语言:javascript复制
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

代码语言:javascript复制
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]'丢弃

代码语言:javascript复制
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()将返回底层数据:

代码语言:javascript复制
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

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

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

代码语言:javascript复制
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 关键字来匹配 索引

代码语言:javascript复制
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 对齐。

代码语言:javascript复制
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()内置函数。该函数同时进行地板除法和取模运算,返回与左侧相同类型的两元组。例如:

代码语言:javascript复制
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()操作:

代码语言:javascript复制
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 替换为其他值)。

代码语言:javascript复制
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,其行为类似于上述二进制算术操作:

代码语言:javascript复制
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() 来提供一种总结布尔结果的方式。

代码语言:javascript复制
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 

您可以将其减少为最终布尔值。

代码语言:javascript复制
In [52]: (df > 0).any().any()
Out[52]: True 

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

代码语言:javascript复制
In [53]: df.empty
Out[53]: False

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

警告

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

代码语言:javascript复制
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(). 
代码语言:javascript复制
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:

代码语言:javascript复制
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 不会被视为相等:

代码语言:javascript复制
In [59]: np.nan == np.nan
Out[59]: False 

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

代码语言:javascript复制
In [60]: (df   df).equals(df * 2)
Out[60]: True 

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

代码语言:javascript复制
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 

0 人点赞