Pandas 2 使用指南:基本功能


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

index = pd.date_range("1/1/2000", periods=8)

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

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

头部和尾部

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

long_series = pd.Series(np.random.randn(1000))

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

long_series.tail(3)
Out[6]: 
997   -0.289388
998   -1.020544
999    0.589993
dtype: float64

属性和底层数据

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

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

    • 轴标签

      Seriesindex(仅轴)DataFrameindex(行)和columns

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

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

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

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属性

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

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

array始终是一个ExtensionArrayExtensionArray的确切细节以及为什么pandas使用它们的原因超出了本介绍的范围。有关更多信息,请参见dtypes

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

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

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,每个对象具有正确的时区
  2. 使用datetime64[ns]-dtype numpy.ndarray,其中的值已转换为UTC并丢弃了时区

时区可以保留为dtype=object

ser = pd.Series(pd.date_range("2000", periods=2, tz="CET"))

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]'丢弃时区

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()将返回底层数据:

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是一组专门用于处理具有nans的数组的cython例程,速度非常快。

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

操作0.11.0(毫秒)之前版本(毫秒)相对于之前版本的比率
df1 > df213.32125.350.1063
df1 * df221.7136.630.5928
df1 + df222.0436.500.6039
这两个选项默认情况下都是启用的,您可以通过设置选项来控制:
pd.set_option("compute.use_bottleneck", False)
pd.set_option("compute.use_numexpr", False)

灵活的二元操作

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

  • 高维(例如 DataFrame)和低维(例如 Series)对象之间的广播行为。
  • 计算中的缺失数据。

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

匹配/广播行为

DataFrame 有 add()sub()mul()div() 等方法和相关函数 radd()rsub() 等用于执行二元操作。对于广播行为,Series 输入是主要关注的对象。使用这些函数,您可以使用 axis 关键字在 indexcolumns 上进行匹配:

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"]),
    }
)


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

row = df.iloc[1]

column = df["two"]

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

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

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

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 对齐。

dfmi = df.copy()

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


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() 内置函数。该函数同时执行地板除法和取模运算,返回与左侧操作数相同类型的两个元素的元组。例如:

s = pd.Series(np.arange(10))

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

div, rem = divmod(s, 3)

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

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

idx = pd.Index(np.arange(10))

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

div, rem = divmod(idx, 3)

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

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

我们还可以对元素进行逐个 divmod()

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

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

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 替换为其他值)。

df2 = df.copy()

df2.loc["a", "three"] = 1.0

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

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

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

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,其行为类似于上述二元算术操作:

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

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() 来提供一种汇总布尔结果的方法。

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

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

您可以将其约简为最终的布尔值。

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

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

df.empty
Out[53]: False

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

警告

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

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)
   1574     @final
   1575     def __nonzero__(self) -> NoReturn:
-> 1576         raise ValueError(
   1577             f"The truth value of a {type(self).__name__} is ambiguous. "
   1578             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1579         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
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)
   1574     @final
   1575     def __nonzero__(self) -> NoReturn:
-> 1576         raise ValueError(
   1577             f"The truth value of a {type(self).__name__} is ambiguous. "
   1578             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1579         )

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:

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

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

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

np.nan == np.nan
Out[59]: False
(df + df).equals(df * 2)
Out[60]: True

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

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

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

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

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

比较数组样式的对象

当将 pandas 数据结构与标量值进行比较时,可以方便地进行逐元素比较:

pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]: 
0     True
1    False
2    False
dtype: bool

pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False])

pandas 还可以处理长度相同的不同数组样式对象之间的逐元素比较:

pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]: 
0     True
1     True
2    False
dtype: bool

pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]: 
0     True
1     True
2    False
dtype: bool

尝试比较长度不同的 IndexSeries 对象将引发 ValueError:

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:6094, in Series._cmp_method(self, other, op)
   6091 res_name = ops.get_op_result_name(self, other)
   6093 if isinstance(other, Series) and not self._indexed_same(other):
-> 6094     raise ValueError("Can only compare identically-labeled Series objects")
   6096 lvalues = self._values
   6097 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

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:6094, in Series._cmp_method(self, other, op)
   6091 res_name = ops.get_op_result_name(self, other)
   6093 if isinstance(other, Series) and not self._indexed_same(other):
-> 6094     raise ValueError("Can only compare identically-labeled Series objects")
   6096 lvalues = self._values
   6097 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

合并重叠的数据集

有时候会遇到将两个相似的数据集组合在一起的问题,其中一个数据集中的值优先于另一个数据集。一个例子是表示特定经济指标的两个数据系列,其中一个被认为是“更高质量”的。然而,质量较低的系列可能在历史上延伸得更久,或者具有更完整的数据覆盖。因此,我们希望将两个 DataFrame 对象组合在一起,其中一个 DataFrame 中的缺失值会根据另一个 DataFrame 中的相同标签值进行条件填充。实现这个操作的函数是 combine_first(),我们来看一个例子:

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]}
)


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],
    }
)


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

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

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()

def combiner(x, y):
    return np.where(pd.isna(x), y, x)


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

描述性统计

SeriesDataFrame 上有大量用于计算描述性统计和其他相关操作的方法。其中大多数是聚合方法(因此产生较低维度的结果),如 sum()mean()quantile(),但其中一些方法,如 cumsum()cumprod(),产生与原始对象大小相同的对象。一般来说,这些方法都有一个 axis 参数,就像 ndarray.{sum, std, …} 一样,但是可以通过名称或整数指定轴:

  • Series:不需要轴参数
  • DataFrame:“index”(axis=0,默认值)、“columns”(axis=1)

