Python 数据分析(PYDA)第三版(二)(3)

简介: Python 数据分析(PYDA)第三版(二)

Python 数据分析(PYDA)第三版(二)(2)https://developer.aliyun.com/article/1482374


5.2 基本功能

本节将带领您了解与 Series 或 DataFrame 中包含的数据进行交互的基本机制。在接下来的章节中,我们将更深入地探讨使用 pandas 进行数据分析和操作的主题。本书不旨在作为 pandas 库的详尽文档,而是专注于让您熟悉常用功能,将不太常见的(即更神秘的)内容留给您通过阅读在线 pandas 文档来学习。

重新索引

pandas 对象上的一个重要方法是reindex,它意味着创建一个新对象,其值重新排列以与新索引对齐。考虑一个例子:

In [98]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=["d", "b", "a", "c"])
In [99]: obj
Out[99]: 
d    4.5
b    7.2
a   -5.3
c    3.6
dtype: float64

在这个 Series 上调用reindex会根据新索引重新排列数据,如果某些索引值之前不存在,则会引入缺失值:

In [100]: obj2 = obj.reindex(["a", "b", "c", "d", "e"])
In [101]: obj2
Out[101]: 
a   -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64

对于有序数据如时间序列,当重新索引时可能需要进行一些插值或值填充。method选项允许我们使用ffill这样的方法来实现,它可以向前填充值:

In [102]: obj3 = pd.Series(["blue", "purple", "yellow"], index=[0, 2, 4])
In [103]: obj3
Out[103]: 
0      blue
2    purple
4    yellow
dtype: object
In [104]: obj3.reindex(np.arange(6), method="ffill")
Out[104]: 
0      blue
1      blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object

对于 DataFrame,reindex可以改变(行)索引、列或两者。当只传递一个序列时,它会重新索引结果中的行:

In [105]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
 .....:                      index=["a", "c", "d"],
 .....:                      columns=["Ohio", "Texas", "California"])
In [106]: frame
Out[106]: 
 Ohio  Texas  California
a     0      1           2
c     3      4           5
d     6      7           8
In [107]: frame2 = frame.reindex(index=["a", "b", "c", "d"])
In [108]: frame2
Out[108]: 
 Ohio  Texas  California
a   0.0    1.0         2.0
b   NaN    NaN         NaN
c   3.0    4.0         5.0
d   6.0    7.0         8.0

可以使用columns关键字重新索引列:

In [109]: states = ["Texas", "Utah", "California"]
In [110]: frame.reindex(columns=states)
Out[110]: 
 Texas  Utah  California
a      1   NaN           2
c      4   NaN           5
d      7   NaN           8

因为"Ohio"不在states中,所以该列的数据被从结果中删除。

重新索引特定轴的另一种方法是将新的轴标签作为位置参数传递,然后使用axis关键字指定要重新索引的轴:

In [111]: frame.reindex(states, axis="columns")
Out[111]: 
 Texas  Utah  California
a      1   NaN           2
c      4   NaN           5
d      7   NaN           8

查看 Table 5.3 以了解有关reindex参数的更多信息。

表 5.3:reindex函数参数

参数 描述
labels 用作索引的新序列。可以是 Index 实例或任何其他类似序列的 Python 数据结构。Index 将被完全使用,不会进行任何复制。
index 使用传递的序列作为新的索引标签。
columns 使用传递的序列作为新的列标签。
axis 要重新索引的轴,无论是"index"(行)还是"columns"。默认为"index"。您也可以使用reindex(index=new_labels)reindex(columns=new_labels)
method 插值(填充)方法;"ffill"向前填充,而"bfill"向后填充。
fill_value 重新索引时引入缺失数据时要使用的替代值。当您希望缺失标签在结果中具有空值时,请使用fill_value="missing"(默认行为)。
limit 在向前填充或向后填充时,要填充的最大大小间隙(元素数量)。
tolerance 在向前填充或向后填充时,要填充的最大大小间隙(绝对数值距离)。
level 在 MultiIndex 级别上匹配简单索引;否则选择子集。
copy 如果为True,即使新索引等效于旧索引,也始终复制基础数据;如果为False,当索引等效时不复制数据。

