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
注意
尝试调用loc
或iloc
等函数而不是使用方括号“索引”可能是新手的常见错误。方括号表示用于启用切片操作并允许在 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 具有专门的属性loc
和iloc
,用于基于标签和基于整数的索引。由于 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 可能会“回退”到整数索引,但是在不引入对用户代码中微妙错误的情况下,通常很难做到这一点。在这里,我们有一个包含0
、1
和2
的索引,但 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
由于这些陷阱,最好始终优先使用loc
和iloc
进行索引,以避免歧义。
链式索引的陷阱
在前一节中,我们看了如何使用loc
和iloc
在 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