重新索引以与另一个对象对齐
你可能希望取一个对象并重新索引其轴,使其标签与另一个对象相同。虽然这个操作的语法虽然冗长但简单,但它是一个常见的操作,因此reindex_like()
方法可用于简化此操作:
In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])
In [214]: df3 = df2 - df2.mean()
In [215]: df2
Out[215]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
In [216]: df3
Out[216]:
one two
a 0.583888 0.051514
b -0.468040 0.191120
c -0.115848 -0.242634
In [217]: df.reindex_like(df2)
Out[217]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
```### 使用`align`将对象与其他对象对齐
`align()` 方法是同时对齐两个对象的最快方法。它支持一个`join`参数(与连接和合并相关):
> `join='outer'`:取索引的并集(默认)
>
> `join='left'`:使用调用对象的索引
>
> `join='right'`:使用传入对象的索引
>
> `join='inner'`:交集索引
返回一个包含重新索引的两个 Series 的元组:
```py
In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [219]: s1 = s[:4]
In [220]: s2 = s[1:]
In [221]: s1.align(s2)
Out[221]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
e NaN
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64)
In [222]: s1.align(s2, join="inner")
Out[222]:
(b -1.692424
c -0.303893
d -1.425662
dtype: float64,
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
In [223]: s1.align(s2, join="left")
Out[223]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
对于 DataFrames,默认情况下,连接方法将应用于索引和列:
代码语言:javascript复制In [224]: df.align(df2, join="inner")
Out[224]:
( one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
你还可以传递一个axis
选项,只在指定的轴上对齐:
In [225]: df.align(df2, join="inner", axis=0)
Out[225]:
( one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
如果你将一个 Series 传递给DataFrame.align()
,你可以选择使用axis
参数在 DataFrame 的索引或列上同时对齐两个对象:
In [226]: df.align(df2.iloc[0], axis=1)
Out[226]:
( one three two
a 1.394981 NaN 1.772517
b 0.343054 -0.050390 1.912123
c 0.695246 1.227435 1.478369
d NaN -0.613172 0.279344,
one 1.394981
three NaN
two 1.772517
Name: a, dtype: float64)
```### 重新索引时填充
`reindex()` 接受一个可选参数`method`,该参数是从以下表中选择的填充方法:
| 方法 | 动作 |
| --- | --- |
| pad / ffill | 向前填充值 |
| bfill / backfill | 向后填充值 |
| nearest | 从最近的索引值填充 |
我们在一个简单的 Series 上演示这些填充方法:
```py
In [227]: rng = pd.date_range("1/3/2000", periods=8)
In [228]: ts = pd.Series(np.random.randn(8), index=rng)
In [229]: ts2 = ts.iloc[[0, 3, 6]]
In [230]: ts
Out[230]:
2000-01-03 0.183051
2000-01-04 0.400528
2000-01-05 -0.015083
2000-01-06 2.395489
2000-01-07 1.414806
2000-01-08 0.118428
2000-01-09 0.733639
2000-01-10 -0.936077
Freq: D, dtype: float64
In [231]: ts2
Out[231]:
2000-01-03 0.183051
2000-01-06 2.395489
2000-01-09 0.733639
Freq: 3D, dtype: float64
In [232]: ts2.reindex(ts.index)
Out[232]:
2000-01-03 0.183051
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 NaN
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]:
2000-01-03 0.183051
2000-01-04 2.395489
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 0.733639
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
这些方法要求索引是有序递增或递减的。
请注意,使用 ffill(除了method='nearest'
)或 interpolate 也可以达到相同的结果:
In [236]: ts2.reindex(ts.index).ffill()
Out[236]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
当索引不是单调递增或递减时,reindex()
会引发 ValueError。fillna()
和 interpolate()
不会对索引的顺序执行任何检查。### 重新索引时的填充限制
limit
和 tolerance
参数提供了在重新索引时填充的额外控制。Limit 指定连续匹配的最大计数:
In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
相反,容差指定索引和索引器值之间的最大距离:
代码语言:javascript复制In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
请注意,当在 DatetimeIndex
、TimedeltaIndex
或 PeriodIndex
上使用时,如果可能,tolerance
将被强制转换为 Timedelta
。这允许您使用适当的字符串指定容差。### 从轴中删除标签
与 reindex
密切相关的方法是 drop()
函数。它从轴中删除一组标签:
In [239]: df
Out[239]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [240]: df.drop(["a", "d"], axis=0)
Out[240]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
In [241]: df.drop(["one"], axis=1)
Out[241]:
two three
a 1.772517 NaN
b 1.912123 -0.050390
c 1.478369 1.227435
d 0.279344 -0.613172
请注意,以下方法也有效,但不够明显/干净:
代码语言:javascript复制In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
```### 重命名 / 映射标签
`rename()` 方法允许您根据某些映射(字典或 Series)或任意函数重新标记轴。
```py
In [243]: s
Out[243]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64
In [244]: s.rename(str.upper)
Out[244]:
A -0.186646
B -1.692424
C -0.303893
D -1.425662
E 1.114285
dtype: float64
如果传递一个函数,当使用任何标签调用时,它必须返回一个值(并且必须生成一组唯一值)。也可以使用字典或 Series:
代码语言:javascript复制In [245]: df.rename(
.....: columns={"one": "foo", "two": "bar"},
.....: index={"a": "apple", "b": "banana", "d": "durian"},
.....: )
.....:
Out[245]:
foo bar three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
如果映射不包括列/索引标签,则不会重命名。请注意,映射中的额外标签不会引发错误。
DataFrame.rename()
还支持“轴样式”调用约定,您可以指定单个 mapper
和要应用该映射的 axis
。
In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]:
foo bar three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]:
one two three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
最后,rename()
还接受标量或类似列表以修改 Series.name
属性。
In [248]: s.rename("scalar-name")
Out[248]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
Name: scalar-name, dtype: float64
DataFrame.rename_axis()
和 Series.rename_axis()
方法允许更改 MultiIndex
的特定名称(而不是标签)。
In [249]: df = pd.DataFrame(
.....: {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
.....: index=pd.MultiIndex.from_product(
.....: [["a", "b", "c"], [1, 2]], names=["let", "num"]
.....: ),
.....: )
.....:
In [250]: df
Out[250]:
x y
let num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [251]: df.rename_axis(index={"let": "abc"})
Out[251]:
x y
abc num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [252]: df.rename_axis(index=str.upper)
Out[252]:
x y
LET NUM
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
```### 重新索引以与另一个对象对齐
你可能希望取一个对象,并将其轴重新索引为与另一个对象相同的标签。虽然这个语法是简单明了的,但它是一个常见的操作,`reindex_like()` 方法可用于简化这个操作:
```py
In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])
In [214]: df3 = df2 - df2.mean()
In [215]: df2
Out[215]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
In [216]: df3
Out[216]:
one two
a 0.583888 0.051514
b -0.468040 0.191120
c -0.115848 -0.242634
In [217]: df.reindex_like(df2)
Out[217]:
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369
使用 align
将对象与其他对象对齐
align()
方法是同时对齐两个对象的最快方法。它支持一个 join
参数(与 joining and merging 相关):
join='outer'
:取索引的并集(默认)join='left'
:使用调用对象的索引join='right'
:使用传递对象的索引join='inner'
:交集索引
它返回一个包含重新索引的两个 Series 的元组:
代码语言:javascript复制In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [219]: s1 = s[:4]
In [220]: s2 = s[1:]
In [221]: s1.align(s2)
Out[221]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
e NaN
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64)
In [222]: s1.align(s2, join="inner")
Out[222]:
(b -1.692424
c -0.303893
d -1.425662
dtype: float64,
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
In [223]: s1.align(s2, join="left")
Out[223]:
(a -0.186646
b -1.692424
c -0.303893
d -1.425662
dtype: float64,
a NaN
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
对于 DataFrame,默认情况下 join 方法将应用于索引和列:
代码语言:javascript复制In [224]: df.align(df2, join="inner")
Out[224]:
( one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
你也可以传递一个 axis
选项,只在指定的轴上对齐:
In [225]: df.align(df2, join="inner", axis=0)
Out[225]:
( one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435,
one two
a 1.394981 1.772517
b 0.343054 1.912123
c 0.695246 1.478369)
如果你将一个 Series 传递给DataFrame.align()
,你可以选择使用 axis
参数来对齐 DataFrame 的索引或列:
In [226]: df.align(df2.iloc[0], axis=1)
Out[226]:
( one three two
a 1.394981 NaN 1.772517
b 0.343054 -0.050390 1.912123
c 0.695246 1.227435 1.478369
d NaN -0.613172 0.279344,
one 1.394981
three NaN
two 1.772517
Name: a, dtype: float64)
重新索引时进行填充
reindex()
接受一个可选参数 method
,这是从以下表中选择的填充方法:
方法 | 动作 |
---|---|
pad / ffill | 向前填充值 |
bfill / backfill | 向后填充值 |
nearest | 从最近的索引值填充 |
我们在一个简单的 Series 上说明这些填充方法:
代码语言:javascript复制In [227]: rng = pd.date_range("1/3/2000", periods=8)
In [228]: ts = pd.Series(np.random.randn(8), index=rng)
In [229]: ts2 = ts.iloc[[0, 3, 6]]
In [230]: ts
Out[230]:
2000-01-03 0.183051
2000-01-04 0.400528
2000-01-05 -0.015083
2000-01-06 2.395489
2000-01-07 1.414806
2000-01-08 0.118428
2000-01-09 0.733639
2000-01-10 -0.936077
Freq: D, dtype: float64
In [231]: ts2
Out[231]:
2000-01-03 0.183051
2000-01-06 2.395489
2000-01-09 0.733639
Freq: 3D, dtype: float64
In [232]: ts2.reindex(ts.index)
Out[232]:
2000-01-03 0.183051
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 NaN
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]:
2000-01-03 0.183051
2000-01-04 2.395489
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 0.733639
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 NaN
Freq: D, dtype: float64
In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 2.395489
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 0.733639
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
这些方法要求索引是有序的,递增或递减的。
注意,可以使用 ffill(除了 method='nearest'
)或 interpolate 来实现相同的结果:
In [236]: ts2.reindex(ts.index).ffill()
Out[236]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 0.183051
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 2.395489
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
当索引不是单调递增或递减时,reindex()
会引发 ValueError。fillna()
和 interpolate()
不会对索引的顺序进行任何检查。
重新索引时填充的限制
limit
和 tolerance
参数提供了在重新索引时填充的额外控制。Limit 指定连续匹配的最大计数:
In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
相反,容差指定索引和索引器值之间的最大距离:
代码语言:javascript复制In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]:
2000-01-03 0.183051
2000-01-04 0.183051
2000-01-05 NaN
2000-01-06 2.395489
2000-01-07 2.395489
2000-01-08 NaN
2000-01-09 0.733639
2000-01-10 0.733639
Freq: D, dtype: float64
请注意,当用于 DatetimeIndex
、TimedeltaIndex
或 PeriodIndex
时,如果可能的话,tolerance
会被转换为 Timedelta
。这允许您使用适当的字符串指定容差。
从轴中删除标签
与 reindex
密切相关的方法是 drop()
函数。它从轴中删除一组标签:
In [239]: df
Out[239]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [240]: df.drop(["a", "d"], axis=0)
Out[240]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
In [241]: df.drop(["one"], axis=1)
Out[241]:
two three
a 1.772517 NaN
b 1.912123 -0.050390
c 1.478369 1.227435
d 0.279344 -0.613172
注意,以下方法也有效,但不够明显/清晰:
代码语言:javascript复制In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
重命名/映射标签
rename()
方法允许您基于一些映射(dict 或 Series)或任意函数重新标记轴。
In [243]: s
Out[243]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64
In [244]: s.rename(str.upper)
Out[244]:
A -0.186646
B -1.692424
C -0.303893
D -1.425662
E 1.114285
dtype: float64
如果传递一个函数,调用任何标签时必须返回一个值(并且必须产生一组唯一值)。也可以使用 dict 或 Series:
代码语言:javascript复制In [245]: df.rename(
.....: columns={"one": "foo", "two": "bar"},
.....: index={"a": "apple", "b": "banana", "d": "durian"},
.....: )
.....:
Out[245]:
foo bar three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
如果映射不包括列/索引标签,则不会重命名。请注意,映射中的额外标签不会引发错误。
DataFrame.rename()
还支持“轴样式”调用约定,您可以指定单个 mapper
和要应用该映射的 axis
。
In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]:
foo bar three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]:
one two three
apple 1.394981 1.772517 NaN
banana 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
durian NaN 0.279344 -0.613172
最后,rename()
也接受标量或类似列表以更改 Series.name
属性。
In [248]: s.rename("scalar-name")
Out[248]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
Name: scalar-name, dtype: float64
方法 DataFrame.rename_axis()
和 Series.rename_axis()
允许更改 MultiIndex
的特定名称(而不是标签)。
In [249]: df = pd.DataFrame(
.....: {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
.....: index=pd.MultiIndex.from_product(
.....: [["a", "b", "c"], [1, 2]], names=["let", "num"]
.....: ),
.....: )
.....:
In [250]: df
Out[250]:
x y
let num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [251]: df.rename_axis(index={"let": "abc"})
Out[251]:
x y
abc num
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
In [252]: df.rename_axis(index=str.upper)
Out[252]:
x y
LET NUM
a 1 1 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 6 60
迭代
对于 pandas 对象的基本迭代行为取决于类型。当迭代 Series 时,它被视为类似数组,基本迭代会产生数值。DataFrame 遵循字典的惯例,迭代对象的“键”。
简而言之,基本迭代(for i in object
)会产生:
- Series:数值
- DataFrame:列标签
因此,例如,迭代 DataFrame 将为您提供列名:
代码语言:javascript复制In [253]: df = pd.DataFrame(
.....: {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
.....: )
.....:
In [254]: for col in df:
.....: print(col)
.....:
col1
col2
pandas 对象还具有类似字典的 items()
方法,用于迭代(键,值)对。
要迭代 DataFrame 的行,可以使用以下方法:
-
iterrows()
:将 DataFrame 的行作为(索引,Series)对进行迭代。这会将行转换为 Series 对象,这可能会改变 dtypes 并具有一些性能影响。 -
itertuples()
:将 DataFrame 的行作为命名元组的值进行迭代。这比iterrows()
快得多,并且在大多数情况下,最好使用它来迭代 DataFrame 的值。
警告
通过 pandas 对象进行迭代通常较慢。在许多情况下,不需要手动遍历行,并且可以通过以下方法之一避免:
- 寻找矢量化解决方案:许多操作可以使用内置方法或 NumPy 函数(布尔)索引等来执行,…
- 当您有一个无法一次处理完整 DataFrame/Series 的函数时,最好使用
apply()
而不是迭代值。请参阅函数应用部分的文档。 - 如果需要对值进行迭代操作但性能很重要,请考虑使用 cython 或 numba 编写内部循环。请参阅提高性能部分,了解一些此方法的示例。
警告
您永远不应该修改您正在迭代的内容。这并不保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是视图,对其进行写入将不会产生任何效果!
例如,在以下情况中设置值没有效果:
代码语言:javascript复制In [255]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})
In [256]: for index, row in df.iterrows():
.....: row["a"] = 10
.....:
In [257]: df
Out[257]:
a b
0 1 a
1 2 b
2 3 c
items
与类似字典的接口一致,items()
遍历键值对:
- Series:(索引,标量值)对
- DataFrame:(列,Series)对
例如:
代码语言:javascript复制In [258]: for label, ser in df.items():
.....: print(label)
.....: print(ser)
.....:
a
0 1
1 2
2 3
Name: a, dtype: int64
b
0 a
1 b
2 c
Name: b, dtype: object
iterrows
iterrows()
允许您将 DataFrame 的行作为 Series 对象进行迭代。它返回一个迭代器,产生每个索引值以及包含每行数据的 Series:
In [259]: for row_index, row in df.iterrows():
.....: print(row_index, row, sep="n")
.....:
0
a 1
b a
Name: 0, dtype: object
1
a 2
b b
Name: 1, dtype: object
2
a 3
b c
Name: 2, dtype: object
注意
因为iterrows()
为每行返回一个 Series,它不会在行之间保留数据类型(数据类型在 DataFrame 的列之间保留)。例如,
In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])
In [261]: df_orig.dtypes
Out[261]:
int int64
float float64
dtype: object
In [262]: row = next(df_orig.iterrows())[1]
In [263]: row
Out[263]:
int 1.0
float 1.5
Name: 0, dtype: float64
row
中的所有值,作为一个 Series 返回,现在都被转换为浮点数,包括列x
中的原始整数值:
In [264]: row["int"].dtype
Out[264]: dtype('float64')
In [265]: df_orig["int"].dtype
Out[265]: dtype('int64')
为了在迭代行时保留数据类型,最好使用itertuples()
,它返回值的命名元组,通常比iterrows()
快得多。
例如,转置 DataFrame 的一种构造方法是:
代码语言:javascript复制In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
In [267]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [268]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})
In [270]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
itertuples
itertuples()
方法将返回一个迭代器,为 DataFrame 中的每一行生成一个命名元组。元组的第一个元素将是行的相应索引值,而其余值是行值。
例如:
代码语言:javascript复制In [271]: for row in df.itertuples():
.....: print(row)
.....:
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')
此方法不会将行转换为 Series 对象;它仅返回命名元组中的值。因此,itertuples()
保留值的数据类型,并且通常比iterrows()
快得多。
注意
如果列名是无效的 Python 标识符、重复的或以下划线开头,则列名将重命名为位置名称。如果列数较多(>255),则返回常规元组。
项目
与类似字典的接口一致,items()
迭代遍历键值对:
- Series:(索引,标量值) 对
- DataFrame:(列,Series) 对
例如:
代码语言:javascript复制In [258]: for label, ser in df.items():
.....: print(label)
.....: print(ser)
.....:
a
0 1
1 2
2 3
Name: a, dtype: int64
b
0 a
1 b
2 c
Name: b, dtype: object
遍历行
iterrows()
允许您遍历 DataFrame 的行作为 Series 对象。它返回一个迭代器,每个索引值以及包含每行数据的 Series:
In [259]: for row_index, row in df.iterrows():
.....: print(row_index, row, sep="n")
.....:
0
a 1
b a
Name: 0, dtype: object
1
a 2
b b
Name: 1, dtype: object
2
a 3
b c
Name: 2, dtype: object
注意
因为iterrows()
为每行返回一个 Series,它不会在行之间保留 dtype(对于 DataFrame,dtype 在列之间保留)。例如,
In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])
In [261]: df_orig.dtypes
Out[261]:
int int64
float float64
dtype: object
In [262]: row = next(df_orig.iterrows())[1]
In [263]: row
Out[263]:
int 1.0
float 1.5
Name: 0, dtype: float64
返回为 Series 的row
中的所有值现在都被转换为浮点数,包括列x
中的原始整数值:
In [264]: row["int"].dtype
Out[264]: dtype('float64')
In [265]: df_orig["int"].dtype
Out[265]: dtype('int64')
在遍历行时保留 dtypes,最好使用itertuples()
,它返回值的命名元组,通常比iterrows()
快得多。
例如,转置 DataFrame 的一种构造方法是:
代码语言:javascript复制In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
In [267]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [268]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})
In [270]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
itertuples
itertuples()
方法将返回一个迭代器,为 DataFrame 中的每一行生成一个命名元组。元组的第一个元素将是行的相应索引值,而其余值是行值。
例如:
代码语言:javascript复制In [271]: for row in df.itertuples():
.....: print(row)
.....:
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')
此方法不会将行转换为 Series
对象;它只是返回命名元组内的值。因此,itertuples()
保留了值的数据类型,并且通常比 iterrows()
更快。
注意
如果列名无效的 Python 标识符、重复或以下划线开头,则列名将重命名为位置名称。当列数较多(>255)时,将返回常规元组。
.dt 访问器
如果 Series
是日期时间/周期类别的 Series
,则 Series
具有一种访问器,以简洁地返回 Series
的值的日期时间类属性。这将返回一个 Series
,其索引类似于现有的 Series
。
# datetime
In [272]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))
In [273]: s
Out[273]:
0 2013-01-01 09:10:12
1 2013-01-02 09:10:12
2 2013-01-03 09:10:12
3 2013-01-04 09:10:12
dtype: datetime64[ns]
In [274]: s.dt.hour
Out[274]:
0 9
1 9
2 9
3 9
dtype: int32
In [275]: s.dt.second
Out[275]:
0 12
1 12
2 12
3 12
dtype: int32
In [276]: s.dt.day
Out[276]:
0 1
1 2
2 3
3 4
dtype: int32
这使得可以像这样进行美观的表达:
代码语言:javascript复制In [277]: s[s.dt.day == 2]
Out[277]:
1 2013-01-02 09:10:12
dtype: datetime64[ns]
您可以轻松地生成带时区的转换:
代码语言:javascript复制In [278]: stz = s.dt.tz_localize("US/Eastern")
In [279]: stz
Out[279]:
0 2013-01-01 09:10:12-05:00
1 2013-01-02 09:10:12-05:00
2 2013-01-03 09:10:12-05:00
3 2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]
In [280]: stz.dt.tz
Out[280]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>
您还可以链接这些类型的操作:
代码语言:javascript复制In [281]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]:
0 2013-01-01 04:10:12-05:00
1 2013-01-02 04:10:12-05:00
2 2013-01-03 04:10:12-05:00
3 2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]
您还可以使用 Series.dt.strftime()
将日期时间值格式化为字符串,其支持与标准 strftime()
相同的格式。
# DatetimeIndex
In [282]: s = pd.Series(pd.date_range("20130101", periods=4))
In [283]: s
Out[283]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: datetime64[ns]
In [284]: s.dt.strftime("%Y/%m/%d")
Out[284]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
代码语言:javascript复制# PeriodIndex
In [285]: s = pd.Series(pd.period_range("20130101", periods=4))
In [286]: s
Out[286]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [287]: s.dt.strftime("%Y/%m/%d")
Out[287]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
.dt
访问器适用于周期和时间增量数据类型。
# period
In [288]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))
In [289]: s
Out[289]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [290]: s.dt.year
Out[290]:
0 2013
1 2013
2 2013
3 2013
dtype: int64
In [291]: s.dt.day
Out[291]:
0 1
1 2
2 3
3 4
dtype: int64
代码语言:javascript复制# timedelta
In [292]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))
In [293]: s
Out[293]:
0 1 days 00:00:05
1 1 days 00:00:06
2 1 days 00:00:07
3 1 days 00:00:08
dtype: timedelta64[ns]
In [294]: s.dt.days
Out[294]:
0 1
1 1
2 1
3 1
dtype: int64
In [295]: s.dt.seconds
Out[295]:
0 5
1 6
2 7
3 8
dtype: int32
In [296]: s.dt.components
Out[296]:
days hours minutes seconds milliseconds microseconds nanoseconds
0 1 0 0 5 0 0 0
1 1 0 0 6 0 0 0
2 1 0 0 7 0 0 0
3 1 0 0 8 0 0 0
注意
如果您使用非日期时间类似值访问 Series.dt
,将引发 TypeError
。
向量化字符串方法
Series
配备了一组字符串处理方法,使得操作数组的每个元素变得容易。最重要的是,这些方法会自动排除丢失/NA 值。通过 Series
的 str
属性访问这些方法,通常名称与等效的(标量)内置字符串方法匹配。例如:
代码语言:javascript复制
In [297]: s = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [298]: s.str.lower()
Out[298]:
0 a
1 b
2 c
3 aaba
4 baca
5 <NA>
6 caba
7 dog
8 cat
dtype: string
还提供了强大的模式匹配方法,但请注意,模式匹配通常默认使用正则表达式(有些情况下总是使用)。
注意
在 pandas 1.0 之前,字符串方法仅适用于 object
-dtype Series
。pandas 1.0 添加了 StringDtype
,专门用于字符串。有关更多信息,请参阅文本数据类型。
请参阅向量化字符串方法以获取完整的描述。
排序
pandas 支持三种排序方式:按索引标签排序、按列值排序以及按两者组合排序。
按索引
Series.sort_index()
和 DataFrame.sort_index()
方法用于按索引级别对 pandas 对象进行排序。
In [299]: df = pd.DataFrame(
.....: {
.....: "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
.....: "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
.....: "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
.....: }
.....: )
.....:
In [300]: unsorted_df = df.reindex(
.....: index=["a", "d", "c", "b"], columns=["three", "two", "one"]
.....: )
.....:
In [301]: unsorted_df
Out[301]:
three two one
a NaN -1.152244 0.562973
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]:
three two one
a NaN -1.152244 0.562973
b -0.098217 0.009797 -1.299504
c 1.273388 -0.167123 0.640382
d -0.252916 -0.109597 NaN
In [303]: unsorted_df.sort_index(ascending=False)
Out[303]:
three two one
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
a NaN -1.152244 0.562973
In [304]: unsorted_df.sort_index(axis=1)
Out[304]:
one three two
a 0.562973 NaN -1.152244
d NaN -0.252916 -0.109597
c 0.640382 1.273388 -0.167123
b -1.299504 -0.098217 0.009797
# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]:
a NaN
b -0.098217
c 1.273388
d -0.252916
Name: three, dtype: float64
按索引排序还支持一个 key
参数,该参数接受一个可调用函数,应用于正在排序的索引。对于 MultiIndex
对象,key 会按级别应用到由 level
指定的级别。
In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
.....: list("ab")
.....: )
.....:
In [307]: s1
Out[307]:
c
a b
B 1 2
a 2 3
C 3 4
代码语言:javascript复制In [308]: s1.sort_index(level="a")
Out[308]:
c
a b
B 1 2
C 3 4
a 2 3
In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]:
c
a b
a 2 3
B 1 2
C 3 4
有关按值排序的关键信息,请参见 value sorting。 ### 按值排序
Series.sort_values()
方法用于按其值对 Series
进行排序。DataFrame.sort_values()
方法用于按其列或行值对 DataFrame
进行排序。可选的 by
参数可用于指定一个或多个列以确定排序顺序。
In [310]: df1 = pd.DataFrame(
.....: {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
.....: )
.....:
In [311]: df1.sort_values(by="two")
Out[311]:
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
by
参数可以接受列名的列表,例如:
In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]:
one two three
2 1 2 3
1 1 3 4
3 1 4 2
0 2 1 5
这些方法通过 na_position
参数对 NA 值进行特殊处理:
In [313]: s[2] = np.nan
In [314]: s.sort_values()
Out[314]:
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
2 <NA>
5 <NA>
dtype: string
In [315]: s.sort_values(na_position="first")
Out[315]:
2 <NA>
5 <NA>
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
dtype: string
排序还支持一个 key
参数,该参数接受一个可调用函数,应用于正在排序的值。
In [316]: s1 = pd.Series(["B", "a", "C"])
代码语言:javascript复制In [317]: s1.sort_values()
Out[317]:
0 B
2 C
1 a
dtype: object
In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]:
1 a
0 B
2 C
dtype: object
key
将获得值的 Series
并应返回具有相同形状的转换值的 Series
或数组。对于 DataFrame
对象,key 会按列应用,因此 key 仍应期望一个 Series 并返回一个 Series,例如
In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]})
代码语言:javascript复制In [320]: df.sort_values(by="a")
Out[320]:
a b
0 B 1
2 C 3
1 a 2
In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]:
a b
1 a 2
0 B 1
2 C 3
每列的名称或类型可用于对不同列应用不同函数。 ### 按索引和值排序
作为 by
参数传递给 DataFrame.sort_values()
的字符串可以指代列或索引级别名称。
# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
.....: [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
.....: )
.....:
In [323]: idx.names = ["first", "second"]
# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)
In [325]: df_multi
Out[325]:
A
first second
a 1 6
2 5
2 4
b 2 3
1 2
1 1
按‘second’(索引)和‘A’(列)排序
代码语言:javascript复制In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]:
A
first second
b 1 1
1 2
a 1 6
b 2 3
a 2 4
2 5
注意
如果一个字符串既匹配列名又匹配索引级别名称,则会发出警告并且列优先。这将导致在将来版本中出现歧义错误。 ### searchsorted
Series 有 searchsorted()
方法,其工作方式类似于 numpy.ndarray.searchsorted()
。
In [327]: ser = pd.Series([1, 2, 3])
In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])
In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])
In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])
In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])
In [332]: ser = pd.Series([3, 1, 2])
In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2])
```### 最小/最大值
`Series` 有 `nsmallest()` 和 `nlargest()` 方法,返回最小或最大的 (n) 值。对于大型 `Series`,这比对整个 Series 进行排序并在结果上调用 `head(n)` 要快得多。
```py
In [334]: s = pd.Series(np.random.permutation(10))
In [335]: s
Out[335]:
0 2
1 0
2 3
3 7
4 1
5 5
6 9
7 6
8 8
9 4
dtype: int64
In [336]: s.sort_values()
Out[336]:
1 0
4 1
0 2
2 3
9 4
5 5
7 6
3 7
8 8
6 9
dtype: int64
In [337]: s.nsmallest(3)
Out[337]:
1 0
4 1
0 2
dtype: int64
In [338]: s.nlargest(3)
Out[338]:
6 9
8 8
3 7
dtype: int64
DataFrame
也有 nlargest
和 nsmallest
方法。
In [339]: df = pd.DataFrame(
.....: {
.....: "a": [-2, -1, 1, 10, 8, 11, -1],
.....: "b": list("abdceff"),
.....: "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
.....: }
.....: )
.....:
In [340]: df.nlargest(3, "a")
Out[340]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
In [341]: df.nlargest(5, ["a", "c"])
Out[341]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
2 1 d 4.0
6 -1 f 4.0
In [342]: df.nsmallest(3, "a")
Out[342]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
In [343]: df.nsmallest(5, ["a", "c"])
Out[343]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
2 1 d 4.0
4 8 e NaN
```### 按 MultiIndex 列排序
当列是 MultiIndex 时,必须明确指定排序,并完全指定所有级别到 `by`。
```py
In [344]: df1.columns = pd.MultiIndex.from_tuples(
.....: [("a", "one"), ("a", "two"), ("b", "three")]
.....: )
.....:
In [345]: df1.sort_values(by=("a", "two"))
Out[345]:
a b
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
```### 按索引
`Series.sort_index()` 和 `DataFrame.sort_index()` 方法用于按其索引级别对 pandas 对象进行排序。
```py
In [299]: df = pd.DataFrame(
.....: {
.....: "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
.....: "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
.....: "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
.....: }
.....: )
.....:
In [300]: unsorted_df = df.reindex(
.....: index=["a", "d", "c", "b"], columns=["three", "two", "one"]
.....: )
.....:
In [301]: unsorted_df
Out[301]:
three two one
a NaN -1.152244 0.562973
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]:
three two one
a NaN -1.152244 0.562973
b -0.098217 0.009797 -1.299504
c 1.273388 -0.167123 0.640382
d -0.252916 -0.109597 NaN
In [303]: unsorted_df.sort_index(ascending=False)
Out[303]:
three two one
d -0.252916 -0.109597 NaN
c 1.273388 -0.167123 0.640382
b -0.098217 0.009797 -1.299504
a NaN -1.152244 0.562973
In [304]: unsorted_df.sort_index(axis=1)
Out[304]:
one three two
a 0.562973 NaN -1.152244
d NaN -0.252916 -0.109597
c 0.640382 1.273388 -0.167123
b -1.299504 -0.098217 0.009797
# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]:
a NaN
b -0.098217
c 1.273388
d -0.252916
Name: three, dtype: float64
按索引排序还支持一个 key
参数,该参数接受一个可调用函数,应用于要排序的索引。对于 MultiIndex
对象,key 会按指定的 level
对每个级别应用。
In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
.....: list("ab")
.....: )
.....:
In [307]: s1
Out[307]:
c
a b
B 1 2
a 2 3
C 3 4
代码语言:javascript复制In [308]: s1.sort_index(level="a")
Out[308]:
c
a b
B 1 2
C 3 4
a 2 3
In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]:
c
a b
a 2 3
B 1 2
C 3 4
有关按值排序的键排序信息,请参阅 值排序。
按数值排序
使用 Series.sort_values()
方法可以按其数值对 Series
进行排序。使用 DataFrame.sort_values()
方法可以按其列或行数值对 DataFrame
进行排序。可选的 by
参数用于指定一个或多个列以确定排序顺序。
In [310]: df1 = pd.DataFrame(
.....: {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
.....: )
.....:
In [311]: df1.sort_values(by="two")
Out[311]:
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
by
参数可以接受列名的列表,例如:
In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]:
one two three
2 1 2 3
1 1 3 4
3 1 4 2
0 2 1 5
这些方法对 NA 值有特殊处理,通过 na_position
参数:
In [313]: s[2] = np.nan
In [314]: s.sort_values()
Out[314]:
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
2 <NA>
5 <NA>
dtype: string
In [315]: s.sort_values(na_position="first")
Out[315]:
2 <NA>
5 <NA>
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
dtype: string
排序还支持一个 key
参数,该参数接受一个可调用函数,应用于要排序的值。
In [316]: s1 = pd.Series(["B", "a", "C"])
代码语言:javascript复制In [317]: s1.sort_values()
Out[317]:
0 B
2 C
1 a
dtype: object
In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]:
1 a
0 B
2 C
dtype: object
key
将给出 Series
的值,并应返回一个形状相同的 Series
或数组,带有转换后的值。对于 DataFrame
对象,key 会按列应用,因此 key 仍应期望一个 Series,并返回一个 Series,例如:
In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]})
代码语言:javascript复制In [320]: df.sort_values(by="a")
Out[320]:
a b
0 B 1
2 C 3
1 a 2
In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]:
a b
1 a 2
0 B 1
2 C 3
每列的名称或类型可用于对不同列应用不同的函数。
通过索引和数值
作为 DataFrame.sort_values()
的 by
参数传递的字符串可以是列名,也可以是索引级别名称。
# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
.....: [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
.....: )
.....:
In [323]: idx.names = ["first", "second"]
# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)
In [325]: df_multi
Out[325]:
A
first second
a 1 6
2 5
2 4
b 2 3
1 2
1 1
按‘second’(索引)和‘A’(列)排序
代码语言:javascript复制In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]:
A
first second
b 1 1
1 2
a 1 6
b 2 3
a 2 4
2 5
注意
如果一个字符串既匹配列名又匹配索引级别名称,那么会发出警告,并且列优先。这将导致将来版本中的歧义错误。
searchsorted
Series 有 searchsorted()
方法,工作方式类似于 numpy.ndarray.searchsorted()
。
In [327]: ser = pd.Series([1, 2, 3])
In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])
In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])
In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])
In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])
In [332]: ser = pd.Series([3, 1, 2])
In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2])
最小值 / 最大值
Series
具有nsmallest()
和nlargest()
方法,返回最小或最大的(n)个值。对于大型Series
,这比对整个 Series 进行排序并在结果上调用head(n)
要快得多。
In [334]: s = pd.Series(np.random.permutation(10))
In [335]: s
Out[335]:
0 2
1 0
2 3
3 7
4 1
5 5
6 9
7 6
8 8
9 4
dtype: int64
In [336]: s.sort_values()
Out[336]:
1 0
4 1
0 2
2 3
9 4
5 5
7 6
3 7
8 8
6 9
dtype: int64
In [337]: s.nsmallest(3)
Out[337]:
1 0
4 1
0 2
dtype: int64
In [338]: s.nlargest(3)
Out[338]:
6 9
8 8
3 7
dtype: int64
DataFrame
也有nlargest
和nsmallest
方法。
In [339]: df = pd.DataFrame(
.....: {
.....: "a": [-2, -1, 1, 10, 8, 11, -1],
.....: "b": list("abdceff"),
.....: "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
.....: }
.....: )
.....:
In [340]: df.nlargest(3, "a")
Out[340]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
In [341]: df.nlargest(5, ["a", "c"])
Out[341]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
2 1 d 4.0
6 -1 f 4.0
In [342]: df.nsmallest(3, "a")
Out[342]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
In [343]: df.nsmallest(5, ["a", "c"])
Out[343]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
2 1 d 4.0
4 8 e NaN
按多级索引列排序
当列是多级索引时,必须明确排序,并完全指定所有级别到by
。
In [344]: df1.columns = pd.MultiIndex.from_tuples(
.....: [("a", "one"), ("a", "two"), ("b", "three")]
.....: )
.....:
In [345]: df1.sort_values(by=("a", "two"))
Out[345]:
a b
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
复制
pandas 对象上的copy()
方法会复制基础数据(尽管不会复制轴索引,因为它们是不可变的),并返回一个新对象。请注意很少需要复制对象。例如,只有少数几种方法可以原地修改 DataFrame:
- 插入、删除或修改列。
- 分配给
index
或columns
属性。 - 对于同质数据,可以通过
values
属性或高级索引直接修改值。
明确一点,没有任何 pandas 方法会具有修改数据的副作用;几乎每个方法都会返回一个新对象,保持原始对象不变。如果数据被修改,那是因为你明确这样做了。
数据类型
在大多数情况下,pandas 使用 NumPy 数组和数据类型来处理 Series 或 DataFrame 的单个列。NumPy 支持float
、int
、bool
、timedelta64[ns]
和datetime64[ns]
(请注意,NumPy 不支持时区感知的日期时间)。
pandas 和第三方库扩展了 NumPy 的类型系统的几个地方。本节描述了 pandas 在内部所做的扩展。请参阅扩展类型了解如何编写自己的扩展以与 pandas 一起使用。请参阅生态系统页面查看已实现扩展的第三方库列表。
以下表列出了所有 pandas 扩展类型。对于需要dtype
参数的方法,可以按照指示指定字符串。有关每种类型的更多信息,请参阅相应的文档部分。
数据类型 | 数据类型 | 标量 | 数组 | 字符串别名 |
---|---|---|---|---|
时区感知日期时间 | DatetimeTZDtype | Timestamp | arrays.DatetimeArray | 'datetime64[ns, <tz>]' |
类别 | CategoricalDtype | (none) | Categorical | 'category' |
期间(时间跨度) | PeriodDtype | Period | arrays.PeriodArray 'Period[<freq>]' | 'period[<freq>]', |
稀疏 | SparseDtype | (none) | arrays.SparseArray | 'Sparse', 'Sparse[int]', 'Sparse[float]' |
间隔 | IntervalDtype | Interval | arrays.IntervalArray | 'interval', 'Interval', 'Interval[<numpy_dtype>]', 'Interval[datetime64[ns, <tz>]]', 'Interval[timedelta64[<freq>]]' |
可空整数 | Int64Dtype, … | (none) | arrays.IntegerArray | 'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64' |
可空浮点数 | Float64Dtype, … | (none) | arrays.FloatingArray | 'Float32', 'Float64' |
字符串 | StringDtype | str | arrays.StringArray | 'string' |
布尔(带 NA) | BooleanDtype | bool | arrays.BooleanArray | 'boolean' |
pandas 有两种存储字符串的方式。
-
object
数据类型,可以保存任何 Python 对象,包括字符串。 -
StringDtype
,专门用于字符串。
通常,我们建议使用StringDtype
。更多信息请参阅文本数据类型。
最后,可以使用object
数据类型存储任意对象,但应尽可能避免(出于性能和与其他库和方法的互操作性考虑。请参阅对象转换)。
一个方便的dtypes
属性用于 DataFrame 返回一个 Series,其中包含每列的数据类型。
In [346]: dft = pd.DataFrame(
.....: {
.....: "A": np.random.rand(3),
.....: "B": 1,
.....: "C": "foo",
.....: "D": pd.Timestamp("20010102"),
.....: "E": pd.Series([1.0] * 3).astype("float32"),
.....: "F": False,
.....: "G": pd.Series([1] * 3, dtype="int8"),
.....: }
.....: )
.....:
In [347]: dft
Out[347]:
A B C D E F G
0 0.035962 1 foo 2001-01-02 1.0 False 1
1 0.701379 1 foo 2001-01-02 1.0 False 1
2 0.281885 1 foo 2001-01-02 1.0 False 1
In [348]: dft.dtypes
Out[348]:
A float64
B int64
C object
D datetime64[s]
E float32
F bool
G int8
dtype: object
在Series
对象上,使用dtype
属性。
In [349]: dft["A"].dtype
Out[349]: dtype('float64')
如果 pandas 对象包含具有多种数据类型在单个列中的数据,则将选择列的数据类型以容纳所有数据类型(object
是最通用的)。
# these ints are coerced to floats
In [350]: pd.Series([1, 2, 3, 4, 5, 6.0])
Out[350]:
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
# string data forces an ``object`` dtype
In [351]: pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]:
0 1
1 2
2 3
3 6.0
4 foo
dtype: object
可以通过调用DataFrame.dtypes.value_counts()
来查找DataFrame
中每种类型的列数。
In [352]: dft.dtypes.value_counts()
Out[352]:
float64 1
int64 1
object 1
datetime64[s] 1
float32 1
bool 1
int8 1
Name: count, dtype: int64
数值数据类型将传播并可以共存于数据框中。如果传递了数据类型(可以直接通过dtype
关键字、传递的ndarray
或传递的Series
),那么它将在数据框操作中保留。此外,不同的数值数据类型不会被合并。以下示例将让你有所了解。
In [353]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")
In [354]: df1
Out[354]:
A
0 0.224364
1 1.890546
2 0.182879
3 0.787847
4 -0.188449
5 0.667715
6 -0.011736
7 -0.399073
In [355]: df1.dtypes
Out[355]:
A float32
dtype: object
In [356]: df2 = pd.DataFrame(
.....: {
.....: "A": pd.Series(np.random.randn(8), dtype="float16"),
.....: "B": pd.Series(np.random.randn(8)),
.....: "C": pd.Series(np.random.randint(0, 255, size=8), dtype="uint8"), # [0,255] (range of uint8)
.....: }
.....: )
.....:
In [357]: df2
Out[357]:
A B C
0 0.823242 0.256090 26
1 1.607422 1.426469 86
2 -0.333740 -0.416203 46
3 -0.063477 1.139976 212
4 -1.014648 -1.193477 26
5 0.678711 0.096706 7
6 -0.040863 -1.956850 184
7 -0.357422 -0.714337 206
In [358]: df2.dtypes
Out[358]:
A float16
B float64
C uint8
dtype: object
默认值
默认情况下,整数类型为int64
,浮点类型为float64
,不受平台(32 位或 64 位)的影响。以下都将导致int64
数据类型。
In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]:
a int64
dtype: object
In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]:
a int64
dtype: object
In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]:
a int64
dtype: object
请注意,当创建数组时,Numpy 将选择依赖于平台的类型。以下将在 32 位平台上导致int32
。
In [362]: frame = pd.DataFrame(np.array([1, 2]))
向上转换
当与其他类型组合时,类型可能会向上转换,这意味着它们从当前类型(例如int
到float
)提升。
In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) df2
In [364]: df3
Out[364]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [365]: df3.dtypes
Out[365]:
A float32
B float64
C float64
dtype: object
DataFrame.to_numpy()
将返回较低的公共分母,意味着可以容纳结果同质化的 NumPy 数组中的所有类型的数据类型。这可能会导致一些向上转换。
In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64')
astype
您可以使用astype()
方法显式地将 dtype 从一种转换为另一种。即使 dtype 未更改,它们默认也会返回一个副本(传递copy=False
以更改此行为)。此外,如果 astype 操作无效,它们将引发异常。
向上转型始终遵循NumPy规则。如果操作涉及两种不同的 dtype,则将使用更通用的 dtype 作为操作的结果。
代码语言:javascript复制In [367]: df3
Out[367]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [368]: df3.dtypes
Out[368]:
A float32
B float64
C float64
dtype: object
# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]:
A float32
B float32
C float32
dtype: object
使用astype()
将一部分列转换为指定类型。
In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)
In [372]: dft
Out[372]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [373]: dft.dtypes
Out[373]:
a uint8
b uint8
c int64
dtype: object
通过将字典传递给astype()
,将某些列转换为特定的 dtype。
In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})
In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})
In [376]: dft1
Out[376]:
a b c
0 True 4 7.0
1 False 5 8.0
2 True 6 9.0
In [377]: dft1.dtypes
Out[377]:
a bool
b int64
c float64
dtype: object
注意
当尝试使用astype()
和loc()
将一部分列转换为指定类型时,会发生向上转型。
loc()
尝试适应我们分配给当前 dtype 的内容,而[]
将覆盖它们,从右侧获取 dtype。因此,以下代码片段会产生意外结果。
In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]:
a uint8
b uint8
dtype: object
In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)
In [381]: dft.dtypes
Out[381]:
a int64
b int64
c int64
dtype: object
对象转换
pandas 提供各种函数,尝试强制将类型从object
dtype 转换为其他类型。在数据已经是正确类型但存储在object
数组中的情况下,可以使用DataFrame.infer_objects()
和Series.infer_objects()
方法进行软转换为正确类型。
代码语言:javascript复制
In [382]: import datetime
In [383]: df = pd.DataFrame(
.....: [
.....: [1, 2],
.....: ["a", "b"],
.....: [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
.....: ]
.....: )
.....:
In [384]: df = df.T
In [385]: df
Out[385]:
0 1 2
0 1 a 2016-03-02 00:00:00
1 2 b 2016-03-02 00:00:00
In [386]: df.dtypes
Out[386]:
0 object
1 object
2 object
dtype: object
由于数据被转置,原始推断将所有列存储为对象,infer_objects
将进行更正。
代码语言:javascript复制
In [387]: df.infer_objects().dtypes
Out[387]:
0 int64
1 object
2 datetime64[ns]
dtype: object
下列函数可用于一维对象数组或标量,执行将对象硬转换为指定类型:
to_numeric()
(转换为数值 dtype)
In [388]: m = ["1.1", 2, 3]
In [389]: pd.to_numeric(m)
Out[389]: array([1.1, 2. , 3. ])
to_datetime()
(转换为日期时间对象)
In [390]: import datetime
In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
In [392]: pd.to_datetime(m)
Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
to_timedelta()
(转换为时间增量对象)
In [393]: m = ["5us", pd.Timedelta("1day")]
In [394]: pd.to_timedelta(m)
Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
要强制转换,我们可以传入一个errors
参数,该参数指定 pandas 如何处理无法转换为所需数据类型或对象的元素。默认情况下,errors='raise'
,意味着在转换过程中遇到的任何错误都将被引发。但是,如果errors='coerce'
,这些错误将被忽略,pandas 将把有问题的元素转换为pd.NaT
(对于日期时间和时间增量)或np.nan
(对于数值)。如果您正在读取大部分为所需数据类型(例如数值、日期时间)的数据,但偶尔混有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:
In [395]: import datetime
In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]
In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [398]: m = ["apple", 2, 3]
In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan, 2., 3.])
In [400]: m = ["apple", pd.Timedelta("1day")]
In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
除了对象转换,to_numeric()
还提供另一个参数downcast
,该参数可以将新(或已有)数值数据向下转换为较小的数据类型,从而节省内存:
In [402]: m = ["1", 2, 3]
In [403]: pd.to_numeric(m, downcast="integer") # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)
In [404]: pd.to_numeric(m, downcast="signed") # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)
In [405]: pd.to_numeric(m, downcast="unsigned") # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)
In [406]: pd.to_numeric(m, downcast="float") # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32)
由于这些方法仅适用于一维数组、列表或标量;它们不能直接用于多维对象,如 DataFrames。但是,通过apply()
,我们可以高效地对每列“应用”函数:
In [407]: import datetime
In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")
In [409]: df
Out[409]:
0 1
0 2016-07-09 2016-03-02 00:00:00
1 2016-07-09 2016-03-02 00:00:00
In [410]: df.apply(pd.to_datetime)
Out[410]:
0 1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02
In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")
In [412]: df
Out[412]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [413]: df.apply(pd.to_numeric)
Out[413]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")
In [415]: df
Out[415]:
0 1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [416]: df.apply(pd.to_timedelta)
Out[416]:
0 1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days
注意事项
对整数
类型数据执行选择操作可能会轻松将数据向上转型为浮点数
。在不引入nans
的情况下,输入数据的数据类型将被保留。另请参阅对整数 NA 的支持。
In [417]: dfi = df3.astype("int32")
In [418]: dfi["E"] = 1
In [419]: dfi
Out[419]:
A B C E
0 1 0 26 1
1 3 1 86 1
2 0 0 46 1
3 0 1 212 1
4 -1 -1 26 1
5 1 0 7 1
6 0 -1 184 1
7 0 0 206 1
In [420]: dfi.dtypes
Out[420]:
A int32
B int32
C int32
E int64
dtype: object
In [421]: casted = dfi[dfi > 0]
In [422]: casted
Out[422]:
A B C E
0 1.0 NaN 26 1
1 3.0 1.0 86 1
2 NaN NaN 46 1
3 NaN 1.0 212 1
4 NaN NaN 26 1
5 1.0 NaN 7 1
6 NaN NaN 184 1
7 NaN NaN 206 1
In [423]: casted.dtypes
Out[423]:
A float64
B float64
C int32
E int64
dtype: object
浮点数数据类型不会改变。
代码语言:javascript复制In [424]: dfa = df3.copy()
In [425]: dfa["A"] = dfa["A"].astype("float32")
In [426]: dfa.dtypes
Out[426]:
A float32
B float64
C float64
dtype: object
In [427]: casted = dfa[df2 > 0]
In [428]: casted
Out[428]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 NaN NaN 46.0
3 NaN 1.139976 212.0
4 NaN NaN 26.0
5 1.346426 0.096706 7.0
6 NaN NaN 184.0
7 NaN NaN 206.0
In [429]: casted.dtypes
Out[429]:
A float32
B float64
C float64
dtype: object
默认值
默认情况下,整数类型为int64
,浮点数类型为float64
,不受平台(32 位或 64 位)影响。以下操作将全部导致int64
数据类型。
In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]:
a int64
dtype: object
In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]:
a int64
dtype: object
In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]:
a int64
dtype: object
请注意,当创建数组时,Numpy 将选择平台相关类型。以下操作在 32 位平台上将导致int32
。
In [362]: frame = pd.DataFrame(np.array([1, 2]))
向上转型
当与其他类型组合时,类型可能会向上转型,这意味着它们从当前类型(例如int
)提升为float
。
In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) df2
In [364]: df3
Out[364]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [365]: df3.dtypes
Out[365]:
A float32
B float64
C float64
dtype: object
DataFrame.to_numpy()
将返回数据类型的最低公共分母,即可以容纳结果中所有类型的同类数据类型 NumPy 数组。这可能会强制进行一些向上转型。
In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64')
astype
您可以使用astype()
方法将数据类型明确转换为另一种数据类型。即使数据类型未更改,这些方法默认也会返回一个副本(如果要更改此行为,请传递copy=False
)。此外,如果 astype 操作无效,它们将引发异常。
向上转型始终遵循NumPy规则。如果操作涉及两种不同的数据类型,则将使用更通用的数据类型作为操作的结果。
代码语言:javascript复制In [367]: df3
Out[367]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 -0.150862 -0.416203 46.0
3 0.724370 1.139976 212.0
4 -1.203098 -1.193477 26.0
5 1.346426 0.096706 7.0
6 -0.052599 -1.956850 184.0
7 -0.756495 -0.714337 206.0
In [368]: df3.dtypes
Out[368]:
A float32
B float64
C float64
dtype: object
# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]:
A float32
B float32
C float32
dtype: object
使用astype()
将一部分列转换为指定类型。
In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)
In [372]: dft
Out[372]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [373]: dft.dtypes
Out[373]:
a uint8
b uint8
c int64
dtype: object
通过将字典传递给astype()
将某些列转换为特定数据类型。
In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})
In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})
In [376]: dft1
Out[376]:
a b c
0 True 4 7.0
1 False 5 8.0
2 True 6 9.0
In [377]: dft1.dtypes
Out[377]:
a bool
b int64
c float64
dtype: object
注意
当尝试使用astype()
和loc()
将一部分列转换为指定类型时,会发生向上转换。
loc()
尝试适应我们正在分配的当前数据类型,而[]
将覆盖它们,从右侧获取数据类型。因此,以下代码片段会产生意外结果。
In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]:
a uint8
b uint8
dtype: object
In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)
In [381]: dft.dtypes
Out[381]:
a int64
b int64
c int64
dtype: object
对象转换
pandas 提供各种函数来尝试强制将object
数据类型转换为其他类型。在数据已经是正确类型但存储在object
数组中的情况下,可以使用DataFrame.infer_objects()
和Series.infer_objects()
方法进行软转换为正确类型。
代码语言:javascript复制
In [382]: import datetime
In [383]: df = pd.DataFrame(
.....: [
.....: [1, 2],
.....: ["a", "b"],
.....: [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
.....: ]
.....: )
.....:
In [384]: df = df.T
In [385]: df
Out[385]:
0 1 2
0 1 a 2016-03-02 00:00:00
1 2 b 2016-03-02 00:00:00
In [386]: df.dtypes
Out[386]:
0 object
1 object
2 object
dtype: object
由于数据被转置,原始推断将所有列存储为对象,infer_objects
���进行更正。
代码语言:javascript复制
In [387]: df.infer_objects().dtypes
Out[387]:
0 int64
1 object
2 datetime64[ns]
dtype: object
以下函数可用于一维对象数组或标量,以执行将对象硬转换为指定类型:
to_numeric()
(转换为数值数据类型)
In [388]: m = ["1.1", 2, 3]
In [389]: pd.to_numeric(m)
Out[389]: array([1.1, 2. , 3. ])
to_datetime()
(转换为日期时间对象)
In [390]: import datetime
In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
In [392]: pd.to_datetime(m)
Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
to_timedelta()
(转换为时间间隔对象)
In [393]: m = ["5us", pd.Timedelta("1day")]
In [394]: pd.to_timedelta(m)
Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
要强制转换,我们可以传入一个errors
参数,指定 pandas 如何处理无法转换为所需数据类型或对象的元素。默认情况下,errors='raise'
,意味着在转换过程中遇到任何错误都将被引发。但是,如果errors='coerce'
,这些错误将被忽略,pandas 将把有问题的元素转换为pd.NaT
(对于日期时间和时间间隔)或np.nan
(对于数值)。如果您正在读取大部分为所需数据类型(例如数值、日期时间)的数据,但偶尔混合有不符合规范的元素,您希望将其表示为缺失值,则这可能很有用:
In [395]: import datetime
In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]
In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [398]: m = ["apple", 2, 3]
In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan, 2., 3.])
In [400]: m = ["apple", pd.Timedelta("1day")]
In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
除了对象转换外,to_numeric()
还提供另一个参数downcast
,该参数可以选择将新的(或已有的)数值数据向下转换为较小的数据类型,从而节省内存:
In [402]: m = ["1", 2, 3]
In [403]: pd.to_numeric(m, downcast="integer") # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)
In [404]: pd.to_numeric(m, downcast="signed") # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)
In [405]: pd.to_numeric(m, downcast="unsigned") # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)
In [406]: pd.to_numeric(m, downcast="float") # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32)
由于这些方法仅适用于一维数组、列表或标量;不能直接用于诸如数据框之类的多维对象。但是,通过apply()
,我们可以高效地“应用”函数到每一列:
In [407]: import datetime
In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")
In [409]: df
Out[409]:
0 1
0 2016-07-09 2016-03-02 00:00:00
1 2016-07-09 2016-03-02 00:00:00
In [410]: df.apply(pd.to_datetime)
Out[410]:
0 1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02
In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")
In [412]: df
Out[412]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [413]: df.apply(pd.to_numeric)
Out[413]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")
In [415]: df
Out[415]:
0 1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [416]: df.apply(pd.to_timedelta)
Out[416]:
0 1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days
注意事项
对integer
类型数据执行选择操作可以轻松地将数据向上转换为floating
。在不引入nans
的情况下,输入数据的数据类型将被保留。另请参阅对整数 NA 的支持。
In [417]: dfi = df3.astype("int32")
In [418]: dfi["E"] = 1
In [419]: dfi
Out[419]:
A B C E
0 1 0 26 1
1 3 1 86 1
2 0 0 46 1
3 0 1 212 1
4 -1 -1 26 1
5 1 0 7 1
6 0 -1 184 1
7 0 0 206 1
In [420]: dfi.dtypes
Out[420]:
A int32
B int32
C int32
E int64
dtype: object
In [421]: casted = dfi[dfi > 0]
In [422]: casted
Out[422]:
A B C E
0 1.0 NaN 26 1
1 3.0 1.0 86 1
2 NaN NaN 46 1
3 NaN 1.0 212 1
4 NaN NaN 26 1
5 1.0 NaN 7 1
6 NaN NaN 184 1
7 NaN NaN 206 1
In [423]: casted.dtypes
Out[423]:
A float64
B float64
C int32
E int64
dtype: object
浮点数数据类型保持不变。
代码语言:javascript复制In [424]: dfa = df3.copy()
In [425]: dfa["A"] = dfa["A"].astype("float32")
In [426]: dfa.dtypes
Out[426]:
A float32
B float64
C float64
dtype: object
In [427]: casted = dfa[df2 > 0]
In [428]: casted
Out[428]:
A B C
0 1.047606 0.256090 26.0
1 3.497968 1.426469 86.0
2 NaN NaN 46.0
3 NaN 1.139976 212.0
4 NaN NaN 26.0
5 1.346426 0.096706 7.0
6 NaN NaN 184.0
7 NaN NaN 206.0
In [429]: casted.dtypes
Out[429]:
A float32
B float64
C float64
dtype: object
基于dtype
选择列
select_dtypes()
方法实现了基于dtype
的列子集。
首先,让我们创建一个具有各种不同数据类型的DataFrame
:
In [430]: df = pd.DataFrame(
.....: {
.....: "string": list("abc"),
.....: "int64": list(range(1, 4)),
.....: "uint8": np.arange(3, 6).astype("u1"),
.....: "float64": np.arange(4.0, 7.0),
.....: "bool1": [True, False, True],
.....: "bool2": [False, True, False],
.....: "dates": pd.date_range("now", periods=3),
.....: "category": pd.Series(list("ABC")).astype("category"),
.....: }
.....: )
.....:
In [431]: df["tdeltas"] = df.dates.diff()
In [432]: df["uint64"] = np.arange(3, 6).astype("u8")
In [433]: df["other_dates"] = pd.date_range("20130101", periods=3)
In [434]: df["tz_aware_dates"] = pd.date_range("20130101", periods=3, tz="US/Eastern")
In [435]: df
Out[435]:
string int64 uint8 ... uint64 other_dates tz_aware_dates
0 a 1 3 ... 3 2013-01-01 2013-01-01 00:00:00-05:00
1 b 2 4 ... 4 2013-01-02 2013-01-02 00:00:00-05:00
2 c 3 5 ... 5 2013-01-03 2013-01-03 00:00:00-05:00
[3 rows x 12 columns]
并且数据类型:
代码语言:javascript复制In [436]: df.dtypes
Out[436]:
string object
int64 int64
uint8 uint8
float64 float64
bool1 bool
bool2 bool
dates datetime64[ns]
category category
tdeltas timedelta64[ns]
uint64 uint64
other_dates datetime64[ns]
tz_aware_dates datetime64[ns, US/Eastern]
dtype: object
select_dtypes()
有两个参数include
和exclude
,允许你说“给我这些数据类型的列”(include
)和/或“给我不包含这些数据类型的列”(exclude
)。
例如,要选择bool
列:
In [437]: df.select_dtypes(include=[bool])
Out[437]:
bool1 bool2
0 True False
1 False True
2 True False
你也可以在NumPy 数据类型层次结构中传递数据类型的名称:
代码语言:javascript复制In [438]: df.select_dtypes(include=["bool"])
Out[438]:
bool1 bool2
0 True False
1 False True
2 True False
select_dtypes()
也适用于通用数据类型。
例如,要选择所有数值和布尔列,同时排除无符号整数:
代码语言:javascript复制In [439]: df.select_dtypes(include=["number", "bool"], exclude=["unsignedinteger"])
Out[439]:
int64 float64 bool1 bool2 tdeltas
0 1 4.0 True False NaT
1 2 5.0 False True 1 days
2 3 6.0 True False 1 days
要选择字符串列,必须使用object
数据类型:
In [440]: df.select_dtypes(include=["object"])
Out[440]:
string
0 a
1 b
2 c
要查看numpy.number
等通用dtype
的所有子数据类型,可以定义一个返回子数据类型树的函数:
In [441]: def subdtypes(dtype):
.....: subs = dtype.__subclasses__()
.....: if not subs:
.....: return dtype
.....: return [dtype, [subdtypes(dt) for dt in subs]]
.....:
所有 NumPy 数据类型都是numpy.generic
的子类:
In [442]: subdtypes(np.generic)
Out[442]:
[numpy.generic,
[[numpy.number,
[[numpy.integer,
[[numpy.signedinteger,
[numpy.int8,
numpy.int16,
numpy.int32,
numpy.int64,
numpy.longlong,
numpy.timedelta64]],
[numpy.unsignedinteger,
[numpy.uint8,
numpy.uint16,
numpy.uint32,
numpy.uint64,
numpy.ulonglong]]]],
[numpy.inexact,
[[numpy.floating,
[numpy.float16, numpy.float32, numpy.float64, numpy.longdouble]],
[numpy.complexfloating,
[numpy.complex64, numpy.complex128, numpy.clongdouble]]]]]],
[numpy.flexible,
[[numpy.character, [numpy.bytes_, numpy.str_]],
[numpy.void, [numpy.record]]]],
numpy.bool_,
numpy.datetime64,
numpy.object_]]
注意
pandas 还定义了category
和datetime64[ns, tz]
类型,它们没有集成到正常的 NumPy 层次结构中,并且不会显示在上述函数中。