基本功能#

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

头部和尾部#

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

属性和底层数据#

pandas 对象拥有许多属性,可以让你访问元数据

  • shape:提供对象的轴维度,与 ndarray 一致

  • 轴标签
    • Series:*index*(仅一个轴)

    • DataFrame:*index*(行)和 *columns*(列)

请注意,这些属性可以安全地被赋值!

pandas 对象(Index 、Series 、DataFrame )可以被视为数组的容器,这些数组保存实际数据并执行实际计算。对于许多类型,底层数组是 numpy.ndarray 。然而,pandas 和第三方库可能会*扩展* NumPy 的类型系统,以支持自定义数组(请参阅 dtypes )。

要获取 Index 或 Series 中的实际数据,请使用 .array 属性。

array 始终是一个 ExtensionArray 。 ExtensionArray 的具体细节以及 pandas 使用它的原因,超出了本引言的范围。请参阅 dtypes 获取更多信息。

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

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

to_numpy() 提供了一些控制结果 numpy.ndarray 的 dtype 的方法。例如,考虑带有时间区的日期时间。NumPy 没有表示时区感知日期时间的 dtype,因此有两种可能的有用表示:

  1. 一个 object-dtype 的 numpy.ndarray ,其中包含 Timestamp 对象,每个对象都有正确的 tz。

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

使用 dtype=object 可以保留时区。

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

获取 DataFrame 中的“原始数据”可能稍微复杂一些。当你的 DataFrame 只有一种数据类型用于所有列时,DataFrame.to_numpy() 将返回底层数据:

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

备注

当处理异质数据时,结果 ndarray 的 dtype 将被选择以适应所有涉及的数据。例如,如果涉及字符串,结果将是 object dtype。如果只有浮点数和整数,则结果数组的 dtype 将是 float。

过去,pandas 推荐使用 Series.values 或 DataFrame.values 来提取 Series 或 DataFrame 的数据。你仍然会在旧代码库和在线资源中找到对它们的引用。今后,我们建议避免使用 .values,而是使用 .array 或 .to_numpy()。.values 有以下缺点:

  1. 当您的 Series 包含 extension type 时, 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 例程,在处理包含 nan 的数组时速度特别快。