正如我们稍后将在使用 loc 和 iloc 在 DataFrame 上进行选择中探讨的,您也可以通过使用loc运算符重新索引,许多用户更喜欢始终以这种方式进行操作。这仅在所有新索引标签已存在于 DataFrame 中时才有效(而reindex将为新标签插入缺失数据):

In [112]: frame.loc[["a", "d", "c"], ["California", "Texas"]]
Out[112]: 
 California  Texas
a           2      1
d           8      7
c           5      4

从轴中删除条目

如果您已经有一个不包含这些条目的索引数组或列表,那么从轴中删除一个或多个条目就很简单,因为您可以使用reindex方法或基于.loc的索引。由于这可能需要一些数据处理和集合逻辑,drop方法将返回一个新对象,其中包含从轴中删除的指定值或值:

In [113]: obj = pd.Series(np.arange(5.), index=["a", "b", "c", "d", "e"])
In [114]: obj
Out[114]: 
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64
In [115]: new_obj = obj.drop("c")
In [116]: new_obj
Out[116]: 
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64
In [117]: obj.drop(["d", "c"])
Out[117]: 
a    0.0
b    1.0
e    4.0
dtype: float64

使用 DataFrame,可以从任一轴删除索引值。为了说明这一点,我们首先创建一个示例 DataFrame:

In [118]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
 .....:                     index=["Ohio", "Colorado", "Utah", "New York"],
 .....:                     columns=["one", "two", "three", "four"])
In [119]: data
Out[119]: 
 one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

使用一系列标签调用drop将从行标签(轴 0)中删除值:

In [120]: data.drop(index=["Colorado", "Ohio"])
Out[120]: 
 one  two  three  four
Utah        8    9     10    11
New York   12   13     14    15

要从列中删除标签,而不是使用columns关键字:

In [121]: data.drop(columns=["two"])
Out[121]: 
 one  three  four
Ohio        0      2     3
Colorado    4      6     7
Utah        8     10    11
New York   12     14    15

您还可以通过传递axis=1(类似于 NumPy)或axis="columns"来从列中删除值:

In [122]: data.drop("two", axis=1)
Out[122]: 
 one  three  four
Ohio        0      2     3
Colorado    4      6     7
Utah        8     10    11
New York   12     14    15
In [123]: data.drop(["two", "four"], axis="columns")
Out[123]: 
 one  three
Ohio        0      2
Colorado    4      6
Utah        8     10
New York   12     14

索引、选择和过滤

Series 索引(obj[...])的工作方式类似于 NumPy 数组索引,只是您可以使用 Series 的索引值而不仅仅是整数。以下是一些示例:

In [124]: obj = pd.Series(np.arange(4.), index=["a", "b", "c", "d"])
In [125]: obj
Out[125]: 
a    0.0
b    1.0
c    2.0
d    3.0
dtype: float64
In [126]: obj["b"]
Out[126]: 1.0
In [127]: obj[1]
Out[127]: 1.0
In [128]: obj[2:4]
Out[128]: 
c    2.0
d    3.0
dtype: float64
In [129]: obj[["b", "a", "d"]]
Out[129]: 
b    1.0
a    0.0
d    3.0
dtype: float64
In [130]: obj[[1, 3]]
Out[130]: 
b    1.0
d    3.0
dtype: float64
In [131]: obj[obj < 2]
Out[131]: 
a    0.0
b    1.0
dtype: float64

虽然您可以通过标签这种方式选择数据,但选择索引值的首选方式是使用特殊的loc运算符:

In [132]: obj.loc[["b", "a", "d"]]
Out[132]: 
b    1.0
a    0.0
d    3.0
dtype: float64

更喜欢loc的原因是因为在使用[]进行索引时,对整数的处理方式不同。如果索引包含整数,常规的[]索引将将整数视为标签,因此行为取决于索引的数据类型。例如:

In [133]: obj1 = pd.Series([1, 2, 3], index=[2, 0, 1])
In [134]: obj2 = pd.Series([1, 2, 3], index=["a", "b", "c"])
In [135]: obj1
Out[135]: 
2    1
0    2
1    3
dtype: int64
In [136]: obj2
Out[136]: 
a    1
b    2
c    3
dtype: int64
In [137]: obj1[[0, 1, 2]]
Out[137]: 
0    2
1    3
2    1
dtype: int64
In [138]: obj2[[0, 1, 2]]
Out[138]: 
a    1
b    2
c    3
dtype: int64

在使用loc时,当索引不包含整数时,表达式obj.loc[[0, 1, 2]]将失败:

In [134]: obj2.loc[[0, 1]]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/tmp/ipykernel_804589/4185657903.py in <module>
----> 1 obj2.loc[[0, 1]]
^ LONG EXCEPTION ABBREVIATED ^
KeyError: "None of [Int64Index([0, 1], dtype="int64")] are in the [index]"

由于loc运算符仅使用标签进行索引,因此还有一个iloc运算符,它仅使用整数进行索引,以便在索引包含整数或不包含整数时始终保持一致:

In [139]: obj1.iloc[[0, 1, 2]]
Out[139]: 
2    1
0    2
1    3
dtype: int64
In [140]: obj2.iloc[[0, 1, 2]]
Out[140]: 
a    1
b    2
c    3
dtype: int64

注意

您也可以使用标签进行切片,但与正常的 Python 切片不同,终点是包含的:

In [141]: obj2.loc["b":"c"]
Out[141]: 
b    2
c    3
dtype: int64

使用这些方法分配值会修改 Series 的相应部分:

In [142]: obj2.loc["b":"c"] = 5
In [143]: obj2
Out[143]: 
a    1
b    5
c    5
dtype: int64

注意

尝试调用lociloc等函数而不是使用方括号“索引”可能是新手的常见错误。方括号表示用于启用切片操作并允许在 DataFrame 对象上的多个轴上进行索引。

在 DataFrame 中进行索引会检索一个或多个列,可以使用单个值或序列:

In [144]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
 .....:                     index=["Ohio", "Colorado", "Utah", "New York"],
 .....:                     columns=["one", "two", "three", "four"])
In [145]: data
Out[145]: 
 one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15
In [146]: data["two"]
Out[146]: 
Ohio         1
Colorado     5
Utah         9
New York    13
Name: two, dtype: int64
In [147]: data[["three", "one"]]
Out[147]: 
 three  one
Ohio          2    0
Colorado      6    4
Utah         10    8
New York     14   12

这种索引有一些特殊情况。第一个是使用布尔数组进行切片或选择数据:

In [148]: data[:2]
Out[148]: 
 one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
In [149]: data[data["three"] > 5]
Out[149]: 
 one  two  three  four
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

行选择语法data[:2]是作为一种便利提供的。将单个元素或列表传递给[]运算符将选择列。

另一个用例是使用布尔 DataFrame 进行索引,比如通过标量比较生成的 DataFrame。考虑一个通过与标量值比较生成的全布尔值的 DataFrame:

In [150]: data < 5
Out[150]: 
 one    two  three   four
Ohio       True   True   True   True
Colorado   True  False  False  False
Utah      False  False  False  False
New York  False  False  False  False

我们可以使用这个 DataFrame 将值为True的位置赋值为 0,就像这样:

In [151]: data[data < 5] = 0
In [152]: data
Out[152]: 
 one  two  three  four
Ohio        0    0      0     0
Colorado    0    5      6     7
Utah        8    9     10    11
New York   12   13     14    15
使用 loc 和 iloc 在 DataFrame 上进行选择

与 Series 一样,DataFrame 具有专门的属性lociloc,用于基于标签和基于整数的索引。由于 DataFrame 是二维的,您可以使用类似 NumPy 的符号使用轴标签(loc)或整数(iloc)选择行和列的子集。