例如:

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

df.mean(0)
Out[79]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

df.mean(1)
Out[80]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

所有这些方法都有一个 skipna 选项,用于指示是否排除缺失数据(默认为 True):

df.sum(0, skipna=False)
Out[81]: 
one           NaN
two      5.442353
three         NaN
dtype: float64

df.sum(axis=1, skipna=True)
Out[82]: 
a    3.167498
b    2.204786
c    3.401050
d   -0.333828
dtype: float64

结合广播/算术行为,可以简洁地描述各种统计过程,例如标准化(使数据的均值为零,标准差为1):

ts_stand = (df - df.mean()) / df.std()

ts_stand.std()
Out[84]: 
one      1.0
two      1.0
three    1.0
dtype: float64

xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

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 参数的影响。

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绝对值

| std | 样本标准差(修正过的贝塞尔标准差) |
| var | 无偏方差 |
| sem | 均值的标准误差 |
| skew | 样本偏度(三阶矩) |
| kurt | 样本峰度(四阶矩) |
| quantile | 样本分位数(%处的值) |
| cumsum | 累计求和 |
| cumprod | 累计乘积 |
| cummax | 累计最大值 |
| cummin | 累计最小值 |

需要注意的是,由于偶然的原因,一些 NumPy 方法(如 meanstdsum)在默认情况下会在 Series 输入时排除 NA 值:

np.mean(df["one"])
Out[88]: 0.8110935116651192

np.mean(df["one"].to_numpy())
Out[89]: nan

Series.nunique() 函数返回 Series 中唯一非 NA 值的数量:

series = pd.Series(np.random.randn(500))

series[20:500] = np.nan

series[10:20] = 5

series.nunique()
Out[93]: 11

数据汇总:describe

有一个方便的 describe() 函数,可以计算 Series 或 DataFrame 的各种汇总统计信息(当然不包括 NA 值):

series = pd.Series(np.random.randn(1000))

series[::2] = np.nan

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

frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])

frame.iloc[::2] = np.nan

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

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

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() 将给出关于唯一值的数量和最常出现的值的简单摘要:

s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])

s.describe()
Out[102]: 
count     9
unique    4
top       a
freq      5
dtype: object

请注意,在混合类型的 DataFrame 对象上,describe() 将限制摘要仅包括数值列或(如果没有数值列)仅包括分类列:

frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})

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

frame.describe(include=["object"])
Out[105]: 
          a
count     4
unique    2
top     Yes
freq      2

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

frame.describe(include="all")
Out[107]: 
          a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000

该功能依赖于 select_dtypes。有关接受的输入的详细信息,请参阅该处的文档。

最小值/最大值的索引

Series 和 DataFrame 上的 idxmin()idxmax() 函数计算具有最小和最大对应值的索引标签:

s1 = pd.Series(np.random.randn(5))

s1
Out[109]: 
0    1.118076
1   -0.352051
2   -1.242883
3   -1.277155
4   -0.641184
dtype: float64

s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)

df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])

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

df1.idxmin(axis=0)
Out[113]: 
A    2
B    3
C    1
dtype: int64

df1.idxmax(axis=1)
Out[114]: 
0    C
1    A
2    C
3    A
4    C
dtype: object

当有多个行(或列)与最小或最大值匹配时,idxmin()idxmax() 返回第一个匹配的索引:

df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))

df3
Out[116]: 
     A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

df3["A"].idxmin()
Out[117]: 'd'

注意

NumPy 中的 idxminidxmax 被称为 argminargmax

值计数(直方图)/ 众数

value_counts() Series 方法可以计算 1D 数组的直方图。它也可以用于常规数组的函数:

data = np.random.randint(0, 7, size=50)

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])

s = pd.Series(data)

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 参数选择子集。

data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}

frame = pd.DataFrame(data)

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 中出现最频繁的值(即众数):

s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

s5.mode()
Out[126]: 
0    3
1    7
dtype: int64

df5 = pd.DataFrame(
    {
        "A": np.random.randint(0, 7, size=50),
        "B": np.random.randint(-10, 15, size=50),
    }
)


df5.mode()
Out[128]: 
     A   B
0  1.0  -9
1  NaN  10
2  NaN  13

离散化和分位数

可以使用 cut()(基于值的分箱)和 qcut()(基于样本分位数的分箱)函数将连续值离散化:

arr = np.random.randn(20)

factor = pd.cut(arr, 4)

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]]

factor = pd.cut(arr, [-5, -1, 0, 1, 5])

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() 函数计算样本分位数。例如,我们可以将一些正态分布的数据等分为相等大小的四分位数:

arr = np.random.randn(30)

factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])

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]]

我们还可以传入无穷大的值来定义分箱:

arr = np.random.randn(20)

factor = pd.cut(arr, [-np.inf, 0, np.inf])

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]]

函数应用

表格级函数应用

可以将DataFramesSeries传递给函数。但是,如果需要在链式调用中调用函数,请考虑使用pipe()方法。

首先进行一些设置:

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


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


df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]})

extract_city_nameadd_country_name是接受DataFrames并返回DataFrames的函数。

现在比较以下两种方式:

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

等同于:

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。如果您希望应用的函数将其数据作为第二个参数(例如)接收,那么可以向pipe提供一个(callable, data_keyword)的元组。.pipeDataFrame路由到元组中指定的参数。

例如,我们可以使用statsmodels拟合回归。他们的API首先期望一个公式,然后是第二个参数data作为DataFrame。我们将函数和关键字对(sm.ols, 'data')传递给pipe

import statsmodels.formula.api as sm