以下是一个示例(使用 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

强烈建议您安装这两个库。有关更多安装信息,请参阅 Recommended Dependencies 部分。

这两个库默认启用,您可以设置选项来控制:

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 关键字选择匹配 索引 或 列:

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

Series 和 Index 也支持 divmod() 内置函数。此函数同时执行整除和取模运算,返回一个与左侧操作数类型相同的二元组。例如:

我们也可以进行逐元素 divmod() :

缺失数据/带填充值的操作#

在 Series 和 DataFrame 中,算术函数可以选择输入一个 fill_value,即一个在至多一个位置的值缺失时进行替换的值。例如,在添加两个 DataFrame 对象时,您可能希望将 NaN 视为 0,除非两个 DataFrame 在该位置都缺失该值,在这种情况下结果将是 NaN(如果需要,您之后可以使用 fillna 将 NaN 替换为其他值)。

灵活的比较#

Series 和 DataFrame 具有与上述二进制算术操作行为相似的二进制比较方法 eq、ne、lt、gt、le 和 ge:

这些操作会产生一个与左侧输入相同类型的 pandas 对象,其 dtype 为 bool。这些 boolean 对象可用于索引操作,请参阅 Boolean indexing 部分。

布尔归约#

您可以应用归约:empty 、any() 、all() 和 bool() 来总结布尔结果。

您可以将结果归约为最终的布尔值。

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

警告

断言 pandas 对象的真值将引发错误,因为对空值或值的测试是模糊的。

有关更详细的讨论,请参阅 gotchas 。

比较对象是否相等#

通常,您会发现计算同一结果有多种方法。作为一个简单的例子,考虑 df + df 和 df * 2。要测试这两种计算是否产生相同的结果,鉴于上述工具,您可能会想象使用 (df + df == df * 2).all()。但实际上,这个表达式为 False:

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

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

请注意,Series 或 DataFrame 的索引需要以相同的顺序排列,相等性才为 True:

比较类数组对象#

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

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

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

合并重叠的数据集#

偶尔出现的一个问题是组合两个相似的数据集,其中一个数据集中的值优先于另一个数据集。一个例子是两个代表特定经济指标的数据系列,其中一个被认为“质量更高”。然而,较低质量的系列可能历史更悠久或数据覆盖更完整。因此,我们希望组合两个 DataFrame 对象,其中一个 DataFrame 中的缺失值用另一个 DataFrame 中具有相同标签的值有条件地填充。实现此操作的函数是 combine_first() ,我们在此举例说明:

通用 DataFrame 合并#

上面的 combine_first() 方法调用了更通用的 DataFrame.combine() 。此方法接受另一个 DataFrame 和一个组合器函数,对输入 DataFrame 进行对齐,然后将 Series 对(即名称相同的列)传递给组合器函数。

因此,例如,要重现上面的 combine_first() :

描述性统计#

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

  • Series:不需要 axis 参数

  • DataFrame:“index”(axis=0,默认)、“columns”(axis=1)

例如:

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

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

请注意,像 cumsum() 和 cumprod() 这样的方法会保留 NaN 值的位置。这与 expanding() 和 rolling() 有些不同,因为 NaN 的行为进一步由 min_periods 参数决定。

以下是常用函数的快速参考摘要表。每个函数还有一个可选的 level 参数,该参数仅在对象具有 hierarchical index 时适用。

函数

描述

count

非 NA 观测值的数量

sum

值的总和

mean

值的平均值

median

值的算术中位数

min

最小值

max

最大值

mode

众数

abs

绝对值

prod

值的乘积

std

贝塞尔校正的样本标准差

var

无偏方差

sem

均值的标准误差

skew

样本偏度(第三矩)

kurt

样本峰度(第四矩)

quantile

样本分位数(%处的值)

cumsum

累积和

cumprod

累积积

cummax

累积最大值

cummin

累积最小值

请注意,碰巧一些NumPy方法,如 mean、std 和 sum,在Series输入时默认会排除NA值:

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

汇总数据:describe#

有一个方便的 describe() 函数,它计算Series或DataFrame列的各种汇总统计信息(当然会排除NA值):

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

默认情况下,中位数始终包含在内。

对于非数值类型的Series对象,describe() 将提供一个关于唯一值数量和最常出现值的简单摘要:

请注意,在混合类型的DataFrame对象上,describe() 将把摘要限制为仅包含数值列,或者,如果不存在数值列,则仅包含分类列:

可以通过提供类型列表作为 include/exclude 参数来控制此行为。特殊值 all 也可以使用:

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

最小/最大值索引#

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

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

备注

idxmin 和 idxmax 在NumPy中被称为 argmin 和 argmax。

值计数(直方图)/ 众数#

value_counts() Series方法计算一维值数组的直方图。它也可以作为函数应用于常规数组:

value_counts() 方法可用于计算多列之间的组合次数。默认情况下使用所有列,但可以使用 subset 参数选择子集。

类似地,您可以获取Series或DataFrame中值最常出现的值(即众数):

离散化和分位数#

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

qcut() 计算样本分位数。例如,我们可以将一些正态分布的数据切分成大小相等的四分位数,如下所示:

我们也可以传递无限值来定义箱:

函数应用#

要将您自己或另一个库的函数应用于pandas对象,您应该了解以下三种方法。使用哪种方法取决于您的函数是期望操作整个 DataFrame 或 Series,逐行或逐列操作,还是逐元素操作。

  1. Tablewise Function Application pipe()

  2. Row or Column-wise Function Application apply()

  3. Aggregation API agg() 和 transform()

  4. Applying Elementwise Functions map()

表格级函数应用#

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

首先是一些设置:

extract_city_name 和 add_country_name 是接受和返回 DataFrames 的函数。

现在比较以下内容:

等同于:

pandas鼓励第二种风格,即方法链。pipe 可以轻松地将您自己的或另一个库的函数与pandas的方法一起用于方法链中。

在上面的示例中,函数 extract_city_name 和 add_country_name 都期望一个 DataFrame 作为第一个位置参数。如果希望应用的函数将其数据作为第二个参数(例如)传入怎么办?在这种情况下,请为 pipe 提供一个元组 (callable, data_keyword)。.pipe 将 DataFrame 路由到元组中指定的参数。

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

In [147]: import statsmodels.formula.api as sm

In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")

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

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

pipe 方法的灵感来源于 unix 管道以及最近的 dplyr 和 magrittr,它们为 R 引入了流行的 (%>%)``(读管道)运算符。这里的 ``pipe 实现非常简洁,并且在 Python 中感觉很自然。我们鼓励您查看 pipe() 的源代码。

按行或按列应用函数#

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

apply() 方法也会根据字符串方法名称进行分派。

传递给 apply() 的函数的返回类型会影响 DataFrame.apply 默认行为下的最终输出类型:

  • 如果应用函数返回一个 Series,则最终输出是一个 DataFrame。列与应用函数返回的 Series 的索引匹配。

  • 如果应用函数返回任何其他类型,则最终输出是一个 Series。

可以使用 result_type 参数覆盖此默认行为,该参数接受三个选项:“reduce”、“broadcast”和“expand”。这些选项将决定列表状返回值的扩展方式(或不扩展)到 DataFrame。

apply() 结合一些巧妙的用法可以回答关于数据集的许多问题。例如,假设我们想提取每列最大值出现的日期:

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

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

最后,apply() 接受一个 raw 参数,该参数默认为 False,它会在应用函数之前将每一行或每一列转换为一个 Series。当设置为 True 时,传递的函数将改接收一个 ndarray 对象,如果您不需要索引功能,这会带来正面的性能影响。

聚合 API#

聚合 API 允许以一种简洁的方式表达可能多个聚合操作。此 API 在 pandas 对象之间是相似的,请参阅 groupby API 、window API 和 resample API 。聚合的入口点是 DataFrame.aggregate() ,或其别名 DataFrame.agg() 。

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

使用单个函数等同于 apply() 。您也可以将命名方法作为字符串传递。这些将返回一个包含聚合输出的 Series:

对 Series 进行单次聚合,将返回一个标量值:

使用多个函数进行聚合#

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

多个函数产生多个行:

在 Series 上,多个函数将返回一个 Series,该 Series 由函数名称索引:

传递一个 lambda 函数将生成一个名为 <lambda> 的行:

传递一个命名函数将使用该名称作为行名:

使用 dict 进行聚合#

将列名映射到标量或标量列表的字典传递给 DataFrame.agg,允许您自定义哪些函数应用于哪些列。请注意,结果的顺序不确定,您可以使用 OrderedDict 来保证顺序。

传递一个类列表结构将生成一个 DataFrame 输出。您将获得所有聚合器的矩阵状输出。输出将包含所有唯一的函数。那些未为特定列指定的函数将为 NaN:

自定义 describe#

使用 .agg() 可以轻松创建自定义的 describe 函数,类似于内置的 describe function 。

Transform API#

transform() 方法返回一个与原始对象具有相同索引(相同大小)的对象。此 API 允许您一次提供*多个*操作,而不是一个接一个地操作。它的 API 与 .agg API 非常相似。

我们创建一个与前面章节中使用的类似的 DataFrame。

转换整个 DataFrame。 .transform() 允许将函数作为输入:NumPy 函数、字符串函数名或用户定义函数。

这里 transform() 接收一个单个函数;这等同于 ufunc 应用。

将单个函数传递给带有 Series 的 .transform() 将返回单个 Series。

使用多个函数进行转换#

传递多个函数将返回一个列 MultiIndexed DataFrame。第一层将是原始 DataFrame 的列名;第二层将是转换函数的名称。

将多个函数传递给 Series 将返回一个 DataFrame。结果列名将是转换函数。

使用字典进行转换#

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

传递一个列表字典将生成一个具有这些选择性转换的 MultiIndexed DataFrame。

应用逐元素函数#

由于并非所有函数都可以矢量化(接受 NumPy 数组并返回另一个数组或值),因此 DataFrame 上的 map() 方法和 Series 上的 map() 方法接受任何接受单个值并返回单个值的 Python 函数。例如:

Series.map() 具有一个附加功能;它可以用于轻松“链接”或“映射”由第二个 Series 定义的值。这与 merging/joining functionality 密切相关:

重新索引和修改标签#

reindex() 是 pandas 中最基本的数据对齐方法。它用于实现几乎所有依赖于标签对齐功能的方法。*重新索引*意味着使数据与特定轴上的一组给定标签匹配。这会完成几件事:

  • 重新排序现有数据以匹配一组新标签

  • 在标签不存在数据的位置插入缺失值 (NA) 标记

  • 如果指定,则使用逻辑(与处理时间序列数据密切相关)**填充**缺失标签的数据

这里是一个简单示例:

在这里,标签 f 不在 Series 中,因此在结果中显示为 NaN。

使用 DataFrame,您可以同时重新索引索引和列:

请注意,包含实际轴标签的 Index 对象可以被对象**共享**。因此,如果我们有一个 Series 和一个 DataFrame,则可以执行以下操作:

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

DataFrame.reindex() 还支持“axis-style”调用约定,您可以在其中指定一个 labels 参数及其适用的 axis。

参见

MultiIndex / Advanced Indexing 是执行重新索引的更简洁的方法。

备注

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

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

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

使用 align 将对象与彼此对齐#

align() 方法是同时对齐两个对象的最快方法。它支持 join 参数(与 joining and merging 相关):

  • join='outer':取索引的并集(默认)

  • join='left':使用调用对象的索引

  • join='right':使用传入对象的索引

  • join='inner':交集索引

它返回一个包含两个重索引 Series 的元组:

对于 DataFrame,join 方法默认将应用于索引和列:

您也可以传递一个 axis 选项来仅在指定的轴上对齐:

如果您将 Series 传递给 DataFrame.align() ,您可以使用 axis 参数选择在 DataFrame 的索引或列上对齐两个对象:

重索引时填充#

reindex() 接受一个可选参数 method,这是一个填充方法,从下表中选择:

方法

操作

pad / ffill

向前填充值

bfill / backfill

向后填充值

nearest

从最近的索引值填充

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

这些方法要求索引是**有序**的递增或递减。

请注意,使用 ffill (除了 method='nearest')或 interpolate 也可以达到相同的结果:

reindex() 如果索引不是单调递增或递减,则会引发 ValueError。fillna() 和 interpolate() 不会对索引的顺序执行任何检查。

重索引时的填充限制#

limit 和 tolerance 参数提供了对重索引时填充的额外控制。Limit 指定连续匹配的最大计数:

相比之下,tolerance 指定了索引和 indexer 值之间的最大距离:

请注意,当在 DatetimeIndex、TimedeltaIndex 或 PeriodIndex 上使用时,tolerance 将尽可能被强制转换为 Timedelta。这允许您使用适当的字符串指定容差。

从轴中删除标签#

与 reindex 密切相关的一个方法是 drop() 函数。它从一个轴中删除一组标签:

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

重命名/映射标签#

rename() 方法允许您根据某些映射(如 dict 或 Series)或任意函数来重新标记一个轴。

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

如果映射不包含列/索引标签,则不会重命名。请注意,映射中多余的标签不会出错。

DataFrame.rename() 也支持“axis-style”调用约定,您可以在其中指定单个 mapper 和要应用该映射的 axis。

最后,rename() 还接受标量或类列表参数来修改 Series.name 属性。

DataFrame.rename_axis() 和 Series.rename_axis() 方法允许更改 MultiIndex 的特定名称(而不是标签)。

迭代#

Pandas 对象的基本迭代行为取决于类型。迭代 Series 时,它被视为类似数组的,基本迭代会产生值。DataFrame 则遵循类似 dict 的约定,迭代对象的“键”。

简而言之,基本迭代(for i in object)产生:

  • Series: 值

  • DataFrame: 列标签

因此,例如,迭代 DataFrame 会得到列名:

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

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

  • iterrows() 迭代 DataFrame 的行,作为 (index, Series) 对。这会将行转换为 Series 对象,可能会更改 dtypes,并带来一些性能影响。

  • itertuples() 迭代 DataFrame 的行,作为值的命名元组。这比 iterrows() 快得多,并且在大多数情况下,优先使用它来迭代 DataFrame 的值。

警告

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

  • 寻找一个*向量化*的解决方案:许多操作都可以使用内置方法或NumPy函数(布尔索引)等来完成。

  • 当有一个函数无法一次性处理整个DataFrame/Series时,最好使用 apply() 而不是迭代值。请参阅 function application 的文档。

  • 如果你需要在迭代处理值的同时保持高性能,可以考虑使用cython或numba编写内部循环。请参阅 enhancing performance 部分中的一些示例。

警告

切勿修改 正在迭代的内容。这不能保证在所有情况下都能正常工作。根据数据类型,迭代器可能返回副本而不是视图,写入副本将无效!

例如,在以下情况下,设置值无效:

items#

与dict类似, items() 会迭代键值对:

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

  • DataFrame: (列, Series) 对

例如:

iterrows#

iterrows() 允许你迭代DataFrame的行,每行作为一个Series返回。它返回一个迭代器,生成每个索引值以及包含每行数据的Series:

备注

由于 iterrows() 为每行返回一个Series,它**不会**在行之间保留dtype(DataFrame在列之间保留dtype)。例如,

row 中的所有值都将作为Series返回,并向上转换为float类型,即使原始整数值在 x 列中:

为了在迭代行时保留dtype,最好使用 itertuples() ,它返回命名元组(namedtuples)形式的值,并且通常比 iterrows() 快得多。

例如,一个不常用的转置DataFrame的方法是:

itertuples#

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

例如:

此方法不会将行转换为Series对象;它只是将值以命名元组的形式返回。因此,itertuples() 保留了值的数据类型,并且通常比 iterrows() 快。

备注

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

.dt accessor#

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

这使得以下漂亮的表达式成为可能:

你可以轻松地进行带时区的转换:

你也可以链接这些类型的操作:

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

.dt 访问器适用于周期(period)和时间差(timedelta)dtype。

备注

如果你使用非日期时间类型访问 Series.dt,它会引发 TypeError。

向量化字符串方法#

Series配备了一套字符串处理方法,可以方便地对数组中的每个元素进行操作。最重要的是,这些方法会自动排除缺失/NA值。它们通过Series的 str 属性访问,并且通常具有与等效的(标量)内置字符串方法相匹配的名称。例如:

还提供了强大的模式匹配方法,但请注意,模式匹配默认使用 regular expressions (在某些情况下始终使用它们)。

备注

在pandas 1.0之前,字符串方法仅在 object-dtype Series 上可用。pandas 1.0添加了专门用于字符串的 StringDtype 。有关更多信息,请参阅 文本数据类型 。

请参阅 Vectorized String Methods 以获得完整描述。

排序#

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

按索引#

:meth:`Series.sort_index 和 :meth:`DataFrame.sort_index 方法用于按其索引级别对 pandas 对象进行排序。

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

有关按值进行键排序的信息,请参阅 value sorting 。

按值#

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

by 参数可以接受一个列名列表,例如:

这些方法通过 na_position 参数对 NA 值进行特殊处理:

排序还支持一个 key 参数,该参数接受一个可调用函数,应用于正在排序的值。

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

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

按索引和值#

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

按“second”(索引)和“A”(列)排序

备注

如果一个字符串同时匹配列名和索引级别名称,则会发出警告,并且列具有优先权。这将在未来版本中导致歧义错误。

searchsorted#

Series 具有 :meth:`~Series.searchsorted 方法,该方法的工作方式类似于 :meth:`numpy.ndarray.searchsorted 。

最小/最大值#

Series 具有 :meth:`~Series.nsmallest 和 :meth:`~Series.nlargest 方法,它们返回最小或最大的 \(n\) 个值。对于大型 Series,这比对整个 Series 进行排序然后调用结果的 head(n) 要快得多。

DataFrame 也有 nlargest 和 nsmallest 方法。

按 MultiIndex 列排序#

当列是 MultiIndex 时,您必须明确地进行排序,并为 to 指定所有级别.

复制#

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

  • 插入、删除或修改列。

  • 赋值给 index 或 columns 属性。

  • 对于同类数据,通过 values 属性或高级索引直接修改值。

需要明确的是,没有 pandas 方法会产生修改数据的副作用;几乎每个方法都会返回一个新对象,而原始对象保持不变。如果数据被修改,那是因为您显式地进行了修改。

dtypes#

在大多数情况下,pandas 对 Series 或 DataFrame 的单个列使用 NumPy 数组和 dtype。NumPy 支持 float、int、bool、timedelta64[ns] 和 ``datetime64[ns]``(请注意,NumPy 不支持时区感知的日期时间)。

pandas 和第三方库在少数地方*扩展*了 NumPy 的类型系统。本节介绍 pandas 内部进行的扩展。有关如何编写与 pandas 配合使用的自己的扩展,请参阅 扩展类型 。有关已实现扩展的第三方库列表,请参阅 the ecosystem page 。

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

数据种类

数据类型

标量

数组

字符串别名

tz-aware datetime

DatetimeTZDtype

Timestamp

arrays.DatetimeArray

'datetime64[ns, <tz>]'

Categorical

CategoricalDtype

(无)

Categorical

'category'

period (time spans)

PeriodDtype

Period

arrays.PeriodArray 'Period[<freq>]'

'period[<freq>]',

sparse

SparseDtype

(无)

arrays.SparseArray

'Sparse', 'Sparse[int]', 'Sparse[float]'

intervals

IntervalDtype

Interval

arrays.IntervalArray

'interval', 'Interval', 'Interval[<numpy_dtype>]', 'Interval[datetime64[ns, <tz>]]', 'Interval[timedelta64[<freq>]]'

nullable integer

Int64Dtype …

(无)

arrays.IntegerArray

'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64'

nullable float

Float64Dtype …

(无)

arrays.FloatingArray

'Float32', 'Float64'

Strings

StringDtype

str

arrays.StringArray

'string'

Boolean (with NA)

BooleanDtype

bool

arrays.BooleanArray

'boolean'

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

  1. object 数据类型,它可以存储任何 Python 对象,包括字符串。

  2. StringDtype ,这是专门用于字符串的数据类型。

通常,我们推荐使用 StringDtype 。更多信息请参阅 文本数据类型 。

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

DataFrame 的一个方便的 dtypes 属性将返回一个 Series,其中包含每一列的数据类型。

在 Series 对象上,使用 dtype 属性。

如果一个 pandas 对象在*单列*中包含多种数据类型的混合数据,则该列的数据类型将被选择为能够容纳所有数据类型的类型(object 是最通用的)。

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

数值数据类型会传播并可以在 DataFrame 中共存。如果传递了数据类型(无论是直接通过 dtype 关键字,还是通过传入的 ndarray,或是传入的 Series),它将在 DataFrame 操作中被保留。此外,不同的数值数据类型**不会**被合并。下面的示例将让你对此有所了解。

默认值#

默认情况下,整数类型为 int64,浮点数类型为 float64,*无论*平台是 32 位还是 64 位。以下操作都将导致 int64 数据类型。

请注意,Numpy 在创建数组时会选择*平台相关的*数据类型。在 32 位平台上,以下操作**将**导致 int32 数据类型。

类型提升 (Upcasting)#

当与其他类型组合时,类型可能会被*提升*,这意味着它们会从当前类型(例如 int 到 float)被提升。

DataFrame.to_numpy() 将返回数据类型的*最低公共分母*,即能够容纳结果中*所有*类型(假设结果是同质数据类型的 NumPy 数组)的数据类型。这可能会强制进行一些*类型提升*。

astype#

你可以使用 astype() 方法将数据类型从一种显式地转换为另一种。默认情况下,即使数据类型未改变,它们也会返回副本(通过传入 copy=False 可以改变此行为)。此外,如果 astype 操作无效,它们将引发异常。

类型提升始终遵循**NumPy**的规则。如果操作中涉及两种不同的数据类型,则将使用更*通用的*一种作为操作的结果。

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

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

备注

当尝试使用 astype() 和 loc() 将一部分列转换为指定类型时,会发生类型提升。

loc() 会尝试将赋给它的值与当前 dtype 匹配,而 [] 会用右侧的值覆盖它们,从而改变 dtype。因此,以下代码会产生非预期的结果。

对象转换#

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

由于数据被转置,原始推断将所有列存储为 object,而 infer_objects 会进行修正。

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

要强制转换,我们可以传递一个 errors 参数,该参数指定 pandas 如何处理无法转换为所需 dtype 或对象的元素。默认情况下,errors='raise',表示在转换过程中会引发遇到的任何错误。但是,如果 errors='coerce',这些错误将被忽略,pandas 会将有问题元素转换为 pd.NaT (对于 datetime 和 timedelta) 或 np.nan (对于数值)。如果您正在读取大部分是所需 dtype (例如,数值、datetime) 但偶尔混有不符合要求的元素的数据,并希望将这些元素表示为缺失值,这可能很有用:

除了对象转换之外,to_numeric() 还提供了一个 downcast 参数,该参数可以选择将新转换的 (或已有的) 数值数据向下转换为更小的 dtype,从而可以节省内存:

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

注意事项#

对 integer 类型数据执行选择操作很容易将数据向上转换为 floating。在不引入 nan 的情况下,输入数据的 dtype 将被保留。另请参阅 Support for integer NA 。

而 float dtypes 则保持不变。

按 dtype 选择列#

select_dtypes() 方法根据列的 dtype 实现列的子集选择。

首先,让我们创建一个具有多种不同 dtype 的 DataFrame :

以及它们的 dtypes:

select_dtypes() 具有两个参数 include 和 exclude,允许您指定“给我那些*具有*这些 dtype 的列”(include)和/或“给我那些*不具有*这些 dtype 的列”(exclude)。

例如,选择 bool 列:

您也可以在 NumPy dtype hierarchy 中传递 dtype 名称:

select_dtypes() 也适用于泛型 dtype。

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

要选择字符串列,必须使用 object dtype:

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

所有 NumPy dtypes 都是 numpy.generic 的子类:

备注

pandas 还定义了 category 和 datetime64[ns, tz] 类型,它们未集成到常规 NumPy 层级结构中,并且不会出现在上述函数的结果中。