作为第一个示例,让我们通过标签选择单行:

In [153]: data
Out[153]: 
 one  two  three  four
Ohio        0    0      0     0
Colorado    0    5      6     7
Utah        8    9     10    11
New York   12   13     14    15
In [154]: data.loc["Colorado"]
Out[154]: 
one      0
two      5
three    6
four     7
Name: Colorado, dtype: int64

选择单行的结果是一个带有包含 DataFrame 列标签的索引的 Series。要选择多个行,创建一个新的 DataFrame,传递一个标签序列:

In [155]: data.loc[["Colorado", "New York"]]
Out[155]: 
 one  two  three  four
Colorado    0    5      6     7
New York   12   13     14    15

您可以通过用逗号分隔选择在loc中同时选择行和列:

In [156]: data.loc["Colorado", ["two", "three"]]
Out[156]: 
two      5
three    6
Name: Colorado, dtype: int64

然后我们将使用iloc执行一些类似的整数选择:

In [157]: data.iloc[2]
Out[157]: 
one       8
two       9
three    10
four     11
Name: Utah, dtype: int64
In [158]: data.iloc[[2, 1]]
Out[158]: 
 one  two  three  four
Utah        8    9     10    11
Colorado    0    5      6     7
In [159]: data.iloc[2, [3, 0, 1]]
Out[159]: 
four    11
one      8
two      9
Name: Utah, dtype: int64
In [160]: data.iloc[[1, 2], [3, 0, 1]]
Out[160]: 
 four  one  two
Colorado     7    0    5
Utah        11    8    9

这两个索引函数都可以处理切片,除了单个标签或标签列表:

In [161]: data.loc[:"Utah", "two"]
Out[161]: 
Ohio        0
Colorado    5
Utah        9
Name: two, dtype: int64
In [162]: data.iloc[:, :3][data.three > 5]
Out[162]: 
 one  two  three
Colorado    0    5      6
Utah        8    9     10
New York   12   13     14

布尔数组可以与loc一起使用,但不能与iloc一起使用:

In [163]: data.loc[data.three >= 2]
Out[163]: 
 one  two  three  four
Colorado    0    5      6     7
Utah        8    9     10    11
New York   12   13     14    15

有许多方法可以选择和重新排列 pandas 对象中包含的数据。对于 DataFrame,表 5.4 提供了许多这些方法的简要总结。正如您将在后面看到的,还有许多其他选项可用于处理分层索引。

表 5.4:DataFrame 的索引选项

类型 注释
df[column] 从 DataFrame 中选择单个列或列序列;特殊情况便利:布尔数组(过滤行)、切片(切片行)或布尔 DataFrame(根据某些条件设置值)
df.loc[rows] 通过标签从 DataFrame 中选择单行或行子集
df.loc[:, cols] 通过标签选择单个列或列子集
df.loc[rows, cols] 通过标签选择行和列
df.iloc[rows] 通过整数位置从 DataFrame 中选择单行或行子集
df.iloc[:, cols] 通过整数位置选择单个列或列子集
df.iloc[rows, cols] 通过整数位置选择行和列
df.at[row, col] 通过行和列标签选择单个标量值
df.iat[row, col] 通过行和列位置(整数)选择单个标量值
reindex方法 通过标签选择行或列
整数索引的陷阱

使用整数索引的 pandas 对象可能会成为新用户的绊脚石,因为它们与内置的 Python 数据结构(如列表和元组)的工作方式不同。例如,您可能不会期望以下代码生成错误:

In [164]: ser = pd.Series(np.arange(3.))
In [165]: ser
Out[165]: 
0    0.0
1    1.0
2    2.0
dtype: float64
In [166]: ser[-1]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
nge.py in get_loc(self, key)
 344             try:
--> 345                 return self._range.index(new_key)
 346             except ValueError as err:
ValueError: -1 is not in range
The above exception was the direct cause of the following exception:
KeyError                                  Traceback (most recent call last)
<ipython-input-166-44969a759c20> in <module>
----> 1 ser[-1]
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py 
in __getitem__(self, key)
 1010 
 1011         elif key_is_scalar:
-> 1012             return self._get_value(key)
 1013 
 1014         if is_hashable(key):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py 
in _get_value(self, label, takeable)
 1119 
 1120         # Similar to Index.get_value, but we do not fall back to position
al
-> 1121         loc = self.index.get_loc(label)
 1122 
 1123         if is_integer(loc):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
nge.py in get_loc(self, key)
 345                 return self._range.index(new_key)
 346             except ValueError as err:
--> 347                 raise KeyError(key) from err
 348         self._check_indexing_error(key)
 349         raise KeyError(key)
KeyError: -1

在这种情况下,pandas 可能会“回退”到整数索引,但是在不引入对用户代码中微妙错误的情况下,通常很难做到这一点。在这里,我们有一个包含012的索引,但 pandas 不想猜测用户想要什么(基于标签的索引还是基于位置的):

In [167]: ser
Out[167]: 
0    0.0
1    1.0
2    2.0
dtype: float64

另一方面,对于非整数索引,没有这种歧义:

In [168]: ser2 = pd.Series(np.arange(3.), index=["a", "b", "c"])
In [169]: ser2[-1]
Out[169]: 2.0

如果您有包含整数的轴索引,数据选择将始终是基于标签的。正如我上面所说的,如果您使用loc(用于标签)或iloc(用于整数),您将得到确切想要的结果:

In [170]: ser.iloc[-1]
Out[170]: 2.0

另一方面,使用整数进行切片始终是基于整数的:

In [171]: ser[:2]
Out[171]: 
0    0.0
1    1.0
dtype: float64

由于这些陷阱,最好始终优先使用lociloc进行索引,以避免歧义。

链式索引的陷阱

在前一节中,我们看了如何使用lociloc在 DataFrame 上进行灵活的选择。这些索引属性也可以用于就地修改 DataFrame 对象,但这样做需要一些小心。

例如,在上面的 DataFrame 示例中,我们可以按标签或整数位置分配到列或行:

In [172]: data.loc[:, "one"] = 1
In [173]: data
Out[173]: 
 one  two  three  four
Ohio        1    0      0     0
Colorado    1    5      6     7
Utah        1    9     10    11
New York    1   13     14    15
In [174]: data.iloc[2] = 5
In [175]: data
Out[175]: 
 one  two  three  four
Ohio        1    0      0     0
Colorado    1    5      6     7
Utah        5    5      5     5
New York    1   13     14    15
In [176]: data.loc[data["four"] > 5] = 3
In [177]: data
Out[177]: 
 one  two  three  four
Ohio        1    0      0     0
Colorado    3    3      3     3
Utah        5    5      5     5
New York    3    3      3     3

对于新的 pandas 用户来说,一个常见的坑是在赋值时链接选择,就像这样:

In [177]: data.loc[data.three == 5]["three"] = 6
<ipython-input-11-0ed1cf2155d5>:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

根据数据内容的不同,这可能会打印一个特殊的SettingWithCopyWarning,它警告您正在尝试修改一个临时值(data.loc[data.three == 5]的非空结果),而不是原始 DataFramedata,这可能是您的本意。在这里,data没有被修改:

In [179]: data
Out[179]: 
 one  two  three  four
Ohio        1    0      0     0
Colorado    3    3      3     3
Utah        5    5      5     5
New York    3    3      3     3

在这些情况下,修复的方法是重写链接赋值,使用单个loc操作:

In [180]: data.loc[data.three == 5, "three"] = 6
In [181]: data
Out[181]: 
 one  two  three  four
Ohio        1    0      0     0
Colorado    3    3      3     3
Utah        5    5      6     5
New York    3    3      3     3