bb = pd.read_csv("data/baseball.csv", index_col="id")

(
    bb.query("h > 0")
    .assign(ln_h=lambda df: np.log(df.h))
    .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
    .fit()
    .summary()
)

Out[149]:
<class 'statsmodels.iolib.summary.Summary'>
"""
                           OLS Regression Results
==============================================================================
Dep. Variable:                     hr   R-squared:                       0.685
Model:                            OLS   Adj. R-squared:                  0.665
Method:                 Least Squares   F-statistic:                     34.28
Date:                Tue, 22 Nov 2022   Prob (F-statistic):           3.48e-15
Time:                        05:34:17   Log-Likelihood:                -205.92
No. Observations:                  68   AIC:                             421.8
Df Residuals:                      63   BIC:                             432.9
Df Model:                           4
Covariance Type:            nonrobust
===============================================================================
                  coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept   -8484.7720   4664.146     -1.819      0.074   -1.78e+04     835.780
C(lg)[T.NL]    -2.2736      1.325     -1.716      0.091      -4.922       0.375
ln_h           -1.3542      0.875     -1.547      0.127      -3.103       0.395
year            4.2277      2.324      1.819      0.074      -0.417       8.872
g               0.1841      0.029      6.258      0.000       0.125       0.243
==============================================================================
Omnibus:                       10.875   Durbin-Watson:                   1.999
Prob(Omnibus):                  0.004   Jarque-Bera (JB):               17.298
Skew:                           0.537   Prob(JB):                     0.000175
Kurtosis:                       5.225   Cond. No.                     1.49e+07
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
"""

pipe方法受到Unix管道和最近的dplyrmagrittr的启发,它们引入了R中流行的(%>%)(读作pipe)运算符。这里的pipe的实现非常干净,感觉在Python中非常自然。我们鼓励您查看pipe()的源代码。

逐行或逐列应用函数

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

df.apply(lambda x: np.mean(x))
Out[145]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

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

df.apply(lambda x: x.max() - x.min())
Out[147]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

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

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()方法还可以根据字符串方法名进行分派。

df.apply("mean")
Out[150]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

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()结合一些巧妙的方法可以用来回答关于数据集的许多问题。例如,假设我们想要提取每列的最大值所在的日期:

tsdf = pd.DataFrame(
    np.random.randn(1000, 3),
    columns=["A", "B", "C"],
    index=pd.date_range("1/1/2000", periods=1000),
)


tsdf.apply(lambda x: x.idxmax())
Out[153]: 
A   2000-08-06
B   2001-01-18
C   2001-07-18
dtype: datetime64[ns]

您还可以将其他参数和关键字参数传递给apply()方法。

def subtract_and_divide(x, sub, divide=1):
    return (x - sub) / divide


df_udf = pd.DataFrame(np.ones((2, 2)))

df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]: 
          0         1
0 -1.333333 -1.333333
1 -1.333333 -1.333333

另一个有用的功能是能够将Series方法传递给每列或每行执行某些Series操作:

tsdf = pd.DataFrame(
    np.random.randn(10, 3),
    columns=["A", "B", "C"],
    index=pd.date_range("1/1/2000", periods=10),
)


tsdf.iloc[3:7] = np.nan

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

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 APIwindow APIresample API。聚合的入口是 DataFrame.aggregate(),或者是别名 DataFrame.agg()

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

tsdf = pd.DataFrame(
    np.random.randn(10, 3),
    columns=["A", "B", "C"],
    index=pd.date_range("1/1/2000", periods=10),
)


tsdf.iloc[3:7] = np.nan

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

tsdf.agg(lambda x: np.sum(x))
Out[164]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

tsdf.agg("sum")
Out[165]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

# 这些与 `.sum()` 等效,因为我们在单个函数上进行聚合
tsdf.sum()
Out[166]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

对于 Series 的单个聚合将返回一个标量值:

tsdf["A"].agg("sum")
Out[167]: 3.033606102414146
使用多个函数进行聚合

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

tsdf.agg(["sum"])
Out[168]: 
            A         B        C
sum  3.033606 -1.803879  1.57551

多个函数会产生多行:

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

tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64

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

tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64

传递一个命名函数将产生该名称的行:

def mymean(x):
    return x.mean()


tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64
使用字典进行聚合

将列名的字典传递给标量或标量列表,以及 DataFrame.agg,可以自定义应用于哪些列的哪些函数。请注意,结果没有特定的顺序,可以使用 OrderedDict 来保证顺序。

tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64

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

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 函数

from functools import partial

q_25 = partial(pd.Series.quantile, q=0.25)

q_25.__name__ = "25%"

q_75 = partial(pd.Series.quantile, q=0.75)

q_75.__name__ = "75%"

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 非常相似。

我们创建一个与上面部分中使用的类似的框架。

tsdf = pd.DataFrame(
    np.random.randn(10, 3),
    columns=["A", "B", "C"],
    index=pd.date_range("1/1/2000", periods=10),
)


tsdf.iloc[3:7] = np.nan

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 函数、
将多个函数传递给 Series 将生成一个 DataFrame。生成的列名将是转换函数。

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
使用字典进行转换

传递一个函数字典将允许按列进行选择性转换。

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。

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 函数。例如:

df4 = df.copy()

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

def f(x):
    return len(str(x))


df4["one"].map(f)
Out[197]: 
a    18
b    19
c    18
d     3
Name: one, dtype: int64

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() 还有一个额外的功能;它可以用于轻松“链接”或“映射”由第二个 Series 定义的值。这与合并/连接功能密切相关:

s = pd.Series(
    ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
)


t = pd.Series({"six": 6.0, "seven": 7.0})

