Pandas-2-2-中文文档-九-
Pandas 2.2 中文文档(九)
处理文本数据
文本数据类型
在 pandas 中有两种存储文本数据的方式:
-
object
-dtype NumPy 数组。 -
StringDtype
扩展类型。
我们建议使用StringDtype
来存储文本数据。
在 pandas 1.0 之前,object
dtype 是唯一的选项。这在很多方面都是不幸的:
-
你可能会在
object
dtype 数组中意外存储字符串和非字符串的混合。最好有一个专用的 dtype。 -
object
dtype 会破坏 dtype 特定的操作,比如DataFrame.select_dtypes()
。没有明确的方法可以仅选择文本而排除非文本但仍为 object-dtype 的列。 -
在阅读代码时,
object
dtype 数组的内容比'string'
不够清晰。
目前,字符串和arrays.StringArray
的object
dtype 数组的性能大致相同。我们期待未来的增强将显著提高StringArray
的性能并降低内存开销。
警告
StringArray
目前被视为实验性质。实现和部分 API 可能会在没有警告的情况下发生变化。
为了向后兼容,我们仍然将object
dtype 作为我们推断字符串列表的默认类型。
In [1]: pd.Series(["a", "b", "c"])
Out[1]:
0 a
1 b
2 c
dtype: object
要明确请求string
dtype,请指定dtype
In [2]: pd.Series(["a", "b", "c"], dtype="string")
Out[2]:
0 a
1 b
2 c
dtype: string
In [3]: pd.Series(["a", "b", "c"], dtype=pd.StringDtype())
Out[3]:
0 a
1 b
2 c
dtype: string
或在创建Series
或DataFrame
之后进行astype
In [4]: s = pd.Series(["a", "b", "c"])
In [5]: s
Out[5]:
0 a
1 b
2 c
dtype: object
In [6]: s.astype("string")
Out[6]:
0 a
1 b
2 c
dtype: string
你也可以在非字符串数据上使用StringDtype
/"string"
作为 dtype,并将其转换为string
dtype:
In [7]: s = pd.Series(["a", 2, np.nan], dtype="string")
In [8]: s
Out[8]:
0 a
1 2
2 <NA>
dtype: string
In [9]: type(s[1])
Out[9]: str
或者从现有的 pandas 数据中转换:
In [10]: s1 = pd.Series([1, 2, np.nan], dtype="Int64")
In [11]: s1
Out[11]:
0 1
1 2
2 <NA>
dtype: Int64
In [12]: s2 = s1.astype("string")
In [13]: s2
Out[13]:
0 1
1 2
2 <NA>
dtype: string
In [14]: type(s2[0])
Out[14]: str
行为差异
这些是StringDtype
对象与object
dtype 不同的地方
-
对于
StringDtype
,返回数字输出的 string 访问器方法将始终返回可空整数 dtype,而不是根据 NA 值的存在返回 int 或 float dtype。返回布尔值输出的方法将返回可空布尔 dtype。In [15]: s = pd.Series(["a", None, "b"], dtype="string") In [16]: s Out[16]: 0 a 1 <NA> 2 b dtype: string In [17]: s.str.count("a") Out[17]: 0 1 1 <NA> 2 0 dtype: Int64 In [18]: s.dropna().str.count("a") Out[18]: 0 1 2 0 dtype: Int64
两个输出都是
Int64
dtype。与 object-dtype 进行比较In [19]: s2 = pd.Series(["a", None, "b"], dtype="object") In [20]: s2.str.count("a") Out[20]: 0 1.0 1 NaN 2 0.0 dtype: float64 In [21]: s2.dropna().str.count("a") Out[21]: 0 1 2 0 dtype: int64
当存在 NA 值时,输出 dtype 为 float64。对于返回布尔值的方法也是如此。
In [22]: s.str.isdigit() Out[22]: 0 False 1 <NA> 2 False dtype: boolean In [23]: s.str.match("a") Out[23]: 0 True 1 <NA> 2 False dtype: boolean
-
一些字符串方法,比如
Series.str.decode()
在StringArray
上不可用,因为StringArray
只保存字符串,而不是字节。 -
在比较操作中,
arrays.StringArray
和由StringArray
支持的Series
将返回一个具有BooleanDtype
的对象,而不是bool
dtype 对象。在StringArray
中的缺失值将在比较操作中传播,而不总是像numpy.nan
那样比较不相等。
本文档其余部分中的所有内容同样适用于string
和object
dtype。 ## 字符串方法
Series 和 Index 配备了一组字符串处理方法,使得可以轻松操作数组的每个元素。最重要的是,这些方法会自动排除缺失/NA 值。这些方法通过str
属性访问,通常与等效的(标量)内置字符串方法名称匹配:
In [24]: s = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
....: )
....:
In [25]: s.str.lower()
Out[25]:
0 a
1 b
2 c
3 aaba
4 baca
5 <NA>
6 caba
7 dog
8 cat
dtype: string
In [26]: s.str.upper()
Out[26]:
0 A
1 B
2 C
3 AABA
4 BACA
5 <NA>
6 CABA
7 DOG
8 CAT
dtype: string
In [27]: s.str.len()
Out[27]:
0 1
1 1
2 1
3 4
4 4
5 <NA>
6 4
7 3
8 3
dtype: Int64
In [28]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])
In [29]: idx.str.strip()
Out[29]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')
In [30]: idx.str.lstrip()
Out[30]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')
In [31]: idx.str.rstrip()
Out[31]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')
Index 上的字符串方法特别适用于清理或转换 DataFrame 列。例如,您可能有带有前导或尾随空格的列:
In [32]: df = pd.DataFrame(
....: np.random.randn(3, 2), columns=[" Column A ", " Column B "], index=range(3)
....: )
....:
In [33]: df
Out[33]:
Column A Column B
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
由于df.columns
是一个 Index 对象,我们可以使用.str
访问器
In [34]: df.columns.str.strip()
Out[34]: Index(['Column A', 'Column B'], dtype='object')
In [35]: df.columns.str.lower()
Out[35]: Index([' column a ', ' column b '], dtype='object')
然后可以使用这些字符串方法根据需要清理列。在这里,我们删除前导和尾随空格,将所有名称转换为小写,并用下划线替换任何剩余的空格:
In [36]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
In [37]: df
Out[37]:
column_a column_b
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
注意
如果您有一个Series
,其中许多元素重复(即Series
中的唯一元素数量远小于Series
的长度),将原始Series
转换为category
类型之后,使用.str.<method>
或.dt.<property>
可能更快。性能差异来自于category
类型的Series
上的字符串操作是在.categories
上执行的,而不是在Series
的每个元素上执行。
请注意,具有字符串.categories
的category
类型Series
与字符串类型的Series
相比存在一些限制(例如,如果s
是category
类型的Series
,则无法将字符串相加:s + " " + s
不起作用)。此外,对于这样的Series
,不可用于操作list
类型元素的.str
方法。
警告
Series 的类型是推断的,并且允许的类型(即字符串)。
一般来说,.str
访问器仅用于字符串。除了极少数例外情况外,不支持其他用途,并且可能在以后被禁用。 ## 拆分和替换字符串
像split
这样的方法返回一个列表的 Series:
In [38]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="string")
In [39]: s2.str.split("_")
Out[39]:
0 [a, b, c]
1 [c, d, e]
2 <NA>
3 [f, g, h]
dtype: object
可以使用get
或[]
符号访问拆分列表中的元素:
In [40]: s2.str.split("_").str.get(1)
Out[40]:
0 b
1 d
2 <NA>
3 g
dtype: object
In [41]: s2.str.split("_").str[1]
Out[41]:
0 b
1 d
2 <NA>
3 g
dtype: object
可以轻松扩展此操作以使用expand
返回 DataFrame。
In [42]: s2.str.split("_", expand=True)
Out[42]:
0 1 2
0 a b c
1 c d e
2 <NA> <NA> <NA>
3 f g h
当原始Series
具有StringDtype
时,输出列也将全部是StringDtype
。
也可以限制拆分的数量:
In [43]: s2.str.split("_", expand=True, n=1)
Out[43]:
0 1
0 a b_c
1 c d_e
2 <NA> <NA>
3 f g_h
rsplit
类似于split
,只是它是从字符串末尾到字符串开头的方向进行操作:
In [44]: s2.str.rsplit("_", expand=True, n=1)
Out[44]:
0 1
0 a_b c
1 c_d e
2 <NA> <NA>
3 f_g h
replace
方法可以选择使用正则表达式:
In [45]: s3 = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
....: dtype="string",
....: )
....:
In [46]: s3
Out[46]:
0 A
1 B
2 C
3 Aaba
4 Baca
5
6 <NA>
7 CABA
8 dog
9 cat
dtype: string
In [47]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
Out[47]:
0 A
1 B
2 C
3 XX-XX ba
4 XX-XX ca
5
6 <NA>
7 XX-XX BA
8 XX-XX
9 XX-XX t
dtype: string
在 2.0 版本中更改。
使用regex=True
的单个字符模式也将被视为正则表达式:
In [48]: s4 = pd.Series(["a.b", ".", "b", np.nan, ""], dtype="string")
In [49]: s4
Out[49]:
0 a.b
1 .
2 b
3 <NA>
4
dtype: string
In [50]: s4.str.replace(".", "a", regex=True)
Out[50]:
0 aaa
1 a
2 a
3 <NA>
4
dtype: string
如果您想要对字符串进行字面替换(相当于str.replace()
),您可以将可选的regex
参数设置为False
,而不是转义每个字符。在这种情况下,pat
和repl
都必须是字符串:
In [51]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="string")
# These lines are equivalent
In [52]: dollars.str.replace(r"-\$", "-", regex=True)
Out[52]:
0 12
1 -10
2 $10,000
dtype: string
In [53]: dollars.str.replace("-$", "-", regex=False)
Out[53]:
0 12
1 -10
2 $10,000
dtype: string
replace
方法还可以接受一个可调用对象作为替换。它会对每个pat
使用re.sub()
进行调用。可调用对象应该期望一个位置参数(一个正则表达式对象)并返回一个字符串。
# Reverse every lowercase alphabetic word
In [54]: pat = r"[a-z]+"
In [55]: def repl(m):
....: return m.group(0)[::-1]
....:
In [56]: pd.Series(["foo 123", "bar baz", np.nan], dtype="string").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[56]:
0 oof 123
1 rab zab
2 <NA>
dtype: string
# Using regex groups
In [57]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
In [58]: def repl(m):
....: return m.group("two").swapcase()
....:
In [59]: pd.Series(["Foo Bar Baz", np.nan], dtype="string").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[59]:
0 bAR
1 <NA>
dtype: string
replace
方法还接受来自re.compile()
的编译的正则表达式对象作为模式。所有标志应包含在编译的正则表达式对象中。
In [60]: import re
In [61]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)
In [62]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
Out[62]:
0 A
1 B
2 C
3 XX-XX ba
4 XX-XX ca
5
6 <NA>
7 XX-XX BA
8 XX-XX
9 XX-XX t
dtype: string
在调用带有编译的正则表达式对象的replace
时包含一个flags
参数将引发ValueError
。
In [63]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex
removeprefix
和removesuffix
与 Python 3.9 中添加的str.removeprefix
和str.removesuffix
具有相同的效果 <docs.python.org/3/library/stdtypes.html#str.removeprefix
>`__:
在 1.4.0 版本中新增。
In [64]: s = pd.Series(["str_foo", "str_bar", "no_prefix"])
In [65]: s.str.removeprefix("str_")
Out[65]:
0 foo
1 bar
2 no_prefix
dtype: object
In [66]: s = pd.Series(["foo_str", "bar_str", "no_suffix"])
In [67]: s.str.removesuffix("_str")
Out[67]:
0 foo
1 bar
2 no_suffix
dtype: object
``` ## 连接
有几种方法可以连接`Series`或`Index`,可以是与自身或其他对象连接,都基于`cat()`,或者`Index.str.cat`。
### 将单个 Series 连接成字符串
`Series`(或`Index`)的内容可以进行连接:
```py
In [68]: s = pd.Series(["a", "b", "c", "d"], dtype="string")
In [69]: s.str.cat(sep=",")
Out[69]: 'a,b,c,d'
如果未指定,用于分隔符的关键字sep
默认为空字符串,sep=''
:
In [70]: s.str.cat()
Out[70]: 'abcd'
默认情况下,会忽略缺失值。使用na_rep
,可以为缺失值指定一个表示:
In [71]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")
In [72]: t.str.cat(sep=",")
Out[72]: 'a,b,d'
In [73]: t.str.cat(sep=",", na_rep="-")
Out[73]: 'a,b,-,d'
将一个 Series 和类似列表的东西连接成一个 Series
cat()
的第一个参数可以是类似列表的对象,只要它与调用的Series
(或Index
)的长度匹配即可。
In [74]: s.str.cat(["A", "B", "C", "D"])
Out[74]:
0 aA
1 bB
2 cC
3 dD
dtype: string
任一侧存在缺失值都会导致结果中也存在缺失值,除非指定了na_rep
:
In [75]: s.str.cat(t)
Out[75]:
0 aa
1 bb
2 <NA>
3 dd
dtype: string
In [76]: s.str.cat(t, na_rep="-")
Out[76]:
0 aa
1 bb
2 c-
3 dd
dtype: string
将一个 Series 和类似数组的东西连接成一个 Series
参数others
也可以是二维的。在这种情况下,行数必须与调用的Series
(或Index
)的长度匹配。
In [77]: d = pd.concat([t, s], axis=1)
In [78]: s
Out[78]:
0 a
1 b
2 c
3 d
dtype: string
In [79]: d
Out[79]:
0 1
0 a a
1 b b
2 <NA> c
3 d d
In [80]: s.str.cat(d, na_rep="-")
Out[80]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将一个 Series 和一个带索引的对象连接成一个 Series,并进行对齐
对于与Series
或DataFrame
连接,可以通过设置join
关键字来在连接之前对齐索引。
In [81]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")
In [82]: s
Out[82]:
0 a
1 b
2 c
3 d
dtype: string
In [83]: u
Out[83]:
1 b
3 d
0 a
2 c
dtype: string
In [84]: s.str.cat(u)
Out[84]:
0 aa
1 bb
2 cc
3 dd
dtype: string
In [85]: s.str.cat(u, join="left")
Out[85]:
0 aa
1 bb
2 cc
3 dd
dtype: string
对于join
,可以使用常规选项之一('left', 'outer', 'inner', 'right'
)。特别是,对齐也意味着不同长度不再需要一致。
In [86]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")
In [87]: s
Out[87]:
0 a
1 b
2 c
3 d
dtype: string
In [88]: v
Out[88]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [89]: s.str.cat(v, join="left", na_rep="-")
Out[89]:
0 aa
1 bb
2 c-
3 dd
dtype: string
In [90]: s.str.cat(v, join="outer", na_rep="-")
Out[90]:
-1 -z
0 aa
1 bb
2 c-
3 dd
4 -e
dtype: string
当others
是一个DataFrame
时,可以使用相同的对齐:
In [91]: f = d.loc[[3, 2, 1, 0], :]
In [92]: s
Out[92]:
0 a
1 b
2 c
3 d
dtype: string
In [93]: f
Out[93]:
0 1
3 d d
2 <NA> c
1 b b
0 a a
In [94]: s.str.cat(f, join="left", na_rep="-")
Out[94]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将一个系列和许多对象连接成一个系列
几个类似数组的项目(特别是:Series
、Index
和np.ndarray
的一维变体)可以组合在一个类似列表的容器中(包括迭代器、dict
-视图等)。
In [95]: s
Out[95]:
0 a
1 b
2 c
3 d
dtype: string
In [96]: u
Out[96]:
1 b
3 d
0 a
2 c
dtype: string
In [97]: s.str.cat([u, u.to_numpy()], join="left")
Out[97]:
0 aab
1 bbd
2 cca
3 ddc
dtype: string
传递的类似列表中没有索引的所有元素(例如np.ndarray
)必须与调用的Series
(或Index
)的长度匹配,但Series
和Index
的长度可以是任意的(只要不使用join=None
禁用对齐):
In [98]: v
Out[98]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [99]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[99]:
-1 -z--
0 aaab
1 bbbd
2 c-ca
3 dddc
4 -e--
dtype: string
如果在一个包含不同索引的others
列表上使用join='right'
,这些索引的并集将被用作最终连接的基础:
In [100]: u.loc[[3]]
Out[100]:
3 d
dtype: string
In [101]: v.loc[[-1, 0]]
Out[101]:
-1 z
0 a
dtype: string
In [102]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[102]:
3 dd-
-1 --z
0 a-a
dtype: string
使用.str
进行索引
您可以使用[]
表示法直接按位置索引。如果索引超出字符串的末尾,结果将是一个NaN
。
In [103]: s = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [104]: s.str[0]
Out[104]:
0 A
1 B
2 C
3 A
4 B
5 <NA>
6 C
7 d
8 c
dtype: string
In [105]: s.str[1]
Out[105]:
0 <NA>
1 <NA>
2 <NA>
3 a
4 a
5 <NA>
6 A
7 o
8 a
dtype: string
提取子字符串
提取每个主题中的第一个匹配项(extract)
extract
方法接受至少一个捕获组的正则表达式。
提取具有多个组的正则表达式将返回一个每个组一列的 DataFrame。
In [106]: pd.Series(
.....: ["a1", "b2", "c3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])(\d)", expand=False)
.....:
Out[106]:
0 1
0 a 1
1 b 2
2 <NA> <NA>
不匹配的元素返回一个填充有NaN
的行。因此,一系列混乱的字符串可以被“转换”为一个具有相同索引的清理或更有用的字符串的系列或DataFrame
,而不需要使用get()
来访问元组或re.match
对象。结果的数据类型始终为对象,即使没有找到匹配项,结果仅包含NaN
。
命名组如
In [107]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(
.....: r"(?P<letter>[ab])(?P<digit>\d)", expand=False
.....: )
.....:
Out[107]:
letter digit
0 a 1
1 b 2
2 <NA> <NA>
和可选组如
In [108]: pd.Series(
.....: ["a1", "b2", "3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])?(\d)", expand=False)
.....:
Out[108]:
0 1
0 a 1
1 b 2
2 <NA> 3
也可以使用。请注意,正则表达式中的任何捕获组名称将用作列名;否则将使用捕获组编号。
使用一个组的正则表达式提取返回一个列的DataFrame
,如果expand=True
。
In [109]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=True)
Out[109]:
0
0 1
1 2
2 <NA>
如果expand=False
,则返回一个 Series。
In [110]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=False)
Out[110]:
0 1
1 2
2 <NA>
dtype: string
使用正则表达式调用带有一个捕获组的Index
,如果expand=True
,则返回一个列的DataFrame
。
In [111]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")
In [112]: s
Out[112]:
A11 a1
B22 b2
C33 c3
dtype: string
In [113]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[113]:
letter
0 A
1 B
2 C
如果expand=False
,则返回一个Index
。
In [114]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[114]: Index(['A', 'B', 'C'], dtype='object', name='letter')
使用具有多个捕获组的正则表达式调用Index
,如果expand=True
,则返回一个DataFrame
。
In [115]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[115]:
letter 1
0 A 11
1 B 22
2 C 33
如果expand=False
,则引发ValueError
。
In [116]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[116], line 1
----> 1 s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:137, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
132 msg = (
133 f"Cannot use .str.{func_name} with values of "
134 f"inferred dtype '{self._inferred_dtype}'."
135 )
136 raise TypeError(msg)
--> 137 return func(self, *args, **kwargs)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:2743, in StringMethods.extract(self, pat, flags, expand)
2740 raise ValueError("pattern contains no capture groups")
2742 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 2743 raise ValueError("only one regex group is supported with Index")
2745 obj = self._data
2746 result_dtype = _result_dtype(obj)
ValueError: only one regex group is supported with Index
下表总结了extract(expand=False)
的行为(第一列为输入主题,正则表达式中的组数为第一行)
1 组 | >1 组 | |
---|---|---|
Index | Index | ValueError |
Series | Series | DataFrame |
提取每个主题中的所有匹配项(extractall)
与仅返回第一个匹配项的extract
不同,
In [117]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="string")
In [118]: s
Out[118]:
A a1a2
B b1
C c1
dtype: string
In [119]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"
In [120]: s.str.extract(two_groups, expand=True)
Out[120]:
letter digit
A a 1
B b 1
C c 1
extractall
方法返回每个匹配项。extractall
的结果始终是一个带有MultiIndex
的DataFrame
。MultiIndex
的最后一级命名为match
,表示主题中的顺序。
In [121]: s.str.extractall(two_groups)
Out[121]:
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1
当系列中的每个主题字符串恰好有一个匹配时,
In [122]: s = pd.Series(["a3", "b3", "c2"], dtype="string")
In [123]: s
Out[123]:
0 a3
1 b3
2 c2
dtype: string
然后extractall(pat).xs(0, level='match')
与extract(pat)
给出相同的结果。
In [124]: extract_result = s.str.extract(two_groups, expand=True)
In [125]: extract_result
Out[125]:
letter digit
0 a 3
1 b 3
2 c 2
In [126]: extractall_result = s.str.extractall(two_groups)
In [127]: extractall_result
Out[127]:
letter digit
match
0 0 a 3
1 0 b 3
2 0 c 2
In [128]: extractall_result.xs(0, level="match")
Out[128]:
letter digit
0 a 3
1 b 3
2 c 2
Index
还支持.str.extractall
。它返回一个与具有默认索引(从 0 开始)的Series.str.extractall
具有相同结果的DataFrame
。
In [129]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[129]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
In [130]: pd.Series(["a1a2", "b1", "c1"], dtype="string").str.extractall(two_groups)
Out[130]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
测试匹配或包含模式的字符串
您可以检查元素是否包含模式:
In [131]: pattern = r"[0-9][a-z]"
In [132]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.contains(pattern)
.....:
Out[132]:
0 False
1 False
2 True
3 True
4 True
5 True
dtype: boolean
或者元素是否匹配模式:
In [133]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.match(pattern)
.....:
Out[133]:
0 False
1 False
2 True
3 True
4 False
5 True
dtype: boolean
In [134]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.fullmatch(pattern)
.....:
Out[134]:
0 False
1 False
2 True
3 True
4 False
5 False
dtype: boolean
注意
match
、fullmatch
和contains
之间的区别在于严格性:fullmatch
测试整个字符串是否与正则表达式匹配;match
测试正则表达式是否在字符串的第一个字符处匹配;contains
测试字符串中是否在任何位置匹配正则表达式。
这三种匹配模式在re
包中对应的函数分别是re.fullmatch,re.match和re.search。
match
、fullmatch
、contains
、startswith
和endswith
等方法接受额外的na
参数,因此缺失值可以被视为 True 或 False:
In [135]: s4 = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [136]: s4.str.contains("A", na=False)
Out[136]:
0 True
1 False
2 False
3 True
4 False
5 False
6 True
7 False
8 False
dtype: boolean
创建指示变量
您可以从字符串列中提取虚拟变量。例如,如果它们由'|'
分隔:
In [137]: s = pd.Series(["a", "a|b", np.nan, "a|c"], dtype="string")
In [138]: s.str.get_dummies(sep="|")
Out[138]:
a b c
0 1 0 0
1 1 1 0
2 0 0 0
3 1 0 1
字符串Index
还支持get_dummies
,它返回一个MultiIndex
。
In [139]: idx = pd.Index(["a", "a|b", np.nan, "a|c"])
In [140]: idx.str.get_dummies(sep="|")
Out[140]:
MultiIndex([(1, 0, 0),
(1, 1, 0),
(0, 0, 0),
(1, 0, 1)],
names=['a', 'b', 'c'])
另请参见get_dummies()
。
方法摘要
方法 | 描述 |
---|---|
cat() |
连接字符串 |
split() |
使用分隔符拆分字符串 |
rsplit() |
从字符串末尾开始使用分隔符拆分字符串 |
get() |
索引到每个元素(检索第 i 个元素) |
join() |
使用传递的分隔符连接 Series 中每个元素的字符串 |
get_dummies() |
使用分隔符拆分字符串,返回包含虚拟变量的 DataFrame |
contains() |
如果每个字符串包含模式/正则表达式,则返回布尔数组 |
replace() |
用其他字符串或可调用对象的返回值替换模式/正则表达式/字符串的出现 |
removeprefix() |
从字符串中移除前缀,即仅在字符串以前缀开头时才移除。 |
removesuffix() |
从字符串中移除后缀,即仅在字符串以后缀结尾时才移除。 |
repeat() |
复制值(s.str.repeat(3) 等同于 x * 3 ) |
pad() |
在字符串的左侧、右侧或两侧添加空格 |
center() |
等同于 str.center |
ljust() |
等同于 str.ljust |
rjust() |
等同于 str.rjust |
zfill() |
等同于 str.zfill |
wrap() |
将长字符串拆分为长度小于给定宽度的行 |
slice() |
对 Series 中的每个字符串进行切片 |
slice_replace() |
用传递的值替换每个字符串中的切片 |
count() |
计算模式的出现次数 |
startswith() |
对每个元素等同于 str.startswith(pat) |
endswith() |
对每个元素等同于 str.endswith(pat) |
findall() |
计算每个字符串中模式/正则表达式的所有出现 |
match() |
对每个元素调用 re.match ,返回匹配的组列表 |
extract() |
对每个元素调用re.search ,返回一个 DataFrame,每个元素一行,每个正则表达式捕获组一列 |
extractall() |
对每个元素调用re.findall ,返回一个 DataFrame,每个匹配项一行,每个正则表达式捕获组一列 |
len() |
计算字符串长度 |
strip() |
等同于 str.strip |
rstrip() |
等同于 str.rstrip |
lstrip() |
等同于 str.lstrip |
partition() |
等同于 str.partition |
rpartition() |
等同于 str.rpartition |
lower() |
等同于 str.lower |
casefold() |
等同于 str.casefold |
upper() |
等同于 str.upper |
find() |
等同于 str.find |
rfind() |
等同于 str.rfind |
index() |
等同于 str.index |
rindex() |
等同于 str.rindex |
capitalize() |
等同于 str.capitalize |
swapcase() |
等同于 str.swapcase |
normalize() |
返回 Unicode 正规形式。等同于unicodedata.normalize |
translate() |
等同于 str.translate |
isalnum() |
等同于 str.isalnum |
isalpha() |
等同于 str.isalpha |
isdigit() |
等同于 str.isdigit |
isspace() |
等同于 str.isspace |
islower() |
等同于 str.islower |
isupper() |
等同于 str.isupper |
istitle() |
等同于 str.istitle |
isnumeric() |
等同于 str.isnumeric |
isdecimal() |
等同于 str.isdecimal |
文本数据类型
在 pandas 中有两种存储文本数据的方式:
-
object
-dtype NumPy 数组。 -
StringDtype
扩展类型。
我们建议使用StringDtype
来存储文本数据。
在 pandas 1.0 之前,object
dtype 是唯一的选择。这在很多方面都是不幸的:
-
在
object
dtype 数组中可能会意外存储字符串和非字符串的混合。最好使用专用的 dtype。 -
object
dtype 会破坏 dtype 特定的操作,比如DataFrame.select_dtypes()
。没有明确的方法可以仅选择文本而排除非文本但仍为 object-dtype 的列。 -
阅读代码时,
object
dtype 数组的内容不如'string'
清晰。
目前,object
dtype 类型的字符串数组和arrays.StringArray
的性能大致相同。我们期待未来的增强将显著提高StringArray
的性能并降低内存开销。
警告
StringArray
目前被视为实验性质。实现和部分 API 可能会在没有警告的情况下发生变化。
为了向后兼容,我们推断字符串列表的默认类型仍然是object
dtype
In [1]: pd.Series(["a", "b", "c"])
Out[1]:
0 a
1 b
2 c
dtype: object
要明确请求string
dtype,请指定dtype
In [2]: pd.Series(["a", "b", "c"], dtype="string")
Out[2]:
0 a
1 b
2 c
dtype: string
In [3]: pd.Series(["a", "b", "c"], dtype=pd.StringDtype())
Out[3]:
0 a
1 b
2 c
dtype: string
在创建Series
或DataFrame
之后使用astype
In [4]: s = pd.Series(["a", "b", "c"])
In [5]: s
Out[5]:
0 a
1 b
2 c
dtype: object
In [6]: s.astype("string")
Out[6]:
0 a
1 b
2 c
dtype: string
你也可以在非字符串数据上使用StringDtype
/"string"
作为 dtype,它将被转换为string
dtype:
In [7]: s = pd.Series(["a", 2, np.nan], dtype="string")
In [8]: s
Out[8]:
0 a
1 2
2 <NA>
dtype: string
In [9]: type(s[1])
Out[9]: str
或者从现有的 pandas 数据转换:
In [10]: s1 = pd.Series([1, 2, np.nan], dtype="Int64")
In [11]: s1
Out[11]:
0 1
1 2
2 <NA>
dtype: Int64
In [12]: s2 = s1.astype("string")
In [13]: s2
Out[13]:
0 1
1 2
2 <NA>
dtype: string
In [14]: type(s2[0])
Out[14]: str
行为差异
这些是StringDtype
对象的行为与object
dtype 不同的地方
-
对于
StringDtype
,返回numeric输出的 string 访问器方法将始终返回可空整数 dtype,而不是根据 NA 值的存在而返回 int 或 float dtype。返回boolean输出的方法将返回可空布尔 dtype。In [15]: s = pd.Series(["a", None, "b"], dtype="string") In [16]: s Out[16]: 0 a 1 <NA> 2 b dtype: string In [17]: s.str.count("a") Out[17]: 0 1 1 <NA> 2 0 dtype: Int64 In [18]: s.dropna().str.count("a") Out[18]: 0 1 2 0 dtype: Int64
两个输出都是
Int64
dtype。与 object-dtype 进行比较In [19]: s2 = pd.Series(["a", None, "b"], dtype="object") In [20]: s2.str.count("a") Out[20]: 0 1.0 1 NaN 2 0.0 dtype: float64 In [21]: s2.dropna().str.count("a") Out[21]: 0 1 2 0 dtype: int64
当存在 NA 值时,输出 dtype 为 float64。返回布尔值的方法也是如此。
In [22]: s.str.isdigit() Out[22]: 0 False 1 <NA> 2 False dtype: boolean In [23]: s.str.match("a") Out[23]: 0 True 1 <NA> 2 False dtype: boolean
-
一些字符串方法,比如
Series.str.decode()
在StringArray
上不可用,因为StringArray
只保存字符串,而不是字节。 -
在比较操作中,
arrays.StringArray
和由StringArray
支持的Series
将返回一个带有BooleanDtype
的对象,而不是bool
dtype 对象。在StringArray
中的缺失值将在比较操作中传播,而不像numpy.nan
那样总是比较不相等。
本文档其余部分中的其他内容同样适用于string
和object
dtype。### 行为差异
这些是StringDtype
对象的行为与object
dtype 不同的地方
-
对于
StringDtype
,返回numeric输出的 string 访问器方法将始终返回可空整数 dtype,而不是根据 NA 值的存在而返回 int 或 float dtype。返回boolean输出的方法将返回可空布尔 dtype。In [15]: s = pd.Series(["a", None, "b"], dtype="string") In [16]: s Out[16]: 0 a 1 <NA> 2 b dtype: string In [17]: s.str.count("a") Out[17]: 0 1 1 <NA> 2 0 dtype: Int64 In [18]: s.dropna().str.count("a") Out[18]: 0 1 2 0 dtype: Int64
两个输出都是
Int64
dtype。与 object-dtype 进行比较In [19]: s2 = pd.Series(["a", None, "b"], dtype="object") In [20]: s2.str.count("a") Out[20]: 0 1.0 1 NaN 2 0.0 dtype: float64 In [21]: s2.dropna().str.count("a") Out[21]: 0 1 2 0 dtype: int64
当存在 NA 值时,输出 dtype 为 float64。返回布尔值的方法也是如此。
In [22]: s.str.isdigit() Out[22]: 0 False 1 <NA> 2 False dtype: boolean In [23]: s.str.match("a") Out[23]: 0 True 1 <NA> 2 False dtype: boolean
-
一些字符串方法,比如
Series.str.decode()
在StringArray
上不可用,因为StringArray
只保存字符串,而不是字节。 -
在比较操作中,由
StringArray
支持的arrays.StringArray
和Series
将返回一个具有BooleanDtype
的对象,而不是bool
dtype 对象。在StringArray
中的缺失值将在比较操作中传播,而不像numpy.nan
那样总是比较不相等。
本文档其余部分中的所有内容同样适用于string
和object
dtype。
字符串方法
Series 和 Index 配备了一组字符串处理方法,使得在数组的每个元素上操作变得容易。也许最重要的是,这些方法会自动排除缺失/NA 值。这些方法通过str
属性访问,通常具有与等效(标量)内置字符串方法匹配的名称:
In [24]: s = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
....: )
....:
In [25]: s.str.lower()
Out[25]:
0 a
1 b
2 c
3 aaba
4 baca
5 <NA>
6 caba
7 dog
8 cat
dtype: string
In [26]: s.str.upper()
Out[26]:
0 A
1 B
2 C
3 AABA
4 BACA
5 <NA>
6 CABA
7 DOG
8 CAT
dtype: string
In [27]: s.str.len()
Out[27]:
0 1
1 1
2 1
3 4
4 4
5 <NA>
6 4
7 3
8 3
dtype: Int64
In [28]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])
In [29]: idx.str.strip()
Out[29]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')
In [30]: idx.str.lstrip()
Out[30]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')
In [31]: idx.str.rstrip()
Out[31]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')
索引上的字符串方法特别适用于清理或转换 DataFrame 列。例如,您可能有带有前导或尾随空格的列:
In [32]: df = pd.DataFrame(
....: np.random.randn(3, 2), columns=[" Column A ", " Column B "], index=range(3)
....: )
....:
In [33]: df
Out[33]:
Column A Column B
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
由于df.columns
是一个 Index 对象,我们可以使用.str
访问器
In [34]: df.columns.str.strip()
Out[34]: Index(['Column A', 'Column B'], dtype='object')
In [35]: df.columns.str.lower()
Out[35]: Index([' column a ', ' column b '], dtype='object')
然后可以使用这些字符串方法根据需要清理列。在这里,我们删除前导和尾随空格,将所有���称转换为小写,并用下划线替换任何剩余的空格:
In [36]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
In [37]: df
Out[37]:
column_a column_b
0 0.469112 -0.282863
1 -1.509059 -1.135632
2 1.212112 -0.173215
注意
如果您有一个Series
,其中有很多重复的元素(即Series
中唯一元素的数量远小于Series
的长度),将原始Series
转换为category
类型,然后在其上使用.str.<method>
或.dt.<property>
可能会更快。性能差异在于,对于category
类型的Series
,字符串操作是在.categories
上而不是在Series
的每个元素上进行的。
请注意,具有字符串.categories
的category
类型的Series
与字符串类型的Series
相比存在一些限制(例如,如果s
是category
类型的Series
,则无法将字符串添加到彼此:s + " " + s
不起作用)。此外,对list
类型元素进行操作的.str
方法在此类Series
上不可用。
警告
系列的类型是推断的,允许的类型为(即字符串)。
一般来说,.str
访问器仅用于处理字符串。除了极少数例外情况外,不支持其他用途,并且可能在以后被禁用。
拆分和替换字符串
像split
这样的方法会返回一个列表的 Series:
In [38]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="string")
In [39]: s2.str.split("_")
Out[39]:
0 [a, b, c]
1 [c, d, e]
2 <NA>
3 [f, g, h]
dtype: object
可以使用get
或[]
表示法访问拆分列表中的元素:
In [40]: s2.str.split("_").str.get(1)
Out[40]:
0 b
1 d
2 <NA>
3 g
dtype: object
In [41]: s2.str.split("_").str[1]
Out[41]:
0 b
1 d
2 <NA>
3 g
dtype: object
使用expand
很容易扩展到返回 DataFrame。
In [42]: s2.str.split("_", expand=True)
Out[42]:
0 1 2
0 a b c
1 c d e
2 <NA> <NA> <NA>
3 f g h
当原始Series
具有StringDtype
时,输出列也将全部是StringDtype
。
也可以限制拆分的数量:
In [43]: s2.str.split("_", expand=True, n=1)
Out[43]:
0 1
0 a b_c
1 c d_e
2 <NA> <NA>
3 f g_h
rsplit
类似于split
,只是它是从字符串的末尾到字符串的开头的方向工作:
In [44]: s2.str.rsplit("_", expand=True, n=1)
Out[44]:
0 1
0 a_b c
1 c_d e
2 <NA> <NA>
3 f_g h
replace
可选使用正则表达式:
In [45]: s3 = pd.Series(
....: ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
....: dtype="string",
....: )
....:
In [46]: s3
Out[46]:
0 A
1 B
2 C
3 Aaba
4 Baca
5
6 <NA>
7 CABA
8 dog
9 cat
dtype: string
In [47]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
Out[47]:
0 A
1 B
2 C
3 XX-XX ba
4 XX-XX ca
5
6 <NA>
7 XX-XX BA
8 XX-XX
9 XX-XX t
dtype: string
版本 2.0���的更改。
使用regex=True
的单个字符模式也将被视为正则表达式:
In [48]: s4 = pd.Series(["a.b", ".", "b", np.nan, ""], dtype="string")
In [49]: s4
Out[49]:
0 a.b
1 .
2 b
3 <NA>
4
dtype: string
In [50]: s4.str.replace(".", "a", regex=True)
Out[50]:
0 aaa
1 a
2 a
3 <NA>
4
dtype: string
如果要对字符串进行字面替换(相当于str.replace()
),可以将可选的regex
参数设置为False
,而不是转义每个字符。在这种情况下,pat
和repl
都必须是字符串:
In [51]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="string")
# These lines are equivalent
In [52]: dollars.str.replace(r"-\$", "-", regex=True)
Out[52]:
0 12
1 -10
2 $10,000
dtype: string
In [53]: dollars.str.replace("-$", "-", regex=False)
Out[53]:
0 12
1 -10
2 $10,000
dtype: string
replace
方法还可以将可调用对象作为替换。它会使用re.sub()
对每个pat
进行调用。可调用对象应该期望一个位置参数(一个正则表达式对象)并返回一个字符串。
# Reverse every lowercase alphabetic word
In [54]: pat = r"[a-z]+"
In [55]: def repl(m):
....: return m.group(0)[::-1]
....:
In [56]: pd.Series(["foo 123", "bar baz", np.nan], dtype="string").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[56]:
0 oof 123
1 rab zab
2 <NA>
dtype: string
# Using regex groups
In [57]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
In [58]: def repl(m):
....: return m.group("two").swapcase()
....:
In [59]: pd.Series(["Foo Bar Baz", np.nan], dtype="string").str.replace(
....: pat, repl, regex=True
....: )
....:
Out[59]:
0 bAR
1 <NA>
dtype: string
replace
方法还接受从re.compile()
编译的正则表达式对象作为模式。所有标志应包含在编译的正则表达式对象中。
In [60]: import re
In [61]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)
In [62]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
Out[62]:
0 A
1 B
2 C
3 XX-XX ba
4 XX-XX ca
5
6 <NA>
7 XX-XX BA
8 XX-XX
9 XX-XX t
dtype: string
在使用编译的正则表达式对象调用replace
时包含flags
参数将引发ValueError
。
In [63]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex
removeprefix
和removesuffix
与 Python 3.9 中添加的str.removeprefix
和str.removesuffix
具有相同的效果 <docs.python.org/3/library/stdtypes.html#str.removeprefix
>`__:
版本 1.4.0 中的新功能。
In [64]: s = pd.Series(["str_foo", "str_bar", "no_prefix"])
In [65]: s.str.removeprefix("str_")
Out[65]:
0 foo
1 bar
2 no_prefix
dtype: object
In [66]: s = pd.Series(["foo_str", "bar_str", "no_suffix"])
In [67]: s.str.removesuffix("_str")
Out[67]:
0 foo
1 bar
2 no_suffix
dtype: object
连接
有几种方法可以连接Series
或Index
,无论是与自身还是其他对象,都基于cat()
或Index.str.cat
。
将单个 Series 连接成字符串
可以连接Series
(或Index
)的内容:
In [68]: s = pd.Series(["a", "b", "c", "d"], dtype="string")
In [69]: s.str.cat(sep=",")
Out[69]: 'a,b,c,d'
如果未指定,分隔符的关键字sep
默认为空字符串,sep=''
:
In [70]: s.str.cat()
Out[70]: 'abcd'
默认情况下,缺失值会被忽略。使用na_rep
,它们可以被赋予一个表示:
In [71]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")
In [72]: t.str.cat(sep=",")
Out[72]: 'a,b,d'
In [73]: t.str.cat(sep=",", na_rep="-")
Out[73]: 'a,b,-,d'
将 Series 和类似列表的内容连接成 Series
cat()
的第一个参数可以是类似列表的对象,只要它与调用的Series
(或Index
)的长度匹配。
In [74]: s.str.cat(["A", "B", "C", "D"])
Out[74]:
0 aA
1 bB
2 cC
3 dD
dtype: string
任一侧的缺失值也会导致结果中的缺失值,除非指定了na_rep
:
In [75]: s.str.cat(t)
Out[75]:
0 aa
1 bb
2 <NA>
3 dd
dtype: string
In [76]: s.str.cat(t, na_rep="-")
Out[76]:
0 aa
1 bb
2 c-
3 dd
dtype: string
将 Series 和类似数组的内容连接成 Series
参数others
也可以是二维的。在这种情况下,行数必须与调用的Series
(或Index
)的长度匹配。
In [77]: d = pd.concat([t, s], axis=1)
In [78]: s
Out[78]:
0 a
1 b
2 c
3 d
dtype: string
In [79]: d
Out[79]:
0 1
0 a a
1 b b
2 <NA> c
3 d d
In [80]: s.str.cat(d, na_rep="-")
Out[80]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将 Series 和索引对象连接成 Series,进行对齐
对于与Series
或DataFrame
连接,可以通过设置join
关键字来在连接之前对齐索引。
In [81]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")
In [82]: s
Out[82]:
0 a
1 b
2 c
3 d
dtype: string
In [83]: u
Out[83]:
1 b
3 d
0 a
2 c
dtype: string
In [84]: s.str.cat(u)
Out[84]:
0 aa
1 bb
2 cc
3 dd
dtype: string
In [85]: s.str.cat(u, join="left")
Out[85]:
0 aa
1 bb
2 cc
3 dd
dtype: string
对于join
的常见选项(其中之一为'left', 'outer', 'inner', 'right'
)都是可用的。特别是,对齐也意味着不同长度不再需要一致。
In [86]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")
In [87]: s
Out[87]:
0 a
1 b
2 c
3 d
dtype: string
In [88]: v
Out[88]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [89]: s.str.cat(v, join="left", na_rep="-")
Out[89]:
0 aa
1 bb
2 c-
3 dd
dtype: string
In [90]: s.str.cat(v, join="outer", na_rep="-")
Out[90]:
-1 -z
0 aa
1 bb
2 c-
3 dd
4 -e
dtype: string
当others
是一个DataFrame
时,可以使用相同的对齐方式:
In [91]: f = d.loc[[3, 2, 1, 0], :]
In [92]: s
Out[92]:
0 a
1 b
2 c
3 d
dtype: string
In [93]: f
Out[93]:
0 1
3 d d
2 <NA> c
1 b b
0 a a
In [94]: s.str.cat(f, join="left", na_rep="-")
Out[94]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将一个 Series 和多个对象连接成一个 Series
几个类似数组的项目(具体来说:Series
,Index
和np.ndarray
的一维变体)可以组合在一个类似列表的容器中(包括迭代器,dict
视图等)。
In [95]: s
Out[95]:
0 a
1 b
2 c
3 d
dtype: string
In [96]: u
Out[96]:
1 b
3 d
0 a
2 c
dtype: string
In [97]: s.str.cat([u, u.to_numpy()], join="left")
Out[97]:
0 aab
1 bbd
2 cca
3 ddc
dtype: string
传递的类似列表中没有索引的所有元素(例如np.ndarray
)必须与调用的Series
(或Index
)的长度匹配,但Series
和Index
的长度可以是任意的(只要不使用join=None
禁用对齐):
In [98]: v
Out[98]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [99]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[99]:
-1 -z--
0 aaab
1 bbbd
2 c-ca
3 dddc
4 -e--
dtype: string
如果在包含不同索引的others
列表上使用join='right'
,则这些索引的并集将被用作最终连接的基础:
In [100]: u.loc[[3]]
Out[100]:
3 d
dtype: string
In [101]: v.loc[[-1, 0]]
Out[101]:
-1 z
0 a
dtype: string
In [102]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[102]:
3 dd-
-1 --z
0 a-a
dtype: string
将一个单独的 Series 连接成一个字符串
可以连接Series
(或Index
)的内容:
In [68]: s = pd.Series(["a", "b", "c", "d"], dtype="string")
In [69]: s.str.cat(sep=",")
Out[69]: 'a,b,c,d'
如果未指定,分隔符的关键字sep
默认为空字符串,sep=''
:
In [70]: s.str.cat()
Out[70]: 'abcd'
默认情况下,缺失值会被忽略。使用na_rep
,它们可以被赋予一个表示:
In [71]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")
In [72]: t.str.cat(sep=",")
Out[72]: 'a,b,d'
In [73]: t.str.cat(sep=",", na_rep="-")
Out[73]: 'a,b,-,d'
将一个 Series 和类似列表的东西连接成一个 Series
cat()
的第一个参数可以是一个类似列表的对象,只要它与调用的Series
(或Index
)的长度匹配即可。
In [74]: s.str.cat(["A", "B", "C", "D"])
Out[74]:
0 aA
1 bB
2 cC
3 dD
dtype: string
任一侧的缺失值也会导致结果中的缺失值,除非指定了na_rep
:
In [75]: s.str.cat(t)
Out[75]:
0 aa
1 bb
2 <NA>
3 dd
dtype: string
In [76]: s.str.cat(t, na_rep="-")
Out[76]:
0 aa
1 bb
2 c-
3 dd
dtype: string
将一个 Series 和类似数组的东西连接成一个 Series
参数others
也可以是二维的。在这种情况下,行数必须与调用的Series
(或Index
)的长度相匹配。
In [77]: d = pd.concat([t, s], axis=1)
In [78]: s
Out[78]:
0 a
1 b
2 c
3 d
dtype: string
In [79]: d
Out[79]:
0 1
0 a a
1 b b
2 <NA> c
3 d d
In [80]: s.str.cat(d, na_rep="-")
Out[80]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将一个 Series 和一个带索引的对象连接成一个 Series,带有对齐
对于与Series
或DataFrame
连接,可以通过设置join
关键字来在连接之前对齐索引。
In [81]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")
In [82]: s
Out[82]:
0 a
1 b
2 c
3 d
dtype: string
In [83]: u
Out[83]:
1 b
3 d
0 a
2 c
dtype: string
In [84]: s.str.cat(u)
Out[84]:
0 aa
1 bb
2 cc
3 dd
dtype: string
In [85]: s.str.cat(u, join="left")
Out[85]:
0 aa
1 bb
2 cc
3 dd
dtype: string
对于join
的常见选项(其中之一为'left', 'outer', 'inner', 'right'
)都是可用的。特别是,对齐也意味着不同长度不再需要一致。
In [86]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")
In [87]: s
Out[87]:
0 a
1 b
2 c
3 d
dtype: string
In [88]: v
Out[88]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [89]: s.str.cat(v, join="left", na_rep="-")
Out[89]:
0 aa
1 bb
2 c-
3 dd
dtype: string
In [90]: s.str.cat(v, join="outer", na_rep="-")
Out[90]:
-1 -z
0 aa
1 bb
2 c-
3 dd
4 -e
dtype: string
当others
是一个DataFrame
时,可以使用相同的对齐方式:
In [91]: f = d.loc[[3, 2, 1, 0], :]
In [92]: s
Out[92]:
0 a
1 b
2 c
3 d
dtype: string
In [93]: f
Out[93]:
0 1
3 d d
2 <NA> c
1 b b
0 a a
In [94]: s.str.cat(f, join="left", na_rep="-")
Out[94]:
0 aaa
1 bbb
2 c-c
3 ddd
dtype: string
将一个 Series 和多个对象连接成一个 Series
几个类似数组的项目(具体来说:Series
,Index
和np.ndarray
的一维变体)可以组合在一个类似列表的容器中(包括迭代器,dict
视图等)。
In [95]: s
Out[95]:
0 a
1 b
2 c
3 d
dtype: string
In [96]: u
Out[96]:
1 b
3 d
0 a
2 c
dtype: string
In [97]: s.str.cat([u, u.to_numpy()], join="left")
Out[97]:
0 aab
1 bbd
2 cca
3 ddc
dtype: string
传递的类似列表中没有索引的所有元素(例如np.ndarray
)必须与调用的Series
(或Index
)的长度匹配,但Series
和Index
的长度可以是任意的(只要不使用join=None
禁用对齐):
In [98]: v
Out[98]:
-1 z
0 a
1 b
3 d
4 e
dtype: string
In [99]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[99]:
-1 -z--
0 aaab
1 bbbd
2 c-ca
3 dddc
4 -e--
dtype: string
如果在包含不同索引的others
列表上使用join='right'
,则这些索引的并集将被用作最终连接的基础:
In [100]: u.loc[[3]]
Out[100]:
3 d
dtype: string
In [101]: v.loc[[-1, 0]]
Out[101]:
-1 z
0 a
dtype: string
In [102]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[102]:
3 dd-
-1 --z
0 a-a
dtype: string
使用.str
进行索引
您可以使用 []
符号直接按位置索引。如果索引超出字符串的末尾,结果将是一个 NaN
。
In [103]: s = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [104]: s.str[0]
Out[104]:
0 A
1 B
2 C
3 A
4 B
5 <NA>
6 C
7 d
8 c
dtype: string
In [105]: s.str[1]
Out[105]:
0 <NA>
1 <NA>
2 <NA>
3 a
4 a
5 <NA>
6 A
7 o
8 a
dtype: string
提取子字符串
提取每个主题中的第一个匹配项(extract)
extract
方法接受至少一个捕获组的 正则表达式。
提取具有多个组的正则表达式将返回一个每组一列的 DataFrame。
In [106]: pd.Series(
.....: ["a1", "b2", "c3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])(\d)", expand=False)
.....:
Out[106]:
0 1
0 a 1
1 b 2
2 <NA> <NA>
不匹配的元素返回一个填充有 NaN
的行。因此,一系列混乱的字符串可以“转换”为一个具有相同索引的已清理或更有用的字符串的 Series 或 DataFrame,而无需使用 get()
访问元组或 re.match
对象。结果的 dtype 始终为 object,即使未找到匹配项,结果仅包含 NaN
。
命名组如
In [107]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(
.....: r"(?P<letter>[ab])(?P<digit>\d)", expand=False
.....: )
.....:
Out[107]:
letter digit
0 a 1
1 b 2
2 <NA> <NA>
和可选组
In [108]: pd.Series(
.....: ["a1", "b2", "3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])?(\d)", expand=False)
.....:
Out[108]:
0 1
0 a 1
1 b 2
2 <NA> 3
也可以使用。请注意,正则表达式中的任何捕获组名称将用于列名;否则将使用捕获组编号。
提取具有一个组的正则表达式将返回一个列的 DataFrame,如果 expand=True
。
In [109]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=True)
Out[109]:
0
0 1
1 2
2 <NA>
如果 expand=False
,则返回一个 Series。
In [110]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=False)
Out[110]:
0 1
1 2
2 <NA>
dtype: string
对具有正好一个捕获组的正则表达式的 Index
调用,如果 expand=True
,则返回一个列。
In [111]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")
In [112]: s
Out[112]:
A11 a1
B22 b2
C33 c3
dtype: string
In [113]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[113]:
letter
0 A
1 B
2 C
如果 expand=False
,则返回一个 Index
。
In [114]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[114]: Index(['A', 'B', 'C'], dtype='object', name='letter')
对具有多个捕获组的正则表达式的 Index
调用,如果 expand=True
,则返回一个 DataFrame。
In [115]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[115]:
letter 1
0 A 11
1 B 22
2 C 33
如果 expand=False
,则引发 ValueError
。
In [116]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[116], line 1
----> 1 s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:137, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
132 msg = (
133 f"Cannot use .str.{func_name} with values of "
134 f"inferred dtype '{self._inferred_dtype}'."
135 )
136 raise TypeError(msg)
--> 137 return func(self, *args, **kwargs)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:2743, in StringMethods.extract(self, pat, flags, expand)
2740 raise ValueError("pattern contains no capture groups")
2742 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 2743 raise ValueError("only one regex group is supported with Index")
2745 obj = self._data
2746 result_dtype = _result_dtype(obj)
ValueError: only one regex group is supported with Index
下表总结了 extract(expand=False)
的行为(输入主题在第一列,正则表达式中的组数在第一行)
1 组 | >1 组 | |
---|---|---|
Index | Index | ValueError |
Series | Series | DataFrame |
提取每个主题中的所有匹配项(extractall)
与仅返回第一个匹配项的 extract
不同,
In [117]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="string")
In [118]: s
Out[118]:
A a1a2
B b1
C c1
dtype: string
In [119]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"
In [120]: s.str.extract(two_groups, expand=True)
Out[120]:
letter digit
A a 1
B b 1
C c 1
extractall
方法返回每个匹配项。extractall
的结果始终是一个具有行上的 MultiIndex
的 DataFrame。MultiIndex
的最后一级命名为 match
,表示主题中的顺序。
In [121]: s.str.extractall(two_groups)
Out[121]:
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1
当 Series 中的每个主题字符串恰好有一个匹配项时,
In [122]: s = pd.Series(["a3", "b3", "c2"], dtype="string")
In [123]: s
Out[123]:
0 a3
1 b3
2 c2
dtype: string
然后 extractall(pat).xs(0, level='match')
给出与 extract(pat)
相同的结果。
In [124]: extract_result = s.str.extract(two_groups, expand=True)
In [125]: extract_result
Out[125]:
letter digit
0 a 3
1 b 3
2 c 2
In [126]: extractall_result = s.str.extractall(two_groups)
In [127]: extractall_result
Out[127]:
letter digit
match
0 0 a 3
1 0 b 3
2 0 c 2
In [128]: extractall_result.xs(0, level="match")
Out[128]:
letter digit
0 a 3
1 b 3
2 c 2
Index
还支持 .str.extractall
。它返回一个 DataFrame
,其结果与具有默认索引(从 0 开始)的 Series.str.extractall
相同。
In [129]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[129]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
In [130]: pd.Series(["a1a2", "b1", "c1"], dtype="string").str.extractall(two_groups)
Out[130]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
提取每个主题中的第一个匹配项(extract)
extract
方法接受至少一个捕获组的 正则表达式。
提取具有多个组的正则表达式将返回一个每组一列的 DataFrame。
In [106]: pd.Series(
.....: ["a1", "b2", "c3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])(\d)", expand=False)
.....:
Out[106]:
0 1
0 a 1
1 b 2
2 <NA> <NA>
不匹配的元素返回一个填充有 NaN
的行。因此,一系列混乱的字符串可以“转换”为一个具有相同索引的已清理或更有用的字符串的 Series 或 DataFrame,而无需使用 get()
访问元组或 re.match
对象。结果的 dtype 始终为 object,即使未找到匹配项,结果仅包含 NaN
。
命名组如
In [107]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(
.....: r"(?P<letter>[ab])(?P<digit>\d)", expand=False
.....: )
.....:
Out[107]:
letter digit
0 a 1
1 b 2
2 <NA> <NA>
和可选���
In [108]: pd.Series(
.....: ["a1", "b2", "3"],
.....: dtype="string",
.....: ).str.extract(r"([ab])?(\d)", expand=False)
.....:
Out[108]:
0 1
0 a 1
1 b 2
2 <NA> 3
也可以使用。请注意,正则表达式中的任何捕获组名称将用于列名;否则将使用捕获组编号。
使用一个组提取正则表达式,如果expand=True
,则返回一个列的DataFrame
。
In [109]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=True)
Out[109]:
0
0 1
1 2
2 <NA>
如果expand=False
,则返回一个 Series。
In [110]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"ab", expand=False)
Out[110]:
0 1
1 2
2 <NA>
dtype: string
对具有正好一个捕获组的正则表达式调用Index
,如果expand=True
,则返回一个列的DataFrame
。
In [111]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")
In [112]: s
Out[112]:
A11 a1
B22 b2
C33 c3
dtype: string
In [113]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[113]:
letter
0 A
1 B
2 C
如果expand=False
,则返回一个Index
。
In [114]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[114]: Index(['A', 'B', 'C'], dtype='object', name='letter')
对具有多个捕获组的正则表达式调用Index
,如果expand=True
,则返回一个DataFrame
。
In [115]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[115]:
letter 1
0 A 11
1 B 22
2 C 33
如果expand=False
,则会引发ValueError
。
In [116]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[116], line 1
----> 1 s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:137, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
132 msg = (
133 f"Cannot use .str.{func_name} with values of "
134 f"inferred dtype '{self._inferred_dtype}'."
135 )
136 raise TypeError(msg)
--> 137 return func(self, *args, **kwargs)
File ~/work/pandas/pandas/pandas/core/strings/accessor.py:2743, in StringMethods.extract(self, pat, flags, expand)
2740 raise ValueError("pattern contains no capture groups")
2742 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 2743 raise ValueError("only one regex group is supported with Index")
2745 obj = self._data
2746 result_dtype = _result_dtype(obj)
ValueError: only one regex group is supported with Index
下表总结了extract(expand=False)
的行为(第一列为输入主题,第一行为正则表达式中的组数)
1 组 | >1 组 | |
---|---|---|
Index | Index | ValueError |
Series | Series | DataFrame |
提取每个主题中的所有匹配(extractall)
与仅返回第一个匹配的extract
不同,
In [117]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="string")
In [118]: s
Out[118]:
A a1a2
B b1
C c1
dtype: string
In [119]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"
In [120]: s.str.extract(two_groups, expand=True)
Out[120]:
letter digit
A a 1
B b 1
C c 1
extractall
方法返回每个匹配。extractall
的结果始终是具有其行上的MultiIndex
的DataFrame
。MultiIndex
的最后一级命名为match
,表示主题中的顺序。
In [121]: s.str.extractall(two_groups)
Out[121]:
letter digit
match
A 0 a 1
1 a 2
B 0 b 1
C 0 c 1
当 Series 中的每个主题字符串正好有一个匹配时,
In [122]: s = pd.Series(["a3", "b3", "c2"], dtype="string")
In [123]: s
Out[123]:
0 a3
1 b3
2 c2
dtype: string
然后extractall(pat).xs(0, level='match')
给出���extract(pat)
相同的结果。
In [124]: extract_result = s.str.extract(two_groups, expand=True)
In [125]: extract_result
Out[125]:
letter digit
0 a 3
1 b 3
2 c 2
In [126]: extractall_result = s.str.extractall(two_groups)
In [127]: extractall_result
Out[127]:
letter digit
match
0 0 a 3
1 0 b 3
2 0 c 2
In [128]: extractall_result.xs(0, level="match")
Out[128]:
letter digit
0 a 3
1 b 3
2 c 2
Index
也支持.str.extractall
。它返回一个DataFrame
,其结果与具有默认索引(从 0 开始)的Series.str.extractall
相同。
In [129]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[129]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
In [130]: pd.Series(["a1a2", "b1", "c1"], dtype="string").str.extractall(two_groups)
Out[130]:
letter digit
match
0 0 a 1
1 a 2
1 0 b 1
2 0 c 1
测试匹配或包含模式的字符串
您可以检查元素是否包含模式:
In [131]: pattern = r"[0-9][a-z]"
In [132]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.contains(pattern)
.....:
Out[132]:
0 False
1 False
2 True
3 True
4 True
5 True
dtype: boolean
或者元素是否匹配模式:
In [133]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.match(pattern)
.....:
Out[133]:
0 False
1 False
2 True
3 True
4 False
5 True
dtype: boolean
In [134]: pd.Series(
.....: ["1", "2", "3a", "3b", "03c", "4dx"],
.....: dtype="string",
.....: ).str.fullmatch(pattern)
.....:
Out[134]:
0 False
1 False
2 True
3 True
4 False
5 False
dtype: boolean
注意
match
、fullmatch
和contains
之间的区别是严格性:fullmatch
测试整个字符串是否与正则表达式匹配;match
测试字符串的第一个字符是否有正则表达式的匹配;contains
测试字符串中是否有正则表达式的匹配。
这三种匹配模式的re
包中对应的函数分别是re.fullmatch、re.match和re.search。
match
、fullmatch
、contains
、startswith
和endswith
等方法接受额外的na
参数,以便将缺失值视为 True 或 False:
In [135]: s4 = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [136]: s4.str.contains("A", na=False)
Out[136]:
0 True
1 False
2 False
3 True
4 False
5 False
6 True
7 False
8 False
dtype: boolean
创建指示变量
您可以从字符串列中提取虚拟变量。例如,如果它们由'|'
分隔:
In [137]: s = pd.Series(["a", "a|b", np.nan, "a|c"], dtype="string")
In [138]: s.str.get_dummies(sep="|")
Out[138]:
a b c
0 1 0 0
1 1 1 0
2 0 0 0
3 1 0 1
字符串Index
也支持get_dummies
,它返回一个MultiIndex
。
In [139]: idx = pd.Index(["a", "a|b", np.nan, "a|c"])
In [140]: idx.str.get_dummies(sep="|")
Out[140]:
MultiIndex([(1, 0, 0),
(1, 1, 0),
(0, 0, 0),
(1, 0, 1)],
names=['a', 'b', 'c'])
查看get_dummies()
。
方法摘要
方法 | 描述 |
---|---|
cat() |
连接字符串 |
split() |
在分隔符上拆分字符串 |
rsplit() |
在字符串上的分隔符上工作,从字符串的末尾开始分割 |
get() |
索引到每个元素(检索第 i 个元素) |
join() |
使用传递的分隔符将 Series 中每个元素的字符串连接起来 |
get_dummies() |
在分隔符上拆分字符串,返回虚拟变量的 DataFrame |
contains() |
如果每个字符串包含模式/正则表达式,则返回布尔数组 |
replace() |
用其他字符串或可调用函数的返回值替换模式/正则表达式/字符串的出现 |
removeprefix() |
从字符串中删除前缀,即仅在字符串以前缀开头时才删除。 |
removesuffix() |
从字符串中删除后缀,即仅在字符串以后缀结尾时才删除。 |
repeat() |
复制值(s.str.repeat(3) 等同于x * 3 ) |
pad() |
在字符串的左侧、右侧或两侧添加空格 |
center() |
等同于str.center |
ljust() |
等同于str.ljust |
rjust() |
等同于str.rjust |
zfill() |
等同于str.zfill |
wrap() |
将长字符串拆分为长度小于给定宽度的行 |
slice() |
切片 Series 中的每个字符串 |
slice_replace() |
用传递的值替换每个字符串中的切片 |
count() |
计算模式出现的次数 |
startswith() |
对每个元素等同于str.startswith(pat) |
endswith() |
对每个元素等同于str.endswith(pat) |
findall() |
计算每个字符串的模式/正则表达式的所有出现的列表 |
match() |
对每个元素调用re.match ,返回匹配的组成列表 |
extract() |
对每个元素调用re.search ,返回一个 DataFrame,每个元素一行,每个正则表达式捕获组一列 |
extractall() |
对每个元素调用re.findall ,返回一个 DataFrame,每个匹配一行,每个正则表达式捕获组一列 |
len() |
计算字符串长度 |
strip() |
等同于str.strip |
rstrip() |
等同于str.rstrip |
lstrip() |
等同于str.lstrip |
partition() |
等同于str.partition |
rpartition() |
等同于str.rpartition |
lower() |
等同于str.lower |
casefold() |
等同于str.casefold |
upper() |
等同于str.upper |
find() |
等同于 str.find |
rfind() |
等同于 str.rfind |
index() |
等同于 str.index |
rindex() |
等同于 str.rindex |
capitalize() |
等同于 str.capitalize |
swapcase() |
等同于 str.swapcase |
normalize() |
返回 Unicode 正规形式。等同于 unicodedata.normalize |
translate() |
等同于 str.translate |
isalnum() |
等同于 str.isalnum |
isalpha() |
等同于 str.isalpha |
isdigit() |
等同于 str.isdigit |
isspace() |
等同于 str.isspace |
islower() |
等同于 str.islower |
isupper() |
等同于 str.isupper |
istitle() |
等同于 str.istitle |
isnumeric() |
等同于 str.isnumeric |
isdecimal() |
等同于 str.isdecimal |
处理缺失数据
被视为“缺失”的值
pandas 使用不同的标记值来表示缺失值(也称为 NA),具体取决于数据类型。
numpy.nan
适用于 NumPy 数据类型。使用 NumPy 数据类型的缺点是原始数据类型将被强制转换为np.float64
或object
。
In [1]: pd.Series([1, 2], dtype=np.int64).reindex([0, 1, 2])
Out[1]:
0 1.0
1 2.0
2 NaN
dtype: float64
In [2]: pd.Series([True, False], dtype=np.bool_).reindex([0, 1, 2])
Out[2]:
0 True
1 False
2 NaN
dtype: object
NaT
适用于 NumPy 的np.datetime64
、np.timedelta64
和PeriodDtype
。对于类型应用程序,请使用api.types.NaTType
。
In [3]: pd.Series([1, 2], dtype=np.dtype("timedelta64[ns]")).reindex([0, 1, 2])
Out[3]:
0 0 days 00:00:00.000000001
1 0 days 00:00:00.000000002
2 NaT
dtype: timedelta64[ns]
In [4]: pd.Series([1, 2], dtype=np.dtype("datetime64[ns]")).reindex([0, 1, 2])
Out[4]:
0 1970-01-01 00:00:00.000000001
1 1970-01-01 00:00:00.000000002
2 NaT
dtype: datetime64[ns]
In [5]: pd.Series(["2020", "2020"], dtype=pd.PeriodDtype("D")).reindex([0, 1, 2])
Out[5]:
0 2020-01-01
1 2020-01-01
2 NaT
dtype: period[D]
NA
适用于StringDtype
、Int64Dtype
(和其他位宽)、Float64Dtype
(和其他位宽)、:class:BooleanDtype
和ArrowDtype
。这些类型将保持数据的原始数据类型。对于类型应用程序,请使用api.types.NAType
。
In [6]: pd.Series([1, 2], dtype="Int64").reindex([0, 1, 2])
Out[6]:
0 1
1 2
2 <NA>
dtype: Int64
In [7]: pd.Series([True, False], dtype="boolean[pyarrow]").reindex([0, 1, 2])
Out[7]:
0 True
1 False
2 <NA>
dtype: bool[pyarrow]
要检测这些缺失值,请使用isna()
或notna()
方法。
In [8]: ser = pd.Series([pd.Timestamp("2020-01-01"), pd.NaT])
In [9]: ser
Out[9]:
0 2020-01-01
1 NaT
dtype: datetime64[ns]
In [10]: pd.isna(ser)
Out[10]:
0 False
1 True
dtype: bool
注意
isna()
或notna()
也会将None
视为缺失值。
In [11]: ser = pd.Series([1, None], dtype=object)
In [12]: ser
Out[12]:
0 1
1 None
dtype: object
In [13]: pd.isna(ser)
Out[13]:
0 False
1 True
dtype: bool
警告
np.nan
、NaT
和NA
之间的相等比较不像None
那样起作用。
In [14]: None == None # noqa: E711
Out[14]: True
In [15]: np.nan == np.nan
Out[15]: False
In [16]: pd.NaT == pd.NaT
Out[16]: False
In [17]: pd.NA == pd.NA
Out[17]: <NA>
因此,DataFrame
或Series
与这些缺失值之一进行相等比较并不提供与isna()
或notna()
相同的信息。
In [18]: ser = pd.Series([True, None], dtype="boolean[pyarrow]")
In [19]: ser == pd.NA
Out[19]:
0 <NA>
1 <NA>
dtype: bool[pyarrow]
In [20]: pd.isna(ser)
Out[20]:
0 False
1 True
dtype: bool
NA
语义
警告
实验性:NA
的行为仍可能在没有警告的情况下发生变化。
从 pandas 1.0 开始,实验性的NA
值(单例)可用于表示标量缺失值。NA
的目标是提供一个可以在各种数据类型之间一致使用的“缺失”指示器(而不是根据数据类型而定的np.nan
、None
或pd.NaT
)。
例如,在具有可空整数 dtype 的 Series
中存在缺失值时,它将使用 NA
:
In [21]: s = pd.Series([1, 2, None], dtype="Int64")
In [22]: s
Out[22]:
0 1
1 2
2 <NA>
dtype: Int64
In [23]: s[2]
Out[23]: <NA>
In [24]: s[2] is pd.NA
Out[24]: True
目前,pandas 尚未默认使用那些使用 NA
的数据类型,如 DataFrame
或 Series
,因此您需要明确指定 dtype。在转换部分中解释了将其转换为这些 dtype 的简单方法。
算术和比较操作中的传播
一般来说,在涉及 NA
的操作中,缺失值会传播。当其中一个操作数未知时,操作的结果也是未知的。
例如,在算术操作中,NA
会传播,类似于 np.nan
:
In [25]: pd.NA + 1
Out[25]: <NA>
In [26]: "a" * pd.NA
Out[26]: <NA>
有一些特殊情况,即使其中一个操作数是NA
,结果也是已知的。
In [27]: pd.NA ** 0
Out[27]: 1
In [28]: 1 ** pd.NA
Out[28]: 1
在相等性和比较操作中,NA
也会传播。这与np.nan
的行为不同,其中与np.nan
的比较总是返回False
。
In [29]: pd.NA == 1
Out[29]: <NA>
In [30]: pd.NA == pd.NA
Out[30]: <NA>
In [31]: pd.NA < 2.5
Out[31]: <NA>
要检查一个值是否等于 NA
,请使用 isna()
In [32]: pd.isna(pd.NA)
Out[32]: True
注意
在这种基本传播规则上的一个例外是缩减(例如均值或最小值),pandas 默认跳过缺失值。更多信息请参见计算部分。
逻辑操作
对于逻辑操作,NA
遵循 三值逻辑(或Kleene 逻辑,类似于 R、SQL 和 Julia)。这种逻辑意味着只有在逻辑上需要时才传播缺失值。
例如,对于逻辑“或”操作(|
),如果操作数之一是True
,我们已经知道结果将是True
,无论另一个值是什么(因此无论缺失值是True
还是False
)。在这种情况下,NA
不会传播:
In [33]: True | False
Out[33]: True
In [34]: True | pd.NA
Out[34]: True
In [35]: pd.NA | True
Out[35]: True
另一方面,如果操作数之一是False
,结果取决于另一个操作数的值。因此,在这种情况下,NA
会传播:
In [36]: False | True
Out[36]: True
In [37]: False | False
Out[37]: False
In [38]: False | pd.NA
Out[38]: <NA>
逻辑“与”操作(&
)的行为可以使用类似的逻辑推导(现在,如果操作数之一已经是False
,那么 NA
将不会传播):
In [39]: False & True
Out[39]: False
In [40]: False & False
Out[40]: False
In [41]: False & pd.NA
Out[41]: False
In [42]: True & True
Out[42]: True
In [43]: True & False
Out[43]: False
In [44]: True & pd.NA
Out[44]: <NA>
在布尔上下文中的NA
由于 NA 的实际值是未知的,将 NA 转换为布尔值是模棱两可的。
In [45]: bool(pd.NA)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[45], line 1
----> 1 bool(pd.NA)
File missing.pyx:392, in pandas._libs.missing.NAType.__bool__()
TypeError: boolean value of NA is ambiguous
这也意味着NA
不能在被评估为布尔值的上下文中使用,例如if condition: ...
,其中condition
可能是NA
。在这种情况下,可以使用isna()
来检查NA
或避免condition
为NA
,例如在填充缺失值之前。
当在if
语句中使用Series
或DataFrame
对象时,会出现类似情况,请参阅在 pandas 中使用 if/truth 语句。
NumPy ufuncs
pandas.NA
实现了 NumPy 的__array_ufunc__
协议。大多数 ufunc 与NA
一起工作,并通常返回NA
:
In [46]: np.log(pd.NA)
Out[46]: <NA>
In [47]: np.add(pd.NA, 1)
Out[47]: <NA>
警��
目前,涉及 ndarray 和NA
的 ufunc 将返回一个填充有 NA 值的对象 dtype。
In [48]: a = np.array([1, 2, 3])
In [49]: np.greater(a, pd.NA)
Out[49]: array([<NA>, <NA>, <NA>], dtype=object)
这里的返回类型可能会在将来更改为返回不同的数组类型。
更多关于 ufunc 的信息,请参阅 DataFrame 与 NumPy 函数的互操作性。
转换
如果你有一个使用np.nan
的DataFrame
或Series
,可以在DataFrame
中使用Series.convert_dtypes()
和DataFrame.convert_dtypes()
将数据转换为使用NA
等数据类型的数据,例如Int64Dtype
或ArrowDtype
。这在从 IO 方法读取数据集并推断数据类型后特别有帮助。
在这个例子中,虽然所有列的 dtype 都已更改,但我们仅展示前 10 列的结果。
In [50]: import io
In [51]: data = io.StringIO("a,b\n,True\n2,")
In [52]: df = pd.read_csv(data)
In [53]: df.dtypes
Out[53]:
a float64
b object
dtype: object
In [54]: df_conv = df.convert_dtypes()
In [55]: df_conv
Out[55]:
a b
0 <NA> True
1 2 <NA>
In [56]: df_conv.dtypes
Out[56]:
a Int64
b boolean
dtype: object
``` ## 插入缺失数据
你可以通过简单地赋值给`Series`或`DataFrame`来插入缺失值。所使用的缺失值标记将根据 dtype 选择。
```py
In [57]: ser = pd.Series([1., 2., 3.])
In [58]: ser.loc[0] = None
In [59]: ser
Out[59]:
0 NaN
1 2.0
2 3.0
dtype: float64
In [60]: ser = pd.Series([pd.Timestamp("2021"), pd.Timestamp("2021")])
In [61]: ser.iloc[0] = np.nan
In [62]: ser
Out[62]:
0 NaT
1 2021-01-01
dtype: datetime64[ns]
In [63]: ser = pd.Series([True, False], dtype="boolean[pyarrow]")
In [64]: ser.iloc[0] = None
In [65]: ser
Out[65]:
0 <NA>
1 False
dtype: bool[pyarrow]
对于object
类型,pandas 将使用给定的值:
In [66]: s = pd.Series(["a", "b", "c"], dtype=object)
In [67]: s.loc[0] = None
In [68]: s.loc[1] = np.nan
In [69]: s
Out[69]:
0 None
1 NaN
2 c
dtype: object
``` ## 使用缺失数据进行计算
缺失值在 pandas 对象之间的算术运算中传播。
```py
In [70]: ser1 = pd.Series([np.nan, np.nan, 2, 3])
In [71]: ser2 = pd.Series([np.nan, 1, np.nan, 4])
In [72]: ser1
Out[72]:
0 NaN
1 NaN
2 2.0
3 3.0
dtype: float64
In [73]: ser2
Out[73]:
0 NaN
1 1.0
2 NaN
3 4.0
dtype: float64
In [74]: ser1 + ser2
Out[74]:
0 NaN
1 NaN
2 NaN
3 7.0
dtype: float64
在数据结构概述中讨论的描述性统计和计算方法(在此处列出 series 和 frame)都考虑了缺失数据。
在对数据求和时,NA 值或空数据将被视为零。
In [75]: pd.Series([np.nan]).sum()
Out[75]: 0.0
In [76]: pd.Series([], dtype="float64").sum()
Out[76]: 0.0
在进行乘法运算时,NA 值或空数据将被视为 1。
In [77]: pd.Series([np.nan]).prod()
Out[77]: 1.0
In [78]: pd.Series([], dtype="float64").prod()
Out[78]: 1.0
累积方法如cumsum()
和cumprod()
默认情况下忽略 NA 值,但在结果中保留它们。可以使用skipna
更改此行为。
- 累积方法如
cumsum()
和cumprod()
默认情况下忽略 NA 值,但在结果数组中保留它们。要覆盖此行为并包含 NA 值,请使用skipna=False
。
In [79]: ser = pd.Series([1, np.nan, 3, np.nan])
In [80]: ser
Out[80]:
0 1.0
1 NaN
2 3.0
3 NaN
dtype: float64
In [81]: ser.cumsum()
Out[81]:
0 1.0
1 NaN
2 4.0
3 NaN
dtype: float64
In [82]: ser.cumsum(skipna=False)
Out[82]:
0 1.0
1 NaN
2 NaN
3 NaN
dtype: float64
``` ## 删除缺失数据
`dropna()`删除具有缺失数据的行或列。
```py
In [83]: df = pd.DataFrame([[np.nan, 1, 2], [1, 2, np.nan], [1, 2, 3]])
In [84]: df
Out[84]:
0 1 2
0 NaN 1 2.0
1 1.0 2 NaN
2 1.0 2 3.0
In [85]: df.dropna()
Out[85]:
0 1 2
2 1.0 2 3.0
In [86]: df.dropna(axis=1)
Out[86]:
1
0 1
1 2
2 2
In [87]: ser = pd.Series([1, pd.NA], dtype="int64[pyarrow]")
In [88]: ser.dropna()
Out[88]:
0 1
dtype: int64[pyarrow]
填充缺失数据
按值填充
fillna()
用非 NA 数据替换 NA 值。
用标量值替换 NA
In [89]: data = {"np": [1.0, np.nan, np.nan, 2], "arrow": pd.array([1.0, pd.NA, pd.NA, 2], dtype="float64[pyarrow]")}
In [90]: df = pd.DataFrame(data)
In [91]: df
Out[91]:
np arrow
0 1.0 1.0
1 NaN <NA>
2 NaN <NA>
3 2.0 2.0
In [92]: df.fillna(0)
Out[92]:
np arrow
0 1.0 1.0
1 0.0 0.0
2 0.0 0.0
3 2.0 2.0
向前或向后填补间隙
In [93]: df.ffill()
Out[93]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 1.0 1.0
3 2.0 2.0
In [94]: df.bfill()
Out[94]:
np arrow
0 1.0 1.0
1 2.0 2.0
2 2.0 2.0
3 2.0 2.0
限制填充的 NA 值数量
In [95]: df.ffill(limit=1)
Out[95]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 NaN <NA>
3 2.0 2.0
NA 值可以用与Series
或DataFrame
对齐的索引和列之间的对应值替换。
In [96]: dff = pd.DataFrame(np.arange(30, dtype=np.float64).reshape(10, 3), columns=list("ABC"))
In [97]: dff.iloc[3:5, 0] = np.nan
In [98]: dff.iloc[4:6, 1] = np.nan
In [99]: dff.iloc[5:8, 2] = np.nan
In [100]: dff
Out[100]:
A B C
0 0.0 1.0 2.0
1 3.0 4.0 5.0
2 6.0 7.0 8.0
3 NaN 10.0 11.0
4 NaN NaN 14.0
5 15.0 NaN NaN
6 18.0 19.0 NaN
7 21.0 22.0 NaN
8 24.0 25.0 26.0
9 27.0 28.0 29.0
In [101]: dff.fillna(dff.mean())
Out[101]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
注意
DataFrame.where()
也可用于填充 NA 值。与上述结果相同。
In [102]: dff.where(pd.notna(dff), dff.mean(), axis="columns")
Out[102]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
``` ### 插值
`DataFrame.interpolate()`和`Series.interpolate()`使用各种插值方法填充 NA 值。
```py
In [103]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [104]: df
Out[104]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [105]: df.interpolate()
Out[105]:
A B
0 1.0 0.25
1 2.1 1.50
2 3.4 2.75
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [106]: idx = pd.date_range("2020-01-01", periods=10, freq="D")
In [107]: data = np.random.default_rng(2).integers(0, 10, 10).astype(np.float64)
In [108]: ts = pd.Series(data, index=idx)
In [109]: ts.iloc[[1, 2, 5, 6, 9]] = np.nan
In [110]: ts
Out[110]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 2.0
2020-01-05 4.0
2020-01-06 NaN
2020-01-07 NaN
2020-01-08 0.0
2020-01-09 3.0
2020-01-10 NaN
Freq: D, dtype: float64
In [111]: ts.plot()
Out[111]: <Axes: >
In [112]: ts.interpolate()
Out[112]:
2020-01-01 8.000000
2020-01-02 6.000000
2020-01-03 4.000000
2020-01-04 2.000000
2020-01-05 4.000000
2020-01-06 2.666667
2020-01-07 1.333333
2020-01-08 0.000000
2020-01-09 3.000000
2020-01-10 3.000000
Freq: D, dtype: float64
In [113]: ts.interpolate().plot()
Out[113]: <Axes: >
相对于Timestamp
在DatetimeIndex
上的插值可通过设置method="time"
来实现。
In [114]: ts2 = ts.iloc[[0, 1, 3, 7, 9]]
In [115]: ts2
Out[115]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 NaN
dtype: float64
In [116]: ts2.interpolate()
Out[116]:
2020-01-01 8.0
2020-01-02 5.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
In [117]: ts2.interpolate(method="time")
Out[117]:
2020-01-01 8.0
2020-01-02 6.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
对于浮点索引,请使用method='values'
:
In [118]: idx = [0.0, 1.0, 10.0]
In [119]: ser = pd.Series([0.0, np.nan, 10.0], idx)
In [120]: ser
Out[120]:
0.0 0.0
1.0 NaN
10.0 10.0
dtype: float64
In [121]: ser.interpolate()
Out[121]:
0.0 0.0
1.0 5.0
10.0 10.0
dtype: float64
In [122]: ser.interpolate(method="values")
Out[122]:
0.0 0.0
1.0 1.0
10.0 10.0
dtype: float64
如果您安装了scipy,您可以将一个 1-d 插值例程的名称传递给method
。如在 scipy 插值文档和参考指南中指定。适当的插值方法将取决于数据类型。
提示
如果您处理的是以增长速度增长的时间序列,请使用method='barycentric'
。
如果您有接近累积分布函数的值,请使用method='pchip'
。
若要填充缺失值以实现平滑绘图,请使用method='akima'
。
In [123]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [124]: df
Out[124]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [125]: df.interpolate(method="barycentric")
Out[125]:
A B
0 1.00 0.250
1 2.10 -7.660
2 3.53 -4.515
3 4.70 4.000
4 5.60 12.200
5 6.80 14.400
In [126]: df.interpolate(method="pchip")
Out[126]:
A B
0 1.00000 0.250000
1 2.10000 0.672808
2 3.43454 1.928950
3 4.70000 4.000000
4 5.60000 12.200000
5 6.80000 14.400000
In [127]: df.interpolate(method="akima")
Out[127]:
A B
0 1.000000 0.250000
1 2.100000 -0.873316
2 3.406667 0.320034
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
当通过多项式或样条逼近进行插值时,您还必须指定逼近的次数或阶数:
In [128]: df.interpolate(method="spline", order=2)
Out[128]:
A B
0 1.000000 0.250000
1 2.100000 -0.428598
2 3.404545 1.206900
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
In [129]: df.interpolate(method="polynomial", order=2)
Out[129]:
A B
0 1.000000 0.250000
1 2.100000 -2.703846
2 3.451351 -1.453846
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
比较几种方法。
In [130]: np.random.seed(2)
In [131]: ser = pd.Series(np.arange(1, 10.1, 0.25) ** 2 + np.random.randn(37))
In [132]: missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
In [133]: ser.iloc[missing] = np.nan
In [134]: methods = ["linear", "quadratic", "cubic"]
In [135]: df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})
In [136]: df.plot()
Out[136]: <Axes: >
通过Series.reindex()
从扩展数据中插值新观测。
In [137]: ser = pd.Series(np.sort(np.random.uniform(size=100)))
# interpolate at new_index
In [138]: new_index = ser.index.union(pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]))
In [139]: interp_s = ser.reindex(new_index).interpolate(method="pchip")
In [140]: interp_s.loc[49:51]
Out[140]:
49.00 0.471410
49.25 0.476841
49.50 0.481780
49.75 0.485998
50.00 0.489266
50.25 0.491814
50.50 0.493995
50.75 0.495763
51.00 0.497074
dtype: float64
插值限制
interpolate()
接受一个limit
关键字参数,以限制自上次有效观测以来填充的连续NaN
值的数量。
In [141]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13, np.nan, np.nan])
In [142]: ser
Out[142]:
0 NaN
1 NaN
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
In [143]: ser.interpolate()
Out[143]:
0 NaN
1 NaN
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
In [144]: ser.interpolate(limit=1)
Out[144]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 NaN
6 13.0
7 13.0
8 NaN
dtype: float64
默认情况下,NaN
值以forward
方向填充。使用limit_direction
参数向backward
或从both
方向填充。
In [145]: ser.interpolate(limit=1, limit_direction="backward")
Out[145]:
0 NaN
1 5.0
2 5.0
3 NaN
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
In [146]: ser.interpolate(limit=1, limit_direction="both")
Out[146]:
0 NaN
1 5.0
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 13.0
8 NaN
dtype: float64
In [147]: ser.interpolate(limit_direction="both")
Out[147]:
0 5.0
1 5.0
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
默认情况下,NaN
值会被填充,无论它们是否被现有有效值包围或在现有有效值之外。limit_area
参数限制填充到内部或外部值。
# fill one consecutive inside value in both directions
In [148]: ser.interpolate(limit_direction="both", limit_area="inside", limit=1)
Out[148]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values backward
In [149]: ser.interpolate(limit_direction="backward", limit_area="outside")
Out[149]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values in both directions
In [150]: ser.interpolate(limit_direction="both", limit_area="outside")
Out[150]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 13.0
8 13.0
dtype: float64
``` ### 替换值
`Series.replace()`和`DataFrame.replace()`可以类似于`Series.fillna()`和`DataFrame.fillna()`用于替换或插入缺失值。
```py
In [151]: df = pd.DataFrame(np.eye(3))
In [152]: df
Out[152]:
0 1 2
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
In [153]: df_missing = df.replace(0, np.nan)
In [154]: df_missing
Out[154]:
0 1 2
0 1.0 NaN NaN
1 NaN 1.0 NaN
2 NaN NaN 1.0
In [155]: df_filled = df_missing.replace(np.nan, 2)
In [156]: df_filled
Out[156]:
0 1 2
0 1.0 2.0 2.0
1 2.0 1.0 2.0
2 2.0 2.0 1.0
通过传递列表可以替换多个值。
In [157]: df_filled.replace([1, 44], [2, 28])
Out[157]:
0 1 2
0 2.0 2.0 2.0
1 2.0 2.0 2.0
2 2.0 2.0 2.0
使用映射字典进行替换。
In [158]: df_filled.replace({1: 44, 2: 28})
Out[158]:
0 1 2
0 44.0 28.0 28.0
1 28.0 44.0 28.0
2 28.0 28.0 44.0
正则表达式替换
注意
Python 字符串以 r
字符开头,例如 r'hello world'
是“原始”字符串。 它们在反斜杠方面具有与没有此前缀的字符串不同的语义。 原始字符串中的反斜杠将被解释为转义的反斜杠,例如,r'\' == '\\'
。
用 NaN
替换 ‘.’
In [159]: d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", np.nan, "d"]}
In [160]: df = pd.DataFrame(d)
In [161]: df.replace(".", np.nan)
Out[161]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
用删除周围空格的正则表达式替换 ‘.’ 为 NaN
。
In [162]: df.replace(r"\s*\.\s*", np.nan, regex=True)
Out[162]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
替换为一组正则表达式。
In [163]: df.replace([r"\.", r"(a)"], ["dot", r"\1stuff"], regex=True)
Out[163]:
a b c
0 0 astuff astuff
1 1 b b
2 2 dot NaN
3 3 dot d
用映射字典中的正则表达式替换。
In [164]: df.replace({"b": r"\s*\.\s*"}, {"b": np.nan}, regex=True)
Out[164]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
传递使用 regex
关键字的正则表达式的嵌套字典。
In [165]: df.replace({"b": {"b": r""}}, regex=True)
Out[165]:
a b c
0 0 a a
1 1 b
2 2 . NaN
3 3 . d
In [166]: df.replace(regex={"b": {r"\s*\.\s*": np.nan}})
Out[166]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
In [167]: df.replace({"b": r"\s*(\.)\s*"}, {"b": r"\1ty"}, regex=True)
Out[167]:
a b c
0 0 a a
1 1 b b
2 2 .ty NaN
3 3 .ty d
传递一个正则表达式列表,将匹配项替换为标量。
In [168]: df.replace([r"\s*\.\s*", r"a|b"], "placeholder", regex=True)
Out[168]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
所有的正则表达式示例也可以作为 to_replace
参数传递给 regex
参数。 在这种情况下,value
参数必须通过名称显式传递,或者 regex
必须是一个嵌套字典。
In [169]: df.replace(regex=[r"\s*\.\s*", r"a|b"], value="placeholder")
Out[169]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
注意
来自 re.compile
的正则表达式对象也是有效的输入。
被视为“缺失”的值
pandas 使用不同的标记值来表示缺失值(也称为 NA),具体取决于数据类型。
numpy.nan
适用于 NumPy 数据类型。 使用 NumPy 数据类型的缺点是原始数据类型将被强制转换为 np.float64
或 object
。
In [1]: pd.Series([1, 2], dtype=np.int64).reindex([0, 1, 2])
Out[1]:
0 1.0
1 2.0
2 NaN
dtype: float64
In [2]: pd.Series([True, False], dtype=np.bool_).reindex([0, 1, 2])
Out[2]:
0 True
1 False
2 NaN
dtype: object
NaT
适用于 NumPy np.datetime64
, np.timedelta64
, 和 PeriodDtype
。 对于类型应用程序,请使用 api.types.NaTType
。
In [3]: pd.Series([1, 2], dtype=np.dtype("timedelta64[ns]")).reindex([0, 1, 2])
Out[3]:
0 0 days 00:00:00.000000001
1 0 days 00:00:00.000000002
2 NaT
dtype: timedelta64[ns]
In [4]: pd.Series([1, 2], dtype=np.dtype("datetime64[ns]")).reindex([0, 1, 2])
Out[4]:
0 1970-01-01 00:00:00.000000001
1 1970-01-01 00:00:00.000000002
2 NaT
dtype: datetime64[ns]
In [5]: pd.Series(["2020", "2020"], dtype=pd.PeriodDtype("D")).reindex([0, 1, 2])
Out[5]:
0 2020-01-01
1 2020-01-01
2 NaT
dtype: period[D]
NA
适用于 StringDtype
, Int64Dtype
(和其他位宽度), Float64Dtype
(和其他位宽度), :class:BooleanDtype
和 ArrowDtype
。 这些类型将保持数据的原始数据类型。 对于类型应用程序,请使用 api.types.NAType
。
In [6]: pd.Series([1, 2], dtype="Int64").reindex([0, 1, 2])
Out[6]:
0 1
1 2
2 <NA>
dtype: Int64
In [7]: pd.Series([True, False], dtype="boolean[pyarrow]").reindex([0, 1, 2])
Out[7]:
0 True
1 False
2 <NA>
dtype: bool[pyarrow]
要检测这些缺失值,使用 isna()
或 notna()
方法。
In [8]: ser = pd.Series([pd.Timestamp("2020-01-01"), pd.NaT])
In [9]: ser
Out[9]:
0 2020-01-01
1 NaT
dtype: datetime64[ns]
In [10]: pd.isna(ser)
Out[10]:
0 False
1 True
dtype: bool
注意
isna()
或 notna()
也会将 None
视为缺失值。
In [11]: ser = pd.Series([1, None], dtype=object)
In [12]: ser
Out[12]:
0 1
1 None
dtype: object
In [13]: pd.isna(ser)
Out[13]:
0 False
1 True
dtype: bool
警告
np.nan
, NaT
, 和 NA
之间的相等比较不像 None
。
In [14]: None == None # noqa: E711
Out[14]: True
In [15]: np.nan == np.nan
Out[15]: False
In [16]: pd.NaT == pd.NaT
Out[16]: False
In [17]: pd.NA == pd.NA
Out[17]: <NA>
因此,DataFrame
或Series
与这些缺失值之间的相等比较不提供与isna()
或notna()
相同的信息。
In [18]: ser = pd.Series([True, None], dtype="boolean[pyarrow]")
In [19]: ser == pd.NA
Out[19]:
0 <NA>
1 <NA>
dtype: bool[pyarrow]
In [20]: pd.isna(ser)
Out[20]:
0 False
1 True
dtype: bool
NA
语义
警告
实验性:NA
的行为仍可能在没有警告的情况下更改。
从 pandas 1.0 开始,提供了一个实验性的NA
值(单例)来表示标量缺失值。NA
的目标是提供一个可以在各种数据类型中一致使用的“缺失”指示符(而不是根据数据类型使用np.nan
、None
或pd.NaT
)。
例如,在具有可空整数 dtype 的Series
中存在缺失值时,它将使用NA
:
In [21]: s = pd.Series([1, 2, None], dtype="Int64")
In [22]: s
Out[22]:
0 1
1 2
2 <NA>
dtype: Int64
In [23]: s[2]
Out[23]: <NA>
In [24]: s[2] is pd.NA
Out[24]: True
目前,pandas 尚未默认使用NA
这些数据类型在DataFrame
或Series
中,因此您需要明确指定 dtype。在转换部分中解释了将其转换为这些 dtype 的简单方法。
算术和比较操作中的传播
一般来说,在涉及NA
的操作中,缺失值会传播。当其中一个操作数未知时,操作的结果也是未知的。
例如,NA
在算术操作中传播,类似于np.nan
:
In [25]: pd.NA + 1
Out[25]: <NA>
In [26]: "a" * pd.NA
Out[26]: <NA>
有一些特殊情况,即使其中一个操作数是NA
,结果也是已知的。
In [27]: pd.NA ** 0
Out[27]: 1
In [28]: 1 ** pd.NA
Out[28]: 1
在相等和比较操作中,NA
也会传播。这与np.nan
的行为不同,其中与np.nan
的比较总是返回False
。
In [29]: pd.NA == 1
Out[29]: <NA>
In [30]: pd.NA == pd.NA
Out[30]: <NA>
In [31]: pd.NA < 2.5
Out[31]: <NA>
要检查一个值是否等于NA
,请使用isna()
In [32]: pd.isna(pd.NA)
Out[32]: True
注意
在这种基本传播规则上的一个例外是缩减(例如平均值或最小值),pandas 默认跳过缺失值。更多信息请参见计算部分。
逻辑操作
对于逻辑操作,NA
遵循三值逻辑的规则(或Kleene 逻辑,类似于 R、SQL 和 Julia)。这种逻辑意味着只有在逻辑上需要时才传播缺失值。
例如,对于逻辑“或”操作(|
),如果其中一个操作数为True
,我们已经知道结果将是True
,无论另一个值是什么(所以无论缺失值是True
还是False
)。在这种情况下,NA
不会传播:
In [33]: True | False
Out[33]: True
In [34]: True | pd.NA
Out[34]: True
In [35]: pd.NA | True
Out[35]: True
另一方面,如果其中一个操作数是False
,结果取决于另一个操作数的值。因此,在这种情况下,NA
会传播:
In [36]: False | True
Out[36]: True
In [37]: False | False
Out[37]: False
In [38]: False | pd.NA
Out[38]: <NA>
逻辑“与”操作(&
)的行为可以使用类似的逻辑推导(现在,如果其中一个操作数已经是False
,那么NA
将不会传播):
In [39]: False & True
Out[39]: False
In [40]: False & False
Out[40]: False
In [41]: False & pd.NA
Out[41]: False
In [42]: True & True
Out[42]: True
In [43]: True & False
Out[43]: False
In [44]: True & pd.NA
Out[44]: <NA>
在布尔上下文中的NA
由于 NA 的实际值是未知的,将 NA 转换为布尔值是模棱两可的。
In [45]: bool(pd.NA)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[45], line 1
----> 1 bool(pd.NA)
File missing.pyx:392, in pandas._libs.missing.NAType.__bool__()
TypeError: boolean value of NA is ambiguous
这也意味着NA
不能在被评估为布尔值的上下文中使用,例如if condition: ...
,其中condition
可能是NA
。在这种情况下,可以使用isna()
来检查NA
,或者可以在此之前填充缺失值来避免condition
为NA
。
当在if
语句中使用Series
或DataFrame
对象时,会出现类似情况,请参见在 pandas 中使用 if/truth 语句。
NumPy ufuncs
pandas.NA
实现了 NumPy 的__array_ufunc__
协议。大多数 ufuncs 与NA
一起工作,并通常返回NA
:
In [46]: np.log(pd.NA)
Out[46]: <NA>
In [47]: np.add(pd.NA, 1)
Out[47]: <NA>
警告
目前,涉及 ndarray 和NA
的 ufuncs 将返回一个填充有 NA 值的对象 dtype。
In [48]: a = np.array([1, 2, 3])
In [49]: np.greater(a, pd.NA)
Out[49]: array([<NA>, <NA>, <NA>], dtype=object)
此处的返回类型可能会在将来更改为返回不同的数组类型。
更多关于 ufuncs 的信息,请参见 DataFrame 与 NumPy 函数的互操作性。
转换
如果你有一个使用np.nan
的DataFrame
或Series
,可以在DataFrame
中使用Series.convert_dtypes()
和DataFrame.convert_dtypes()
将数据转换为使用NA
的数据类型,如Int64Dtype
或ArrowDtype
。这在从 IO 方法中读取数据集并推断数据类型后特别有帮助。
在这个例子中,虽然所有列的数据类型都被更改,但我们展示了前 10 列的结果。
In [50]: import io
In [51]: data = io.StringIO("a,b\n,True\n2,")
In [52]: df = pd.read_csv(data)
In [53]: df.dtypes
Out[53]:
a float64
b object
dtype: object
In [54]: df_conv = df.convert_dtypes()
In [55]: df_conv
Out[55]:
a b
0 <NA> True
1 2 <NA>
In [56]: df_conv.dtypes
Out[56]:
a Int64
b boolean
dtype: object
算术和比较操作中的传播
一般来说,在涉及NA
的操作中,缺失值会传播。当操作数中有一个未知时,操作的结果也是未知的。
例如,NA
在算术操作中会传播,类似于np.nan
:
In [25]: pd.NA + 1
Out[25]: <NA>
In [26]: "a" * pd.NA
Out[26]: <NA>
在一些特殊情况下,即使操作数之一是NA
,结果也是已知的。
In [27]: pd.NA ** 0
Out[27]: 1
In [28]: 1 ** pd.NA
Out[28]: 1
在相等性和比较操作中,NA
也会传播。这与np.nan
的行为不同,其中与np.nan
的比较总是返回False
。
In [29]: pd.NA == 1
Out[29]: <NA>
In [30]: pd.NA == pd.NA
Out[30]: <NA>
In [31]: pd.NA < 2.5
Out[31]: <NA>
要检查一个值是否等于NA
,使用isna()
In [32]: pd.isna(pd.NA)
Out[32]: True
注意
在这个基本传播规则上的一个例外是缩减(如平均值或最小值),pandas 默认跳过缺失值。更多信息请参见计算部分。
逻辑操作
对于逻辑操作,NA
遵循三值逻辑(或Kleene 逻辑,类似于 R、SQL 和 Julia)。这种逻辑意味着只在逻辑上需要时传播缺失值。
例如,在逻辑“或”操作(|
)中,如果操作数之一是True
,我们已经知道结果将是True
,无论另一个值是什么(因此无论缺失值是True
还是False
)。在这种情况下,NA
不会传播:
In [33]: True | False
Out[33]: True
In [34]: True | pd.NA
Out[34]: True
In [35]: pd.NA | True
Out[35]: True
另一方面,如果操作数中的一个是False
,则结果取决于另一个操作数的值。因此,在这种情况下NA
会传播:
In [36]: False | True
Out[36]: True
In [37]: False | False
Out[37]: False
In [38]: False | pd.NA
Out[38]: <NA>
逻辑“与”操作(&
)的行为可以使用类似的逻辑推导出来(现在,如果操作数中的一个已经是False
,那么NA
将不会传播):
In [39]: False & True
Out[39]: False
In [40]: False & False
Out[40]: False
In [41]: False & pd.NA
Out[41]: False
In [42]: True & True
Out[42]: True
In [43]: True & False
Out[43]: False
In [44]: True & pd.NA
Out[44]: <NA>
在布尔上下文中的NA
由于 NA 的实际值是未知的,将 NA 转换为布尔值是模棱两可的。
In [45]: bool(pd.NA)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[45], line 1
----> 1 bool(pd.NA)
File missing.pyx:392, in pandas._libs.missing.NAType.__bool__()
TypeError: boolean value of NA is ambiguous
这也意味着NA
不能在被评估为布尔值的上下文中使用,例如if condition: ...
,其中condition
可能是NA
。在这种情况下,可以使用isna()
来检查NA
或避免condition
为NA
,例如在填充缺失值之前。
当在if
语句中使用Series
或DataFrame
对象时,会出现类似的情况,请参见在 pandas 中使用 if/真值语句。
NumPy 通用函数
pandas.NA
实现了 NumPy 的__array_ufunc__
协议。大多数通用函数与NA
一起工作,并通常返回NA
:
In [46]: np.log(pd.NA)
Out[46]: <NA>
In [47]: np.add(pd.NA, 1)
Out[47]: <NA>
警告
目前,涉及 ndarray 和NA
的通用函数将返回一个填充有 NA 值的对象 dtype。
In [48]: a = np.array([1, 2, 3])
In [49]: np.greater(a, pd.NA)
Out[49]: array([<NA>, <NA>, <NA>], dtype=object)
这里的返回类型可能会在将来更改为返回不同的数组类型。
有关通用函数的更多信息,请参见 DataFrame 与 NumPy 函数的互操作性。
转换
如果您有一个使用np.nan
的DataFrame
或Series
,Series.convert_dtypes()
和DataFrame.convert_dtypes()
在DataFrame
中可以将数据转换为使用NA
的数据类型,例如Int64Dtype
或ArrowDtype
。这在从 IO 方法读取数据集并推断数据类型后特别有帮助。
在这个例子中,虽然所有列的数据类型都发生了变化,但我们展示了前 10 列的结果。
In [50]: import io
In [51]: data = io.StringIO("a,b\n,True\n2,")
In [52]: df = pd.read_csv(data)
In [53]: df.dtypes
Out[53]:
a float64
b object
dtype: object
In [54]: df_conv = df.convert_dtypes()
In [55]: df_conv
Out[55]:
a b
0 <NA> True
1 2 <NA>
In [56]: df_conv.dtypes
Out[56]:
a Int64
b boolean
dtype: object
``` #### 转换
如果您有一个使用`np.nan`的`DataFrame`或`Series`,则可以在`DataFrame`中使用`Series.convert_dtypes()`和`DataFrame.convert_dtypes()`将数据转换为使用`NA`等数据类型的数据类型,如`Int64Dtype`或`ArrowDtype`。这在从 IO 方法读取数据集并推断数据类型后特别有帮助。
在这个例子中,虽然所有列的数据类型都发生了变化,但我们展示了前 10 列的结果。
```py
In [50]: import io
In [51]: data = io.StringIO("a,b\n,True\n2,")
In [52]: df = pd.read_csv(data)
In [53]: df.dtypes
Out[53]:
a float64
b object
dtype: object
In [54]: df_conv = df.convert_dtypes()
In [55]: df_conv
Out[55]:
a b
0 <NA> True
1 2 <NA>
In [56]: df_conv.dtypes
Out[56]:
a Int64
b boolean
dtype: object
插入缺失数据
您可以通过简单地分配给Series
或DataFrame
来插入缺失值。所使用的缺失值标记将根据数据类型选择。
In [57]: ser = pd.Series([1., 2., 3.])
In [58]: ser.loc[0] = None
In [59]: ser
Out[59]:
0 NaN
1 2.0
2 3.0
dtype: float64
In [60]: ser = pd.Series([pd.Timestamp("2021"), pd.Timestamp("2021")])
In [61]: ser.iloc[0] = np.nan
In [62]: ser
Out[62]:
0 NaT
1 2021-01-01
dtype: datetime64[ns]
In [63]: ser = pd.Series([True, False], dtype="boolean[pyarrow]")
In [64]: ser.iloc[0] = None
In [65]: ser
Out[65]:
0 <NA>
1 False
dtype: bool[pyarrow]
对于object
类型,pandas 将使用给定的值:
In [66]: s = pd.Series(["a", "b", "c"], dtype=object)
In [67]: s.loc[0] = None
In [68]: s.loc[1] = np.nan
In [69]: s
Out[69]:
0 None
1 NaN
2 c
dtype: object
带有缺失数据的计算
缺失值会在 pandas 对象之间的算术运算中传播。
In [70]: ser1 = pd.Series([np.nan, np.nan, 2, 3])
In [71]: ser2 = pd.Series([np.nan, 1, np.nan, 4])
In [72]: ser1
Out[72]:
0 NaN
1 NaN
2 2.0
3 3.0
dtype: float64
In [73]: ser2
Out[73]:
0 NaN
1 1.0
2 NaN
3 4.0
dtype: float64
In [74]: ser1 + ser2
Out[74]:
0 NaN
1 NaN
2 NaN
3 7.0
dtype: float64
在数据结构概述中讨论的描述性统计和计算方法(并在这里和这里列出)都考虑了缺失数据。
在求和数据时,NA 值或空数据将被视为零。
In [75]: pd.Series([np.nan]).sum()
Out[75]: 0.0
In [76]: pd.Series([], dtype="float64").sum()
Out[76]: 0.0
在进行乘积运算时,NA 值或空数据将被视为 1。
In [77]: pd.Series([np.nan]).prod()
Out[77]: 1.0
In [78]: pd.Series([], dtype="float64").prod()
Out[78]: 1.0
累积方法如cumsum()
和cumprod()
默认情况下会忽略 NA 值,在结果中保留它们。这种行为可以通过skipna
进行更改。
- 累积方法如
cumsum()
和cumprod()
默认情况下会忽略 NA 值,但在结果数组中保留它们。要覆盖此行为并包含 NA 值,请使用skipna=False
。
In [79]: ser = pd.Series([1, np.nan, 3, np.nan])
In [80]: ser
Out[80]:
0 1.0
1 NaN
2 3.0
3 NaN
dtype: float64
In [81]: ser.cumsum()
Out[81]:
0 1.0
1 NaN
2 4.0
3 NaN
dtype: float64
In [82]: ser.cumsum(skipna=False)
Out[82]:
0 1.0
1 NaN
2 NaN
3 NaN
dtype: float64
删除缺失数据
dropna()
删除带有缺失数据的行或列。
In [83]: df = pd.DataFrame([[np.nan, 1, 2], [1, 2, np.nan], [1, 2, 3]])
In [84]: df
Out[84]:
0 1 2
0 NaN 1 2.0
1 1.0 2 NaN
2 1.0 2 3.0
In [85]: df.dropna()
Out[85]:
0 1 2
2 1.0 2 3.0
In [86]: df.dropna(axis=1)
Out[86]:
1
0 1
1 2
2 2
In [87]: ser = pd.Series([1, pd.NA], dtype="int64[pyarrow]")
In [88]: ser.dropna()
Out[88]:
0 1
dtype: int64[pyarrow]
填充缺失数据
按值填充
fillna()
用非 NA 数据替换 NA 值。
用标量值替换 NA 值
In [89]: data = {"np": [1.0, np.nan, np.nan, 2], "arrow": pd.array([1.0, pd.NA, pd.NA, 2], dtype="float64[pyarrow]")}
In [90]: df = pd.DataFrame(data)
In [91]: df
Out[91]:
np arrow
0 1.0 1.0
1 NaN <NA>
2 NaN <NA>
3 2.0 2.0
In [92]: df.fillna(0)
Out[92]:
np arrow
0 1.0 1.0
1 0.0 0.0
2 0.0 0.0
3 2.0 2.0
向前或向后填补间隙
In [93]: df.ffill()
Out[93]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 1.0 1.0
3 2.0 2.0
In [94]: df.bfill()
Out[94]:
np arrow
0 1.0 1.0
1 2.0 2.0
2 2.0 2.0
3 2.0 2.0
限制填充的 NA 值数量
In [95]: df.ffill(limit=1)
Out[95]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 NaN <NA>
3 2.0 2.0
NA 值可以用原始对象和填充对象之间的索引和列对齐的Series
或DataFrame
中的相应值替换。
In [96]: dff = pd.DataFrame(np.arange(30, dtype=np.float64).reshape(10, 3), columns=list("ABC"))
In [97]: dff.iloc[3:5, 0] = np.nan
In [98]: dff.iloc[4:6, 1] = np.nan
In [99]: dff.iloc[5:8, 2] = np.nan
In [100]: dff
Out[100]:
A B C
0 0.0 1.0 2.0
1 3.0 4.0 5.0
2 6.0 7.0 8.0
3 NaN 10.0 11.0
4 NaN NaN 14.0
5 15.0 NaN NaN
6 18.0 19.0 NaN
7 21.0 22.0 NaN
8 24.0 25.0 26.0
9 27.0 28.0 29.0
In [101]: dff.fillna(dff.mean())
Out[101]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
注意
DataFrame.where()
也可用于填充 NA 值。与上述结果相同。
In [102]: dff.where(pd.notna(dff), dff.mean(), axis="columns")
Out[102]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
``` ### 插值
`DataFrame.interpolate()`和`Series.interpolate()`使用各种插值方法填充 NA 值。
```py
In [103]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [104]: df
Out[104]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [105]: df.interpolate()
Out[105]:
A B
0 1.0 0.25
1 2.1 1.50
2 3.4 2.75
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [106]: idx = pd.date_range("2020-01-01", periods=10, freq="D")
In [107]: data = np.random.default_rng(2).integers(0, 10, 10).astype(np.float64)
In [108]: ts = pd.Series(data, index=idx)
In [109]: ts.iloc[[1, 2, 5, 6, 9]] = np.nan
In [110]: ts
Out[110]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 2.0
2020-01-05 4.0
2020-01-06 NaN
2020-01-07 NaN
2020-01-08 0.0
2020-01-09 3.0
2020-01-10 NaN
Freq: D, dtype: float64
In [111]: ts.plot()
Out[111]: <Axes: >
In [112]: ts.interpolate()
Out[112]:
2020-01-01 8.000000
2020-01-02 6.000000
2020-01-03 4.000000
2020-01-04 2.000000
2020-01-05 4.000000
2020-01-06 2.666667
2020-01-07 1.333333
2020-01-08 0.000000
2020-01-09 3.000000
2020-01-10 3.000000
Freq: D, dtype: float64
In [113]: ts.interpolate().plot()
Out[113]: <Axes: >
相对于DatetimeIndex
中的Timestamp
进行插值,可通过设置method="time"
来实现。
In [114]: ts2 = ts.iloc[[0, 1, 3, 7, 9]]
In [115]: ts2
Out[115]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 NaN
dtype: float64
In [116]: ts2.interpolate()
Out[116]:
2020-01-01 8.0
2020-01-02 5.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
In [117]: ts2.interpolate(method="time")
Out[117]:
2020-01-01 8.0
2020-01-02 6.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
对于浮点索引,请使用method='values'
:
In [118]: idx = [0.0, 1.0, 10.0]
In [119]: ser = pd.Series([0.0, np.nan, 10.0], idx)
In [120]: ser
Out[120]:
0.0 0.0
1.0 NaN
10.0 10.0
dtype: float64
In [121]: ser.interpolate()
Out[121]:
0.0 0.0
1.0 5.0
10.0 10.0
dtype: float64
In [122]: ser.interpolate(method="values")
Out[122]:
0.0 0.0
1.0 1.0
10.0 10.0
dtype: float64
如果您安装了scipy,您可以将一个 1-d 插值例程的名称传递给method
。如在 scipy 插值文档和参考指南中指定的。适当的插值方法将取决于数据类型。
提���
如果你处理的时间序列以递增速率增长,请使用method='barycentric'
。
如果您有近似累积分布函数的值,请使用method='pchip'
。
为了填补缺失值以实现平滑绘图的目的,请使用method='akima'
。
In [123]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [124]: df
Out[124]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [125]: df.interpolate(method="barycentric")
Out[125]:
A B
0 1.00 0.250
1 2.10 -7.660
2 3.53 -4.515
3 4.70 4.000
4 5.60 12.200
5 6.80 14.400
In [126]: df.interpolate(method="pchip")
Out[126]:
A B
0 1.00000 0.250000
1 2.10000 0.672808
2 3.43454 1.928950
3 4.70000 4.000000
4 5.60000 12.200000
5 6.80000 14.400000
In [127]: df.interpolate(method="akima")
Out[127]:
A B
0 1.000000 0.250000
1 2.100000 -0.873316
2 3.406667 0.320034
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
当通过多项式或样条近似进行插值时,您还必须指定近似的次数或阶数:
In [128]: df.interpolate(method="spline", order=2)
Out[128]:
A B
0 1.000000 0.250000
1 2.100000 -0.428598
2 3.404545 1.206900
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
In [129]: df.interpolate(method="polynomial", order=2)
Out[129]:
A B
0 1.000000 0.250000
1 2.100000 -2.703846
2 3.451351 -1.453846
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
比较几种方法。
In [130]: np.random.seed(2)
In [131]: ser = pd.Series(np.arange(1, 10.1, 0.25) ** 2 + np.random.randn(37))
In [132]: missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
In [133]: ser.iloc[missing] = np.nan
In [134]: methods = ["linear", "quadratic", "cubic"]
In [135]: df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})
In [136]: df.plot()
Out[136]: <Axes: >
通过Series.reindex()
从扩展数据中插值新观测。
In [137]: ser = pd.Series(np.sort(np.random.uniform(size=100)))
# interpolate at new_index
In [138]: new_index = ser.index.union(pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]))
In [139]: interp_s = ser.reindex(new_index).interpolate(method="pchip")
In [140]: interp_s.loc[49:51]
Out[140]:
49.00 0.471410
49.25 0.476841
49.50 0.481780
49.75 0.485998
50.00 0.489266
50.25 0.491814
50.50 0.493995
50.75 0.495763
51.00 0.497074
dtype: float64
插值限制
interpolate()
接受一个 limit
关键字参数,以限制自上次有效观察以来填充的连续 NaN
值的数量
In [141]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13, np.nan, np.nan])
In [142]: ser
Out[142]:
0 NaN
1 NaN
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
In [143]: ser.interpolate()
Out[143]:
0 NaN
1 NaN
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
In [144]: ser.interpolate(limit=1)
Out[144]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 NaN
6 13.0
7 13.0
8 NaN
dtype: float64
默认情况下,NaN
值以 forward
方向填充。使用 limit_direction
参数以 backward
或从 both
方向填充。
In [145]: ser.interpolate(limit=1, limit_direction="backward")
Out[145]:
0 NaN
1 5.0
2 5.0
3 NaN
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
In [146]: ser.interpolate(limit=1, limit_direction="both")
Out[146]:
0 NaN
1 5.0
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 13.0
8 NaN
dtype: float64
In [147]: ser.interpolate(limit_direction="both")
Out[147]:
0 5.0
1 5.0
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
默认情况下,NaN
值会被填充,无论它们是否被现有有效值包围或在现有有效值之外。limit_area
参数限制填充到值的内部或外部。
# fill one consecutive inside value in both directions
In [148]: ser.interpolate(limit_direction="both", limit_area="inside", limit=1)
Out[148]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values backward
In [149]: ser.interpolate(limit_direction="backward", limit_area="outside")
Out[149]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values in both directions
In [150]: ser.interpolate(limit_direction="both", limit_area="outside")
Out[150]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 13.0
8 13.0
dtype: float64
``` ### 替换值
`Series.replace()` 和 `DataFrame.replace()` 可以类似于 `Series.fillna()` 和 `DataFrame.fillna()` 用于替换或插入缺失值。
```py
In [151]: df = pd.DataFrame(np.eye(3))
In [152]: df
Out[152]:
0 1 2
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
In [153]: df_missing = df.replace(0, np.nan)
In [154]: df_missing
Out[154]:
0 1 2
0 1.0 NaN NaN
1 NaN 1.0 NaN
2 NaN NaN 1.0
In [155]: df_filled = df_missing.replace(np.nan, 2)
In [156]: df_filled
Out[156]:
0 1 2
0 1.0 2.0 2.0
1 2.0 1.0 2.0
2 2.0 2.0 1.0
通过传递列表可以替换多个值。
In [157]: df_filled.replace([1, 44], [2, 28])
Out[157]:
0 1 2
0 2.0 2.0 2.0
1 2.0 2.0 2.0
2 2.0 2.0 2.0
使用映射字典进行替换。
In [158]: df_filled.replace({1: 44, 2: 28})
Out[158]:
0 1 2
0 44.0 28.0 28.0
1 28.0 44.0 28.0
2 28.0 28.0 44.0
正则表达式替换
注意
使用以 r
字符为前缀的 Python 字符串,例如 r'hello world'
是“原始”字符串。它们在反斜杠方面具有不同的语义,与没有此前缀的字符串不同。原始字符串中的反斜杠将被解释为转义的反斜杠,例如,r'\' == '\\'
。
用 NaN
替换‘.’
In [159]: d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", np.nan, "d"]}
In [160]: df = pd.DataFrame(d)
In [161]: df.replace(".", np.nan)
Out[161]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
用删除周围空格的正则表达式将‘.’ 替换为 NaN
In [162]: df.replace(r"\s*\.\s*", np.nan, regex=True)
Out[162]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
用正则表达式列表替换。
In [163]: df.replace([r"\.", r"(a)"], ["dot", r"\1stuff"], regex=True)
Out[163]:
a b c
0 0 astuff astuff
1 1 b b
2 2 dot NaN
3 3 dot d
用映射字典中的正则表达式替换。
In [164]: df.replace({"b": r"\s*\.\s*"}, {"b": np.nan}, regex=True)
Out[164]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
传递使用 regex
关键字的正则表达式嵌套字典。
In [165]: df.replace({"b": {"b": r""}}, regex=True)
Out[165]:
a b c
0 0 a a
1 1 b
2 2 . NaN
3 3 . d
In [166]: df.replace(regex={"b": {r"\s*\.\s*": np.nan}})
Out[166]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
In [167]: df.replace({"b": r"\s*(\.)\s*"}, {"b": r"\1ty"}, regex=True)
Out[167]:
a b c
0 0 a a
1 1 b b
2 2 .ty NaN
3 3 .ty d
传递一组正则表达式,将匹配项替换为标量。
In [168]: df.replace([r"\s*\.\s*", r"a|b"], "placeholder", regex=True)
Out[168]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
所有正则表达式示例也可以作为 to_replace
参数传递给 regex
参数。在这种情况下,必须通过名称显式传递 value
参数或 regex
必须是一个嵌套字典。
In [169]: df.replace(regex=[r"\s*\.\s*", r"a|b"], value="placeholder")
Out[169]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
注意
也可以传递由 re.compile
创建的正则表达式对象作为有效输入。### 按值填充
fillna()
用非 NA 数据替换 NA 值。
���标量值替换 NA 值
In [89]: data = {"np": [1.0, np.nan, np.nan, 2], "arrow": pd.array([1.0, pd.NA, pd.NA, 2], dtype="float64[pyarrow]")}
In [90]: df = pd.DataFrame(data)
In [91]: df
Out[91]:
np arrow
0 1.0 1.0
1 NaN <NA>
2 NaN <NA>
3 2.0 2.0
In [92]: df.fillna(0)
Out[92]:
np arrow
0 1.0 1.0
1 0.0 0.0
2 0.0 0.0
3 2.0 2.0
向前或向后填充间隙
In [93]: df.ffill()
Out[93]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 1.0 1.0
3 2.0 2.0
In [94]: df.bfill()
Out[94]:
np arrow
0 1.0 1.0
1 2.0 2.0
2 2.0 2.0
3 2.0 2.0
限制填充的 NA 值数量
In [95]: df.ffill(limit=1)
Out[95]:
np arrow
0 1.0 1.0
1 1.0 1.0
2 NaN <NA>
3 2.0 2.0
可以用 Series
或 DataFrame
中对应值替换 NA 值,其中原始对象和填充对象之间的索引和列对齐。
In [96]: dff = pd.DataFrame(np.arange(30, dtype=np.float64).reshape(10, 3), columns=list("ABC"))
In [97]: dff.iloc[3:5, 0] = np.nan
In [98]: dff.iloc[4:6, 1] = np.nan
In [99]: dff.iloc[5:8, 2] = np.nan
In [100]: dff
Out[100]:
A B C
0 0.0 1.0 2.0
1 3.0 4.0 5.0
2 6.0 7.0 8.0
3 NaN 10.0 11.0
4 NaN NaN 14.0
5 15.0 NaN NaN
6 18.0 19.0 NaN
7 21.0 22.0 NaN
8 24.0 25.0 26.0
9 27.0 28.0 29.0
In [101]: dff.fillna(dff.mean())
Out[101]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
注意
DataFrame.where()
也可用于填充 NA 值。结果与上述相同。
In [102]: dff.where(pd.notna(dff), dff.mean(), axis="columns")
Out[102]:
A B C
0 0.00 1.0 2.000000
1 3.00 4.0 5.000000
2 6.00 7.0 8.000000
3 14.25 10.0 11.000000
4 14.25 14.5 14.000000
5 15.00 14.5 13.571429
6 18.00 19.0 13.571429
7 21.00 22.0 13.571429
8 24.00 25.0 26.000000
9 27.00 28.0 29.000000
插值
DataFrame.interpolate()
和 Series.interpolate()
使用各种插值方法填充 NA 值。
In [103]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [104]: df
Out[104]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [105]: df.interpolate()
Out[105]:
A B
0 1.0 0.25
1 2.1 1.50
2 3.4 2.75
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [106]: idx = pd.date_range("2020-01-01", periods=10, freq="D")
In [107]: data = np.random.default_rng(2).integers(0, 10, 10).astype(np.float64)
In [108]: ts = pd.Series(data, index=idx)
In [109]: ts.iloc[[1, 2, 5, 6, 9]] = np.nan
In [110]: ts
Out[110]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 2.0
2020-01-05 4.0
2020-01-06 NaN
2020-01-07 NaN
2020-01-08 0.0
2020-01-09 3.0
2020-01-10 NaN
Freq: D, dtype: float64
In [111]: ts.plot()
Out[111]: <Axes: >
In [112]: ts.interpolate()
Out[112]:
2020-01-01 8.000000
2020-01-02 6.000000
2020-01-03 4.000000
2020-01-04 2.000000
2020-01-05 4.000000
2020-01-06 2.666667
2020-01-07 1.333333
2020-01-08 0.000000
2020-01-09 3.000000
2020-01-10 3.000000
Freq: D, dtype: float64
In [113]: ts.interpolate().plot()
Out[113]: <Axes: >
相对于Timestamp
在DatetimeIndex
中的插值可通过设置 method="time"
实现。
In [114]: ts2 = ts.iloc[[0, 1, 3, 7, 9]]
In [115]: ts2
Out[115]:
2020-01-01 8.0
2020-01-02 NaN
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 NaN
dtype: float64
In [116]: ts2.interpolate()
Out[116]:
2020-01-01 8.0
2020-01-02 5.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
In [117]: ts2.interpolate(method="time")
Out[117]:
2020-01-01 8.0
2020-01-02 6.0
2020-01-04 2.0
2020-01-08 0.0
2020-01-10 0.0
dtype: float64
对于浮点索引,请使用 method='values'
:
In [118]: idx = [0.0, 1.0, 10.0]
In [119]: ser = pd.Series([0.0, np.nan, 10.0], idx)
In [120]: ser
Out[120]:
0.0 0.0
1.0 NaN
10.0 10.0
dtype: float64
In [121]: ser.interpolate()
Out[121]:
0.0 0.0
1.0 5.0
10.0 10.0
dtype: float64
In [122]: ser.interpolate(method="values")
Out[122]:
0.0 0.0
1.0 1.0
10.0 10.0
dtype: float64
如果您已安装了scipy,可以将一个一维插值例程的名称传递给 method
。如在 scipy 插值 文档 和参考 指南 中指定的。适当的插值方法将取决于数据类型。
提示
如果您处理的时间序列呈增长趋势,请使用 method='barycentric'
。
如果您拥有近似累积分布函数的值,请使用 method='pchip'
。
若要使用 method='akima'
填充缺失值以获得平滑的绘图效果。
In [123]: df = pd.DataFrame(
.....: {
.....: "A": [1, 2.1, np.nan, 4.7, 5.6, 6.8],
.....: "B": [0.25, np.nan, np.nan, 4, 12.2, 14.4],
.....: }
.....: )
.....:
In [124]: df
Out[124]:
A B
0 1.0 0.25
1 2.1 NaN
2 NaN NaN
3 4.7 4.00
4 5.6 12.20
5 6.8 14.40
In [125]: df.interpolate(method="barycentric")
Out[125]:
A B
0 1.00 0.250
1 2.10 -7.660
2 3.53 -4.515
3 4.70 4.000
4 5.60 12.200
5 6.80 14.400
In [126]: df.interpolate(method="pchip")
Out[126]:
A B
0 1.00000 0.250000
1 2.10000 0.672808
2 3.43454 1.928950
3 4.70000 4.000000
4 5.60000 12.200000
5 6.80000 14.400000
In [127]: df.interpolate(method="akima")
Out[127]:
A B
0 1.000000 0.250000
1 2.100000 -0.873316
2 3.406667 0.320034
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
当通过多项式或样条逼近进行插值时,您还必须指定逼近的次数或阶数:
In [128]: df.interpolate(method="spline", order=2)
Out[128]:
A B
0 1.000000 0.250000
1 2.100000 -0.428598
2 3.404545 1.206900
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
In [129]: df.interpolate(method="polynomial", order=2)
Out[129]:
A B
0 1.000000 0.250000
1 2.100000 -2.703846
2 3.451351 -1.453846
3 4.700000 4.000000
4 5.600000 12.200000
5 6.800000 14.400000
比较几种方法。
In [130]: np.random.seed(2)
In [131]: ser = pd.Series(np.arange(1, 10.1, 0.25) ** 2 + np.random.randn(37))
In [132]: missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
In [133]: ser.iloc[missing] = np.nan
In [134]: methods = ["linear", "quadratic", "cubic"]
In [135]: df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})
In [136]: df.plot()
Out[136]: <Axes: >
通过Series.reindex()
从扩展数据中插值新观测值。
In [137]: ser = pd.Series(np.sort(np.random.uniform(size=100)))
# interpolate at new_index
In [138]: new_index = ser.index.union(pd.Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]))
In [139]: interp_s = ser.reindex(new_index).interpolate(method="pchip")
In [140]: interp_s.loc[49:51]
Out[140]:
49.00 0.471410
49.25 0.476841
49.50 0.481780
49.75 0.485998
50.00 0.489266
50.25 0.491814
50.50 0.493995
50.75 0.495763
51.00 0.497074
dtype: float64
插值限制
interpolate()
接受 limit
关键字参数,以限制自上次有效观测以来填充的连续 NaN
值的数量。
In [141]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13, np.nan, np.nan])
In [142]: ser
Out[142]:
0 NaN
1 NaN
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
In [143]: ser.interpolate()
Out[143]:
0 NaN
1 NaN
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
In [144]: ser.interpolate(limit=1)
Out[144]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 NaN
6 13.0
7 13.0
8 NaN
dtype: float64
默认情况下,NaN
值以forward
方向填充。使用 limit_direction
参数填充backward
或从both
方向填充。
In [145]: ser.interpolate(limit=1, limit_direction="backward")
Out[145]:
0 NaN
1 5.0
2 5.0
3 NaN
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
In [146]: ser.interpolate(limit=1, limit_direction="both")
Out[146]:
0 NaN
1 5.0
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 13.0
8 NaN
dtype: float64
In [147]: ser.interpolate(limit_direction="both")
Out[147]:
0 5.0
1 5.0
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
默认情况下,无论 NaN
值是被现有有效值包围还是在现有有效值之外,都会被填充。limit_area
参数限制填充到内部或外部值。
# fill one consecutive inside value in both directions
In [148]: ser.interpolate(limit_direction="both", limit_area="inside", limit=1)
Out[148]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values backward
In [149]: ser.interpolate(limit_direction="backward", limit_area="outside")
Out[149]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values in both directions
In [150]: ser.interpolate(limit_direction="both", limit_area="outside")
Out[150]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 13.0
8 13.0
dtype: float64
``` #### 插值限制
`interpolate()`接受一个`limit`关键字参数,以限制自上次有效观察以来填充的连续`NaN`值的数量。
```py
In [141]: ser = pd.Series([np.nan, np.nan, 5, np.nan, np.nan, np.nan, 13, np.nan, np.nan])
In [142]: ser
Out[142]:
0 NaN
1 NaN
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
In [143]: ser.interpolate()
Out[143]:
0 NaN
1 NaN
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
In [144]: ser.interpolate(limit=1)
Out[144]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 NaN
6 13.0
7 13.0
8 NaN
dtype: float64
默认情况下,NaN
值向forward
方向填充。使用limit_direction
参数向backward
或从both
方向填充。
In [145]: ser.interpolate(limit=1, limit_direction="backward")
Out[145]:
0 NaN
1 5.0
2 5.0
3 NaN
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
In [146]: ser.interpolate(limit=1, limit_direction="both")
Out[146]:
0 NaN
1 5.0
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 13.0
8 NaN
dtype: float64
In [147]: ser.interpolate(limit_direction="both")
Out[147]:
0 5.0
1 5.0
2 5.0
3 7.0
4 9.0
5 11.0
6 13.0
7 13.0
8 13.0
dtype: float64
默认情况下,NaN
值会被填充,无论它们是否被现有有效值包围或在现有有效值之外。limit_area
参数限制填充到内部或外部值。
# fill one consecutive inside value in both directions
In [148]: ser.interpolate(limit_direction="both", limit_area="inside", limit=1)
Out[148]:
0 NaN
1 NaN
2 5.0
3 7.0
4 NaN
5 11.0
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values backward
In [149]: ser.interpolate(limit_direction="backward", limit_area="outside")
Out[149]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 NaN
8 NaN
dtype: float64
# fill all consecutive outside values in both directions
In [150]: ser.interpolate(limit_direction="both", limit_area="outside")
Out[150]:
0 5.0
1 5.0
2 5.0
3 NaN
4 NaN
5 NaN
6 13.0
7 13.0
8 13.0
dtype: float64
替换值
Series.replace()
和DataFrame.replace()
可以类似于Series.fillna()
和DataFrame.fillna()
用于替换或插入缺失值。
In [151]: df = pd.DataFrame(np.eye(3))
In [152]: df
Out[152]:
0 1 2
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
In [153]: df_missing = df.replace(0, np.nan)
In [154]: df_missing
Out[154]:
0 1 2
0 1.0 NaN NaN
1 NaN 1.0 NaN
2 NaN NaN 1.0
In [155]: df_filled = df_missing.replace(np.nan, 2)
In [156]: df_filled
Out[156]:
0 1 2
0 1.0 2.0 2.0
1 2.0 1.0 2.0
2 2.0 2.0 1.0
通过传递一个列表可以实现替换多个值。
In [157]: df_filled.replace([1, 44], [2, 28])
Out[157]:
0 1 2
0 2.0 2.0 2.0
1 2.0 2.0 2.0
2 2.0 2.0 2.0
使用映射字典进行替换。
In [158]: df_filled.replace({1: 44, 2: 28})
Out[158]:
0 1 2
0 44.0 28.0 28.0
1 28.0 44.0 28.0
2 28.0 28.0 44.0
正则表达式替换
注意
Python 字符串前缀为r
字符,例如r'hello world'
是“原始”字符串。它们在反斜杠方面与没有此前缀的字符串有不同的语义。原始字符串中的反斜杠将被解释为转义的反斜杠,例如,r'\' == '\\'
。
用正则表达式将‘.’替换为NaN
。
In [159]: d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", np.nan, "d"]}
In [160]: df = pd.DataFrame(d)
In [161]: df.replace(".", np.nan)
Out[161]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
使用正则表达式将‘.’替换为NaN
,并去除周围的空格。
In [162]: df.replace(r"\s*\.\s*", np.nan, regex=True)
Out[162]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
用正则表达式列表替换。
In [163]: df.replace([r"\.", r"(a)"], ["dot", r"\1stuff"], regex=True)
Out[163]:
a b c
0 0 astuff astuff
1 1 b b
2 2 dot NaN
3 3 dot d
用映射字典中的正则表达式替换。
In [164]: df.replace({"b": r"\s*\.\s*"}, {"b": np.nan}, regex=True)
Out[164]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
传递使用regex
关键字的正则表达式的嵌套字典。
In [165]: df.replace({"b": {"b": r""}}, regex=True)
Out[165]:
a b c
0 0 a a
1 1 b
2 2 . NaN
3 3 . d
In [166]: df.replace(regex={"b": {r"\s*\.\s*": np.nan}})
Out[166]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
In [167]: df.replace({"b": r"\s*(\.)\s*"}, {"b": r"\1ty"}, regex=True)
Out[167]:
a b c
0 0 a a
1 1 b b
2 2 .ty NaN
3 3 .ty d
传递一个正则表达式列表,将匹配项替换为标量。
In [168]: df.replace([r"\s*\.\s*", r"a|b"], "placeholder", regex=True)
Out[168]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
所有的正则表达式示例也可以通过to_replace
参数作为regex
参数传递。在这种情况下,必须通过名称显式传递value
参数或regex
必须是一个嵌套字典。
In [169]: df.replace(regex=[r"\s*\.\s*", r"a|b"], value="placeholder")
Out[169]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
注意
来自re.compile
的正则表达式对象也是有效的输入。#### 正则表达式替换
注意
Python 字符串前缀为r
字符,例如r'hello world'
是“原始”字符串。它们在反斜杠方面与没有此前缀的字符串有不同的语义。原始字符串中的反斜杠将被解释为转义的反斜杠,例如,r'\' == '\\'
。
用正则表达式将‘.’替换为NaN
。
In [159]: d = {"a": list(range(4)), "b": list("ab.."), "c": ["a", "b", np.nan, "d"]}
In [160]: df = pd.DataFrame(d)
In [161]: df.replace(".", np.nan)
Out[161]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
使用正则表达式将‘.’替换为NaN
,并去除周围的空格。
In [162]: df.replace(r"\s*\.\s*", np.nan, regex=True)
Out[162]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
用正则表达式��表替换。
In [163]: df.replace([r"\.", r"(a)"], ["dot", r"\1stuff"], regex=True)
Out[163]:
a b c
0 0 astuff astuff
1 1 b b
2 2 dot NaN
3 3 dot d
用映射字典中的正则表达式替换。
In [164]: df.replace({"b": r"\s*\.\s*"}, {"b": np.nan}, regex=True)
Out[164]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
传递使用regex
关键字的正则表达式的嵌套字典。
In [165]: df.replace({"b": {"b": r""}}, regex=True)
Out[165]:
a b c
0 0 a a
1 1 b
2 2 . NaN
3 3 . d
In [166]: df.replace(regex={"b": {r"\s*\.\s*": np.nan}})
Out[166]:
a b c
0 0 a a
1 1 b b
2 2 NaN NaN
3 3 NaN d
In [167]: df.replace({"b": r"\s*(\.)\s*"}, {"b": r"\1ty"}, regex=True)
Out[167]:
a b c
0 0 a a
1 1 b b
2 2 .ty NaN
3 3 .ty d
传递一个正则表达式列表,将匹配项替换为一个标量。
In [168]: df.replace([r"\s*\.\s*", r"a|b"], "placeholder", regex=True)
Out[168]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
所有的正则表达式示例也可以作为to_replace
参数传递给regex
参数。在这种情况下,value
参数必须通过名称显式传递,或者regex
必须是一个嵌套字典。
In [169]: df.replace(regex=[r"\s*\.\s*", r"a|b"], value="placeholder")
Out[169]:
a b c
0 0 placeholder placeholder
1 1 placeholder placeholder
2 2 placeholder NaN
3 3 placeholder d
注意
从re.compile
创建的正则表达式对象也是有效的输入。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-06-24 如何评价「施一公请辞清华大学副校长,全职执掌西湖大学」?你如何看待西湖大学的发展前景?