一个很好的经验法则是在进行赋值时避免链接索引。还有其他情况下,pandas 会生成SettingWithCopyWarning,这与链接索引有关。我建议您查阅在线 pandas 文档中的这个主题。

算术和数据对齐

pandas 可以使处理具有不同索引的对象变得更简单。例如,当您添加对象时,如果任何索引对不相同,结果中的相应索引将是索引对的并集。让我们看一个例子:

In [182]: s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=["a", "c", "d", "e"])
In [183]: s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
 .....:                index=["a", "c", "e", "f", "g"])
In [184]: s1
Out[184]: 
a    7.3
c   -2.5
d    3.4
e    1.5
dtype: float64
In [185]: s2
Out[185]: 
a   -2.1
c    3.6
e   -1.5
f    4.0
g    3.1
dtype: float64

将它们相加得到:

In [186]: s1 + s2
Out[186]: 
a    5.2
c    1.1
d    NaN
e    0.0
f    NaN
g    NaN
dtype: float64

内部数据对齐会在不重叠的标签位置引入缺失值。缺失值将在进一步的算术计算中传播。

对于 DataFrame,对齐是在行和列上执行的:

In [187]: df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list("bcd"),
 .....:                    index=["Ohio", "Texas", "Colorado"])
In [188]: df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list("bde"),
 .....:                    index=["Utah", "Ohio", "Texas", "Oregon"])
In [189]: df1
Out[189]: 
 b    c    d
Ohio      0.0  1.0  2.0
Texas     3.0  4.0  5.0
Colorado  6.0  7.0  8.0
In [190]: df2
Out[190]: 
 b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0

将它们相加返回一个 DataFrame,其索引和列是每个 DataFrame 中的索引的并集:

In [191]: df1 + df2
Out[191]: 
 b   c     d   e
Colorado  NaN NaN   NaN NaN
Ohio      3.0 NaN   6.0 NaN
Oregon    NaN NaN   NaN NaN
Texas     9.0 NaN  12.0 NaN
Utah      NaN NaN   NaN NaN

由于 DataFrame 对象中都没有找到"c""e"列,它们在结果中显示为缺失。对于标签不共同的行也是如此。

如果添加没有共同列或行标签的 DataFrame 对象,结果将包含所有空值:

In [192]: df1 = pd.DataFrame({"A": [1, 2]})
In [193]: df2 = pd.DataFrame({"B": [3, 4]})
In [194]: df1
Out[194]: 
 A
0  1
1  2
In [195]: df2
Out[195]: 
 B
0  3
1  4
In [196]: df1 + df2
Out[196]: 
 A   B
0 NaN NaN
1 NaN NaN
带有填充值的算术方法

在不同索引对象之间的算术操作中,当一个对象中找到一个轴标签而另一个对象中没有时,您可能希望填充一个特殊值,比如 0。以下是一个示例,我们通过将np.nan赋值给它来将特定值设置为 NA(null):

In [197]: df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
 .....:                    columns=list("abcd"))
In [198]: df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
 .....:                    columns=list("abcde"))
In [199]: df2.loc[1, "b"] = np.nan
In [200]: df1
Out[200]: 
 a    b     c     d
0  0.0  1.0   2.0   3.0
1  4.0  5.0   6.0   7.0
2  8.0  9.0  10.0  11.0
In [201]: df2
Out[201]: 
 a     b     c     d     e
0   0.0   1.0   2.0   3.0   4.0
1   5.0   NaN   7.0   8.0   9.0
2  10.0  11.0  12.0  13.0  14.0
3  15.0  16.0  17.0  18.0  19.0

将它们相加会导致不重叠位置的缺失值:

In [202]: df1 + df2
Out[202]: 
 a     b     c     d   e
0   0.0   2.0   4.0   6.0 NaN
1   9.0   NaN  13.0  15.0 NaN
2  18.0  20.0  22.0  24.0 NaN
3   NaN   NaN   NaN   NaN NaN

df1上使用add方法,我传递df2和一个参数给fill_value,它会用传递的值替换操作中的任何缺失值:

In [203]: df1.add(df2, fill_value=0)
Out[203]: 
 a     b     c     d     e
0   0.0   2.0   4.0   6.0   4.0
1   9.0   5.0  13.0  15.0   9.0
2  18.0  20.0  22.0  24.0  14.0
3  15.0  16.0  17.0  18.0  19.0

请参阅表 5.5 以获取有关算术的 Series 和 DataFrame 方法的列表。每个方法都有一个对应的方法,以字母r开头,参数顺序相反。因此,以下两个语句是等价的:

In [204]: 1 / df1
Out[204]: 
 a         b         c         d
0    inf  1.000000  0.500000  0.333333
1  0.250  0.200000  0.166667  0.142857
2  0.125  0.111111  0.100000  0.090909
In [205]: df1.rdiv(1)
Out[205]: 
 a         b         c         d
0    inf  1.000000  0.500000  0.333333
1  0.250  0.200000  0.166667  0.142857
2  0.125  0.111111  0.100000  0.090909

相关地,在重新索引 Series 或 DataFrame 时,您还可以指定不同的填充值:

In [206]: df1.reindex(columns=df2.columns, fill_value=0)
Out[206]: 
 a    b     c     d  e
0  0.0  1.0   2.0   3.0  0
1  4.0  5.0   6.0   7.0  0
2  8.0  9.0  10.0  11.0  0

表 5.5:灵活的算术方法

方法 描述
add, radd 加法方法(+)
sub, rsub 减法方法(-)
div, rdiv 除法方法(/)
floordiv, rfloordiv 地板除法方法(//)
mul, rmul 乘法方法(*)
pow, rpow 指数方法(**)
DataFrame 和 Series 之间的操作

与不同维度的 NumPy 数组一样,DataFrame 和 Series 之间的算术也是定义的。首先,作为一个激励性的例子,考虑一个二维数组和其一行之间的差异:

In [207]: arr = np.arange(12.).reshape((3, 4))
In [208]: arr
Out[208]: 
array([[ 0.,  1.,  2.,  3.],
 [ 4.,  5.,  6.,  7.],
 [ 8.,  9., 10., 11.]])
In [209]: arr[0]
Out[209]: array([0., 1., 2., 3.])
In [210]: arr - arr[0]
Out[210]: 
array([[0., 0., 0., 0.],
 [4., 4., 4., 4.],
 [8., 8., 8., 8.]])

当我们从arr中减去arr[0]时,减法将针对每一行执行一次。这被称为广播,并且在附录 A:高级 NumPy 中更详细地解释了它与一般 NumPy 数组的关系。DataFrame 和 Series 之间的操作类似:

In [211]: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
 .....:                      columns=list("bde"),
 .....:                      index=["Utah", "Ohio", "Texas", "Oregon"])
In [212]: series = frame.iloc[0]
In [213]: frame
Out[213]: 
 b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0
In [214]: series
Out[214]: 
b    0.0
d    1.0
e    2.0
Name: Utah, dtype: float64

默认情况下,DataFrame 和 Series 之间的算术会将 Series 的索引与 DataFrame 的列匹配,向下广播行:

In [215]: frame - series
Out[215]: 
 b    d    e
Utah    0.0  0.0  0.0
Ohio    3.0  3.0  3.0
Texas   6.0  6.0  6.0
Oregon  9.0  9.0  9.0

如果索引值既不在 DataFrame 的列中,也不在 Series 的索引中找到,那么对象将被重新索引以形成并集:

In [216]: series2 = pd.Series(np.arange(3), index=["b", "e", "f"])
In [217]: series2
Out[217]: 
b    0
e    1
f    2
dtype: int64
In [218]: frame + series2
Out[218]: 
 b   d     e   f
Utah    0.0 NaN   3.0 NaN
Ohio    3.0 NaN   6.0 NaN
Texas   6.0 NaN   9.0 NaN
Oregon  9.0 NaN  12.0 NaN