s
Out[201]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

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)标记
  • 如果指定,使用逻辑填充缺失标签的数据(与处理时间序列数据高度相关)

这是一个简单的例子:

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

s
Out[204]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
e   -1.326508
dtype: float64

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,您可以同时重新索引索引和列:

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

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,可以执行以下操作:

rs = s.reindex(df.index)

rs
Out[209]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
dtype: float64

rs.index is df.index
Out[210]: True

这意味着重新索引的 Series 的索引是与 DataFrame 的索引相同的 Python 对象。

DataFrame.reindex() 还支持“轴样式”调用约定,其中您指定一个单独的 labels 参数和它适用的 axis

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

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 / Advanced Indexing 是一种更简洁的重新索引方法。

注意

在编写性能敏感的代码时,有充分的理由花费一些时间成为重新索引的高手:许多操作在预对齐数据上更快。添加两个未对齐的 DataFrame 内部触发重新索引步骤。对于探索性分析,您几乎不会注意到差异(因为 reindex 已经经过了大量优化),但是当 CPU 周期很重要时,偶尔在这里和那里撒上几个显式的 reindex 调用可能会产生影响。

重新索引以与另一个对象对齐

您可能希望获取一个对象并重新索引其轴,使其与另一个对象的标签相同。虽然语法很简单但冗长,但这是一个常见的操作,因此提供了 reindex_like() 方法以简化此过程:

df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])

df3 = df2 - df2.mean()

df2
Out[215]: 
        one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

df3
Out[216]: 
        one       two
a  0.583888  0.051514
b -0.468040  0.191120
c -0.115848 -0.242634

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:

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

s1 = s[:4]

s2 = s[1:]

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)

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)

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 方法将默认应用于索引和列:

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  
| 方法           | 操作                            |
| ---------------- | --------------------------------- |
| pad / ffill      | 向前填充值               |
| bfill / backfill | 向后填充值              |
| nearest          | 从最近的索引值填充 |

我们在一个简单的 Series 上演示这些填充方法:

```python
rng = pd.date_range("1/3/2000", periods=8)

ts = pd.Series(np.random.randn(8), index=rng)

ts2 = ts.iloc[[0, 3, 6]]

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

ts2
Out[231]: 
2000-01-03    0.183051
2000-01-06    2.395489
2000-01-09    0.733639
Freq: 3D, dtype: float64

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

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

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

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](https://pandas.pydata.org/docs/user_guide/missing_data.html#missing-data-interpolate)也可以实现相同的结果:

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 指定连续匹配的最大计数:

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

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

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() 函数。它从轴中删除一组标签:

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

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

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

请注意,以下方法也可以实现相同的效果,但不够明显/简洁:

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)或任意函数重新标记轴。

s
Out[243]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
dtype: float64

s.rename(str.upper)
Out[244]: 
A   -0.186646
B   -1.692424
C   -0.303893
D   -1.425662
E    1.114285
dtype: float64

如果传递一个函数,它必须在任何标签调用时返回一个值(并且必须产生一组唯一值)。也可以使用字典或 Series:

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

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

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 属性。

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 的特定名称(而不是标签)。

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"]
    ),
)


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

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

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 会给出列名:

df = pd.DataFrame(
    {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
)


for col in df:
    print(col)

col1
col2

pandas 对象还具有类似字典的 items() 方法,用于迭代(键,值)对。

要迭代 DataFrame 的行,可以使用以下方法:

  • iterrows():将 DataFrame 的行迭代为(索引,Series)对。这将行转换为 Series 对象,可能会改变 dtypes,并且会有一些性能影响。
  • itertuples():将 DataFrame 的行迭代为具有命名字段的命名元组。这比 iterrows() 快得多,并且在大多数情况下更适合用于迭代 DataFrame 的值。

警告

通过 pandas 对象进行迭代通常速度较慢。在许多情况下,手动迭代行是不必要的,并且可以通过以下方法之一避免:

  • 当你有一个不能一次处理整个 DataFrame/Series 的函数时,最好使用 apply() 而不是遍历值。请参阅关于函数应用的文档。
  • 如果你需要对值进行迭代操作,但性能很重要,可以考虑使用 cython 或 numba 编写内部循环。请参阅提高性能部分,了解一些这种方法的示例。

警告

绝对不应该修改你正在迭代的内容。这在所有情况下都不能保证有效。根据数据类型的不同,迭代器返回的是一个副本而不是视图,对其进行写入操作将没有任何效果!

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

df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})

for index, row in df.iterrows():
    row["a"] = 10


df
Out[257]: 
   a  b
0  1  a
1  2  b
2  3  c

items

与字典类似的接口一致,items() 遍历键值对:

  • Series: (索引, 标量值) 对
  • DataFrame: (列名, Series) 对

例如:

for label, ser in df.items():
    print(label)
    print(ser)

a
0    1
1    2
2    3
Name: a, dtype: int64
b
0    a
1    b
2    c
Name: b, dtype: object

iterrows

iterrows() 允许你遍历 DataFrame 的行,每行作为 Series 对象返回。它返回一个迭代器,每次返回索引值和包含每行数据的 Series:

for row_index, row in df.iterrows():
    print(row_index, row, sep="\n")

0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object

注意

因为 iterrows() 对于每一行返回一个 Series,所以它不会在行之间保留数据类型(对于 DataFrame,列之间的数据类型是保留的)。例如,

df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])

df_orig.dtypes
Out[261]: 
int        int64
float    float64
dtype: object

row = next(df_orig.iterrows())[1]

row
Out[263]: 
int      1.0
float    1.5
Name: 0, dtype: float64

row 中的所有值,作为一个 Series,现在都被转换为浮点数,包括列 x 中的原始整数值:

row["int"].dtype
Out[264]: dtype('float64')

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

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

例如,一个简单的转置 DataFrame 的方法是:

df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})

print(df2)
   x  y
0  1  4
1  2  5
2  3  6

print(df2.T)
   0  1  2
x  1  2  3
y  4  5  6

df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})

print(df2_t)
   0  1  2
x  1  2  3
y  4  5  6

itertuples

itertuples() 方法将返回一个迭代器,每次返回一个命名元组,该元组对应 DataFrame 中的一行。元组的第一个元素是行的索引值,其余的值是行的数据。

例如:

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() 更快。

注意

如果列名无效、重复或以下划线开头,列名将被重命名为位置名称。当列数较多(>255)时,将返回常规元组。

.dt 访问器

Series 有一个访问器,可以简洁地返回 Series 的值的类似于日期时间的属性,如果它是一个日期时间/周期类似的 Series。这将返回一个 Series,索引与现有的 Series 相同。

# datetime
s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))

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]

s.dt.hour
Out[274]: 
0    9
1    9
2    9
3    9
dtype: int32

s.dt.second
Out[275]: 
0    12
1    12
2    12
3    12
dtype: int32

s.dt.day
Out[276]: 
0    1
1    2
2    3
3    4
dtype: int32

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

s[s.dt.day == 2]
Out[277]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns]

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

stz = s.dt.tz_localize("US/Eastern")

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]

stz.dt.tz
Out[280]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>

你还可以链式地进行这些类型的操作:

s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]: 
0   2013-01-01 04:10:12-05:00
1   2013-01-02 04:10:12-05:00
2   2013-01-03 04:10:12-05:00
3   2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]

你还可以使用 Series.dt.strftime() 将日期时间值格式化为字符串,它支持与标准 strftime() 相同的格式。

# DatetimeIndex
s = pd.Series(pd.date_range("20130101", periods=4))

s
Out[283]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: datetime64[ns]

s.dt.strftime("%Y/%m/%d")
Out[284]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object
# PeriodIndex
s = pd.Series(pd.period_range("20130101", periods=4))

s
Out[286]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

s.dt.strftime("%Y/%m/%d")
Out[287]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object

.dt 访问器适用于周期和时间差类型。

# period
s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))

s
Out[289]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

s.dt.year
Out[290]: 
0    2013
1    2013
2    2013
3    2013
dtype: int64

s.dt.day
Out[291]: 
0    1
1    2
2    3
3    4
dtype: int64
# timedelta
s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))

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]

s.dt.days
Out[294]: 
0    1
1    1
2    1
3    1
dtype: int64

s.dt.seconds
Out[295]: 
0    5
1    6
2    7
3    8
dtype: int32

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 属性访问,并且通常具有与等效的(标量)内置字符串方法相匹配的名称。例如:

s = pd.Series(
 ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
)


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 -dtypeSeries。pandas 1.0 添加了 StringDtype,专门用于字符串。有关更多信息,请参阅文本数据类型

请参阅矢量化字符串方法获取完整描述。

排序

pandas 支持三种排序方式:按索引标签排序、按列值排序以及按索引标签和列值的组合排序。

按索引排序

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"]),
    }
)


unsorted_df = df.reindex(
    index=["a", "d", "c", "b"], columns=["three", "two", "one"]
)


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
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

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

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
unsorted_df["three"].sort_index()
Out[305]: 
a         NaN
b   -0.098217
c    1.273388
d   -0.252916
Name: three, dtype: float64

按索引排序还支持 key 参数,该参数接受一个可调用函数,用于应用于正在排序的索引。对于 MultiIndex 对象,key 逐级应用于由 level 指定的级别。

s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
    list("ab")
)


s1
Out[307]: 
     c
a b   
B 1  2
a 2  3
C 3  4
s1.sort_index(level="a")
Out[308]: 
     c
a b   
B 1  2
C 3  4
a 2  3

s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]: 
     c
a b   
a 2  3
B 1  2
C 3  4

有关按值排序的详细信息,请参见 value sorting

按值排序

Series.sort_values() 方法用于按值对 Series 进行排序。DataFrame.sort_values() 方法用于按列或行值对 DataFrame 进行排序。可选的 by 参数可以用于指定一个或多个列来确定排序顺序。

df1 = pd.DataFrame(
    {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
)


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 参数可以接受一个列名列表,例如:

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 值进行特殊处理:

s[2] = np.nan

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

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 参数,该参数接受一个可调用函数,用于应用于正在排序的值。

s1 = pd.Series(["B", "a", "C"])
s1.sort_values()
Out[317]: 
0    B
2    C
1    a
dtype: object

s1.sort_values(key=lambda x: x.str.lower())
Out[318]: 
1    a
0    B
2    C
dtype: object

key 将接收值的 Series 并应返回具有相同形状的转换值的 Series 或数组。对于 DataFrame 对象,key 逐列应用,因此 key 应仍然期望一个 Series 并返回一个 Series,例如:

df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]})
df.sort_values(by="a")
Out[320]: 
   a  b
0  B  1
2  C  3
1  a  2

df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]: 
   a  b
1  a  2
0  B  1
2  C  3

可以使用每列的名称或类型来对不同列应用不同的函数。

按索引和值排序

作为 by 参数传递给 DataFrame.sort_values() 的字符串可以引用列名或索引级别名称。

# Build MultiIndex
idx = pd.MultiIndex.from_tuples(
    [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
)


idx.names = ["first", "second"]

# Build DataFrame
df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)

df_multi
Out[325]: 
              A
first second   
a     1       6
      2       5
      2       4
b     2       3
      1       2
      1       1

按 ‘second’(索引)和 ‘A’(列)排序

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()

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

ser.searchsorted([0, 3])
Out[328]: array([0, 2])

ser.searchsorted([0, 4])
Out[329]: array([0, 3])

ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])

ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])

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

ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2])

最小/最大值

Seriesnsmallest()nlargest() 方法,用于返回最小或最大值。对于大型 Series,这比对整个 Series 进行排序并在结果上调用 head(n) 要快得多。

s = pd.Series(np.random.permutation(10))

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

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

s.nsmallest(3)
Out[337]: 
1    0
4    1
0    2
dtype: int64

s.nlargest(3)
Out[338]: 
6    9
8    8
3    7
dtype: int64

DataFrame 也有 nlargestnsmallest 方法。

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],
    }
)


df.nlargest(3, "a")
Out[340]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

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

df.nsmallest(3, "a")
Out[342]: 
   a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0

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 时,必须明确指定排序,并完全指定所有级别以进行排序。

df1.columns = pd.MultiIndex.from_tuples(
    [("a", "one"), ("a", "two"), ("b", "three")]
)


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

复制

pandas 对象上的 copy() 方法会复制底层数据(尽管不会复制轴索引,因为它们是不可变的),并返回一个新对象。请注意,很少需要复制对象。例如,只有少数几种方法可以 原地 修改 DataFrame:

  • 插入、删除或修改列。
  • 分配给 indexcolumns 属性。
  • 对于同质数据,通过 values 属性或高级索引直接修改值。

需要明确的是,没有 pandas 方法会有修改数据的副作用;几乎每个方法都返回一个新对象,保持原始对象不变。如果数据被修改,那是因为你明确地这样做了。

dtypes

在大多数情况下,pandas 使用 NumPy 数组和 dtypes 来处理 Series 或 DataFrame 的单个列。NumPy 提供对 floatintbooltimedelta64[ns]datetime64[ns](请注意,NumPy 不支持带时区的日期时间)的支持。

pandas 和第三方库在几个地方 扩展 了 NumPy 的类型系统。本节介绍了 pandas 在内部进行的扩展。有关如何编写与 pandas 兼容的扩展的详细信息,请参见 Extension types。有关已实现扩展的第三方库列表,请参见 生态系统页面

下表列出了 pandas 的所有扩展类型。对于需要 dtype 参数的方法,可以按照指示将字符串指定为参数。有关每种类型的更多信息,请参见相应的文档部分。

| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [tz-aware datetime](https://pandas.pydata.org/docs/user_guide/timeseries.html#timeseries-timezone) | [`DatetimeTZDtype`](https://pandas.pydata.org/docs/reference/api/pandas.DatetimeTZDtype.html#pandas.DatetimeTZDtype) | [`Timestamp`](https://pandas.pydata.org/docs/reference/api/pandas.Timestamp.html#pandas.Timestamp) | [`arrays.DatetimeArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.DatetimeArray.html#pandas.arrays.DatetimeArray) | `'datetime64[ns, <tz>]'`                                     |
| [Categorical](https://pandas.pydata.org/docs/user_guide/categorical.html#categorical) | [`CategoricalDtype`](https://pandas.pydata.org/docs/reference/api/pandas.CategoricalDtype.html#pandas.CategoricalDtype) | (none)                                                       | [`Categorical`](https://pandas.pydata.org/docs/reference/api/pandas.Categorical.html#pandas.Categorical) | `'category'`                                                 |
| [period (time spans)](https://pandas.pydata.org/docs/user_guide/timeseries.html#timeseries-periods) | [`PeriodDtype`](https://pandas.pydata.org/docs/reference/api/pandas.PeriodDtype.html#pandas.PeriodDtype) | [`Period`](https://pandas.pydata.org/docs/reference/api/pandas.Period.html#pandas.Period) | [`arrays.PeriodArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.PeriodArray.html#pandas.arrays.PeriodArray)`'Period[<freq>]'` | `'period[<freq>]'`,                                          |
| [sparse](https://pandas.pydata.org/docs/user_guide/sparse.html#sparse) | [`SparseDtype`](https://pandas.pydata.org/docs/reference/api/pandas.SparseDtype.html#pandas.SparseDtype) | (none)                                                       | [`arrays.SparseArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.SparseArray.html#pandas.arrays.SparseArray) | `'Sparse'`, `'Sparse[int]'`,`'Sparse[float]'`                |
| [intervals](https://pandas.pydata.org/docs/user_guide/advanced.html#advanced-intervalindex) | [`IntervalDtype`](https://pandas.pydata.org/docs/reference/api/pandas.IntervalDtype.html#pandas.IntervalDtype) | [`Interval`](https://pandas.pydata.org/docs/reference/api/pandas.Interval.html#pandas.Interval) | [`arrays.IntervalArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.IntervalArray.html#pandas.arrays.IntervalArray) | `'interval'`, `'Interval'`,`'Interval[<numpy_dtype>]'`,`'Interval[datetime64[ns,<tz>]]'`,`'Interval[timedelta64[<freq>]]'` |
| [nullable integer](https://pandas.pydata.org/docs/user_guide/integer_na.html#integer-na) | [`Int64Dtype`](https://pandas.pydata.org/docs/reference/api/pandas.Int64Dtype.html#pandas.Int64Dtype), … | (none)                                                       | [`arrays.IntegerArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.IntegerArray.html#pandas.arrays.IntegerArray) | `'Int8'`, `'Int16'`, `'Int32'`,`'Int64'`, `'UInt8'`, `'UInt16'`,`'UInt32'`, `'UInt64'` |
| [nullable float](https://pandas.pydata.org/docs/reference/arrays.html#api-arrays-float-na) | [`Float64Dtype`](https://pandas.pydata.org/docs/reference/api/pandas.Float64Dtype.html#pandas.Float64Dtype), … | (none)                                                       | [`arrays.FloatingArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.FloatingArray.html#pandas.arrays.FloatingArray) | `'Float32'`, `'Float64'`                                     |
| [Strings](https://pandas.pydata.org/docs/user_guide/text.html#text) | [`StringDtype`](https://pandas.pydata.org/docs/reference/api/pandas.StringDtype.html#pandas.StringDtype) | [`str`](https://docs.python.org/3/library/stdtypes.html#str) | [`arrays.StringArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.StringArray.html#pandas.arrays.StringArray) | `'string'`                                                   |
| [Boolean (with NA)](https://pandas.pydata.org/docs/reference/arrays.html#api-arrays-bool) | [`BooleanDtype`](https://pandas.pydata.org/docs/reference/api/pandas.BooleanDtype.html#pandas.BooleanDtype) | [`bool`](https://docs.python.org/3/library/functions.html#bool) | [`arrays.BooleanArray`](https://pandas.pydata.org/docs/reference/api/pandas.arrays.BooleanArray.html#pandas.arrays.BooleanArray) | `'boolean'`                                                  |

pandas 有两种存储字符串的方式。

  1. object 数据类型,可以存储任何 Python 对象,包括字符串。
  2. StringDtype,专门用于字符串。

通常,我们建议使用 StringDtype。更多信息请参见文本数据类型

最后,任意对象可以使用 object 数据类型存储,但应尽量避免使用(出于性能和与其他库和方法的互操作性考虑)。请参见对象转换

DataFrame 的方便属性 dtypes 返回每列的数据类型的 Series。

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"),
    }
)


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

dft.dtypes
Out[348]: 
A          float64
B            int64
C           object
D    datetime64[s]
E          float32
F             bool
G             int8
dtype: object

对于 Series 对象,可以使用 dtype 属性。

dft["A"].dtype
Out[349]: dtype('float64')

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

# 这些整数被强制转换为浮点数
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

# 字符串数据会强制转换为 ``object`` 数据类型
pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]: 
0      1
1      2
2      3
3    6.0
4    foo
dtype: object

可以通过调用 DataFrame.dtypes.value_counts() 来查找 DataFrame 中每种类型的列数。

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

数值数据类型会传播并可以共存于 DataFrame 中。如果传递了数据类型(可以直接通过 dtype 关键字、传递的 ndarray 或传递的 Series),则它将在 DataFrame 操作中保留。此外,不同的数值数据类型将不会合并。以下示例将让您了解一下。

df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")

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

df1.dtypes
Out[355]: 
A    float32
dtype: object

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)
    }
)


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

df2.dtypes
Out[358]: 
A    float16
B    float64
C      uint8
dtype: object

默认值

默认情况下,整数类型为 int64,浮点类型为 float64不受平台(32 位或 64 位)的影响。以下所有示例都将得到 int64 数据类型。

pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]: 
a    int64
dtype: object

pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]: 
a    int64
dtype: object

pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]: 
a    int64
dtype: object

请注意,当创建数组时,NumPy 会选择平台相关的类型。以下示例在 32 位平台上将得到 int32

frame = pd.DataFrame(np.array([1, 2]))

类型转换

当与其他类型组合时,类型可能会被转换,即从当前类型(例如 int 转换为 float)。

df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

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

df3.dtypes
Out[365]: 
A    float32
B    float64
C    float64
dtype: object

DataFrame.to_numpy() 将返回 dtype 的最低公共分母,即能够容纳结果同质 dtyped NumPy 数组中所有类型的 dtype。这可能会导致一些转换

df3.to_numpy().dtype
Out[366]: dtype('float64')

astype

向上转型始终遵循NumPy的规则。如果在操作中涉及到两种不同的数据类型,那么较为“通用”的数据类型将作为操作的结果。

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

df3.dtypes
Out[368]: 
A    float32
B    float64
C    float64
dtype: object

# 转换数据类型
df3.astype("float32").dtypes
Out[369]: 
A    float32
B    float32
C    float32
dtype: object

使用astype()将一部分列转换为指定的数据类型。

dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)

dft
Out[372]: 
   a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

dft.dtypes
Out[373]: 
a    uint8
b    uint8
c    int64
dtype: object

通过将字典传递给astype(),将特定的列转换为特定的数据类型。

dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})

dft1 = dft1.astype({"a": np.bool_, "c": np.float64})

dft1
Out[376]: 
       a  b    c
0   True  4  7.0
1  False  5  8.0
2   True  6  9.0

dft1.dtypes
Out[377]: 
a       bool
b      int64
c    float64
dtype: object

注意

当尝试使用astype()loc()将一部分列转换为指定的数据类型时,会发生向上转型。

loc()会尝试适应我们要分配给当前数据类型的内容,而[]将从右侧获取数据类型并覆盖它们。因此,以下代码会产生意外的结果。

dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]: 
a    uint8
b    uint8
dtype: object

dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)

dft.dtypes
Out[381]: 
a    int64
b    int64
c    int64
dtype: object

对象转换

pandas提供了各种函数,用于尝试将object数据类型强制转换为其他类型。在数据已经是正确类型但存储在object数组中的情况下,可以使用DataFrame.infer_objects()Series.infer_objects()方法进行软转换为正确类型。

import datetime

df = pd.DataFrame(
 [
     [1, 2],
     ["a", "b"],
     [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
 ]
)


df = df.T

df
Out[385]: 
0  1                    2
0  1  a  2016-03-02 00:00:00
1  2  b  2016-03-02 00:00:00

df.dtypes
Out[386]: 
0    object
1    object
2    object
dtype: object

由于数据被转置,原始推断将所有列存储为对象,infer_objects将其更正。

df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object

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

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

    m = ["1.1", 2, 3]
    
    pd.to_numeric(m)
    Out[389]: array([1.1, 2. , 3. ])
    
  • to_datetime()(转换为日期时间对象)

    import datetime
    
    m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
    
    pd.to_datetime(m)
    Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
    
  • to_timedelta()(转换为时间差对象)

    m = ["5us", pd.Timedelta("1day")]
    
    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(对于数值)。如果您正在读取的数据大部分是所需的数据类型(例如数值、日期时间),但偶尔有不符合规范的元素混合在一起,这可能会很有用,您希望将其表示为缺失值:

import datetime

m = ["apple", datetime.datetime(2016, 3, 2)]

pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

m = ["apple", 2, 3]

pd.to_numeric(m, errors="coerce")
Out[399]: array([nan,  2.,  3.])

m = ["apple", pd.Timedelta("1day")]

pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)