如果您希望在列上进行广播,匹配行,您必须使用其中一个算术方法并指定匹配索引。例如:

In [219]: series3 = frame["d"]
In [220]: frame
Out[220]: 
 b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0
In [221]: series3
Out[221]: 
Utah       1.0
Ohio       4.0
Texas      7.0
Oregon    10.0
Name: d, dtype: float64
In [222]: frame.sub(series3, axis="index")
Out[222]: 
 b    d    e
Utah   -1.0  0.0  1.0
Ohio   -1.0  0.0  1.0
Texas  -1.0  0.0  1.0
Oregon -1.0  0.0  1.0

您传递的轴是要匹配的轴。在这种情况下,我们的意思是匹配 DataFrame 的行索引(axis="index")并在列之间广播。


Python 数据分析(PYDA)第三版(二)(4)https://developer.aliyun.com/article/1482378

相关文章
|
27天前
|
机器学习/深度学习 数据可视化 数据挖掘
使用Python进行数据分析的入门指南
本文将引导读者了解如何使用Python进行数据分析,从安装必要的库到执行基础的数据操作和可视化。通过本文的学习,你将能够开始自己的数据分析之旅,并掌握如何利用Python来揭示数据背后的故事。
|
1月前
|
机器学习/深度学习 数据可视化 数据挖掘
使用Python进行数据分析的入门指南
【10月更文挑战第42天】本文是一篇技术性文章,旨在为初学者提供一份关于如何使用Python进行数据分析的入门指南。我们将从安装必要的工具开始,然后逐步介绍如何导入数据、处理数据、进行数据可视化以及建立预测模型。本文的目标是帮助读者理解数据分析的基本步骤和方法,并通过实际的代码示例来加深理解。
55 3
|
1月前
|
机器学习/深度学习 算法 数据挖掘
数据分析的 10 个最佳 Python 库
数据分析的 10 个最佳 Python 库
88 4
数据分析的 10 个最佳 Python 库
|
1月前
|
存储 数据可视化 数据挖掘
使用Python进行数据分析和可视化
本文将引导你理解如何使用Python进行数据分析和可视化。我们将从基础的数据结构开始,逐步深入到数据处理和分析的方法,最后通过实际的代码示例来展示如何创建直观的数据可视化。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。让我们一起探索数据的世界,发现隐藏在数字背后的故事!
|
1月前
|
存储 数据可视化 数据挖掘
Python数据分析项目:抖音短视频达人粉丝增长趋势
Python数据分析项目:抖音短视频达人粉丝增长趋势
|
1月前
|
数据采集 存储 数据可视化
Python数据分析:揭秘"黑神话:悟空"Steam用户评论趋势
Python数据分析:揭秘"黑神话:悟空"Steam用户评论趋势
|
1月前
|
机器学习/深度学习 数据可视化 数据挖掘
使用Python进行数据分析和可视化
【10月更文挑战第42天】本文将介绍如何使用Python进行数据分析和可视化。我们将从数据导入、清洗、探索性分析、建模预测,以及结果的可视化展示等方面展开讲解。通过这篇文章,你将了解到Python在数据处理和分析中的强大功能,以及如何利用这些工具来提升你的工作效率。
|
1月前
|
数据采集 数据可视化 数据挖掘
深入浅出:使用Python进行数据分析的基础教程
【10月更文挑战第41天】本文旨在为初学者提供一个关于如何使用Python语言进行数据分析的入门指南。我们将通过实际案例,了解数据处理的基本步骤,包括数据的导入、清洗、处理、分析和可视化。文章将用浅显易懂的语言,带领读者一步步掌握数据分析师的基本功,并在文末附上完整的代码示例供参考和实践。
|
1月前
|
数据采集 数据可视化 数据挖掘
掌握Python数据分析,解锁数据驱动的决策能力
掌握Python数据分析,解锁数据驱动的决策能力
|
1月前
|
数据采集 数据可视化 数据挖掘
Python数据分析:Pandas库实战指南
Python数据分析:Pandas库实战指南