除了对象转换之外,to_numeric()还提供了另一个参数downcast,可以选择将新的(或已有的)数值数据类型降级为较小的数据类型,以节省内存:

m = ["1", 2, 3]

pd.to_numeric(m, downcast="integer")  # 最小的有符号整数数据类型
Out[403]: array([1, 2, 3], dtype=int8)

pd.to_numeric(m, downcast="signed")  # 与'integer'相同
Out[404]: array([1, 2, 3], dtype=int8)

pd.to_numeric(m, downcast="unsigned")  # 最小的无符号整数数据类型
Out[405]: array([1, 2, 3], dtype=uint8)

pd.to_numeric(m, downcast="float")  # 最小的浮点数数据类型
Out[406]: array([1., 2., 3.], dtype=float32)

由于这些方法仅适用于一维数组、列表或标量,无法直接用于多维对象(如DataFrame)。但是,使用apply(),我们可以高效地在每列上“应用”函数:

import datetime

df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")

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

df.apply(pd.to_datetime)
Out[410]: 
           0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")

df
Out[412]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

df.apply(pd.to_numeric)
Out[413]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")

df
Out[415]: 
     0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

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的情况下保持不变。参见Support for integer NA

dfi = df3.astype("int32")

dfi["E"] = 1

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

dfi.dtypes
Out[420]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

casted = dfi[dfi > 0]

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

casted.dtypes
Out[423]: 
A    float64
B    float64
C      int32
E      int64
dtype: object

而浮点数数据类型不会改变。

dfa = df3.copy()

dfa["A"] = dfa["A"].astype("float32")

dfa.dtypes
Out[426]: 
A    float32
B    float64
C    float64
dtype: object

casted = dfa[df2 > 0]

casted
Out[428]: 
          A         B      C

并且数据类型如下:

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 类型的列:

df.select_dtypes(include=[bool])
Out[437]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

你也可以传入 NumPy 数据类型层次结构 中的数据类型名称:

df.select_dtypes(include=["bool"])
Out[438]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

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

例如,选择所有数字和布尔类型的列,同时排除无符号整数:

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 数据类型:

df.select_dtypes(include=["object"])
Out[440]: 
  string
0      a
1      b
2      c

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

def subdtypes(dtype):
    subs = dtype.__subclasses__()
    if not subs:
        return dtype
    return [dtype, [subdtypes(dt) for dt in subs]]

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

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 层次结构中,上述函数中不会显示出来。

Pandas 2 使用指南导读

Pandas 2 使用指南:1、十分钟入门Pandas

Pandas 2 使用指南:2、数据结构简介

Pandas 2 使用指南:3、基本功能

Pandas 2 使用指南:4、IO工具(文本、CSV、HDF5等)

Pandas 2 使用指南:5、PyArrow 功能介绍

Pandas 2 使用指南: 6、索引和选择数据

Pandas 2 使用指南:7、多级索引 / 高级索引

Pandas 2 使用指南:8、写时复制(Copy-on-Write,CoW)

Pandas 2 使用指南:9、合并、连接、串联和比较

Pandas 2 使用指南:10、重塑和透视表ReShapingand Pivot Tables

Pandas 2 使用指南:11、处理文本数据 Working with text data

Pandas 2 使用指南:12、处理缺失数据Working with missing data

Pandas 2 使用指南: 13、重复标签 Duplicate Labels

Pandas 2 使用指南:14、分类数据 Categorical data
Pandas 2 使用指南:15、可空整数数据类型、可空布尔数据类型

Pandas 2 使用指南:16、图表可视化

Pandas 2 使用指南:17、表格可视化

Pandas 2 使用指南:18、Groupby:拆分-应用-合并 split-apply-combine

Pandas 2 使用指南:19、窗口操作 Windowing operations

Pandas 2 使用指南:20、时间序列/日期功能
Pandas 2 使用指南:21、时间差 Timedelta

Pandas 2 使用指南:22、选项和设置

Pandas 2 使用指南:23、提升性能 Enhancing performance

Pandas 2 使用指南:24、大规模数据集的扩展

Pandas 2 使用指南:25、稀疏数据结构 Sparse data structures

Pandas 2 使用指南:26、常见问题解答 (FAQ)

Pandas 2 使用指南:27、Cookbook

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值