Python 数据处理(九)—— 重置索引标签
7. 重置索引和更改标签
reindex()
是 pandas
里最基本的数据对齐方法,该方法执行标签对齐功能。
reindex
指的是让指定轴上的数据与给定的一组标签进行匹配,能够完成了以下几项操作:
- 将现有数据重新排序,以匹配新的标签集
- 在没有匹配到标签的数据位置插入缺失值(
NaN
)标记 - 可以指定
fill_value
来填充无标签的数据,该操作多见于时间序列数据
例如下面这个例子
In [205]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [206]: s
Out[206]:
a 1.695148
b 1.328614
c 1.234686
d -0.385845
e -1.326508
dtype: float64
In [207]: s.reindex(["e", "b", "f", "d"])
Out[207]:
e -1.326508
b 1.328614
f NaN
d -0.385845
dtype: float64
在这里,标签 f
不包含在 Series
对象中,因此在结果中显示为 NaN
。
对于 DataFrame
对象,您可以重新建立索引和列名
In [208]: df
Out[208]:
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 [209]: df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
Out[209]:
three two one
c 1.227435 1.478369 0.695246
f NaN NaN NaN
b -0.050390 1.912123 0.343054
您也可以使用 axis
参数
In [210]: df.reindex(["c", "f", "b"], axis="index")
Out[210]:
one two three
c 0.695246 1.478369 1.227435
f NaN NaN NaN
b 0.343054 1.912123 -0.050390
注意,轴标签的索引可以在对象之间共享。
因此,如果我们有一个 Series
和一个 DataFrame
,可以执行以下操作
In [211]: rs = s.reindex(df.index)
In [212]: rs
Out[212]:
a 1.695148
b 1.328614
c 1.234686
d -0.385845
dtype: float64
In [213]: rs.index is df.index
Out[213]: True
这意味着重建后的 Series
的索引与 DataFrame
的索引是同一个 Python
对象
DataFrame.reindex()
还支持 "轴样式" 调用,可以指定单个 labels
参数,并指定应用于哪个 axis
。
In [214]: df.reindex(["c", "f", "b"], axis="index")
Out[214]:
one two three
c 0.695246 1.478369 1.227435
f NaN NaN NaN
b 0.343054 1.912123 -0.050390
In [215]: df.reindex(["three", "two", "one"], axis="columns")
Out[215]:
three two one
a NaN 1.772517 1.394981
b -0.050390 1.912123 0.343054
c 1.227435 1.478369 0.695246
d -0.613172 0.279344 NaN
在后面的多级索引和高级索引方式中我们将会介绍更加简便的重置索引方式
注意:如果编写的代码对性能要求较高的话,预先对齐的数据操作会更快。例如,对两个未对齐的 DataFrame
相加后台也会先调用 reindex
,但是这种是不是的调用会拖慢运行速度。
7.1 重建索引并与另一个对象对齐
您可能有时会希望获取一个对象并为其轴重建索引,使其与另一个对象相同。
尽管其语法很简单,但略显冗长,因此 reindex_like()
方法可用于简化此操作
In [216]: df
Out[216]:
one two
a 0.509335 0.840612
b 0.086555 0.523010
c 0.588121 0.351784
d 0.121684 0.027703
In [217]: df2
Out[217]:
one two
a 0.700215 0.112092
b 0.098034 0.791992
d 0.251426 0.770680
In [218]: df.reindex_like(df2)
Out[218]:
one two
a 0.509335 0.840612
b 0.086555 0.523010
d 0.121684 0.027703
7.2 用 align 对齐对象
align()
方法是同时对齐两个对象的最快方法,它还支持 join
参数(与连接和合并相关)
-
join='outer'
: 使用两个索引的并集,默认方式 -
join='left'
: 使用左侧对象的索引 -
join='right'
: 使用右侧对象的索引 -
join='inner'
: 使用两个索引的交集
对于 Series
,它返回一个带有两个重置索引的 Series
的元组
In [219]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
In [220]: s1 = s[:4]
In [221]: s2 = s[1:]
In [222]: s1.align(s2)
Out[222]:
(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 [223]: s1.align(s2, join="inner")
Out[223]:
(b -1.692424
c -0.303893
d -1.425662
dtype: float64,
b -1.692424
c -0.303893
d -1.425662
dtype: float64)
In [224]: s1.align(s2, join="left")
Out[224]:
(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
,连接方法默认同时应用于索引和列
In [225]: df.align(df2, join="inner")
Out[225]:
( 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 [226]: df.align(df2, join="inner", axis=0)
Out[226]:
( 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 [227]: df.align(df2.iloc[0], axis=1)
Out[227]:
( 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)
7.3 重建索引时 NaN 的填充方式
reindex()
接受一个可选参数 method
,用于指定产生缺失值时的填充方法
来看一个简单的例子
In [228]: rng = pd.date_range("1/3/2000", periods=8)
In [229]: ts = pd.Series(np.random.randn(8), index=rng)
In [230]: ts2 = ts[[0, 3, 6]]
In [231]: ts
Out[231]:
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 [232]: ts2
Out[232]:
2000-01-03 0.183051
2000-01-06 2.395489
2000-01-09 0.733639
Freq: 3D, dtype: float64
In [233]: ts2.reindex(ts.index)
Out[233]:
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 [234]: ts2.reindex(ts.index, method="ffill")
Out[234]:
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 [235]: ts2.reindex(ts.index, method="bfill")
Out[235]:
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 [236]: ts2.reindex(ts.index, method="nearest")
Out[236]:
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
这些方法要求索引按递增或递减顺序排列
注意,使用 fillna
或 interpolate
可以实现相同的效果(method ='nearest'
除外)
In [237]: ts2.reindex(ts.index).fillna(method="ffill")
Out[237]:
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()
不会对索引的顺序进行任何检查
7.4 对重建索引的填充方式的限制
limit
和 tolerance
参数可以对 reindex
的填充操作进行额外的控制。
limit
限定了连续匹配的最大数量
In [238]: ts2.reindex(ts.index, method="ffill", limit=1)
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
而 tolerance
用于指定索引值之间的最大距离
In [239]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[239]:
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
类型,
因此需要你为 tolerance
参数设置恰当的字符串。
7.5 删除标签
drop()
函数经常会与 reindex
配合使用,用于删除轴上的一组标签
In [240]: df
Out[240]:
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 [241]: df.drop(["a", "d"], axis=0)
Out[241]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
In [242]: df.drop(["one"], axis=1)
Out[242]:
two three
a 1.772517 NaN
b 1.912123 -0.050390
c 1.478369 1.227435
d 0.279344 -0.613172
注意,虽然下面的方法也可以实现,但不太明显也不太干净
In [243]: df.reindex(df.index.difference(["a", "d"]))
Out[243]:
one two three
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
7.6 标签的重命名与映射
可以使用 rename()
方法,来基于某些映射(字典或 Series
)或任意函数来重新标记轴
In [244]: s
Out[244]:
a -0.186646
b -1.692424
c -0.303893
d -1.425662
e 1.114285
dtype: float64
In [245]: s.rename(str.upper)
Out[245]:
A -0.186646
B -1.692424
C -0.303893
D -1.425662
E 1.114285
dtype: float64
如果传递的是函数,则该函数必须返回一个值(并且必须生成一组唯一的值)。
此外,也可以使用 dict
或 Series
In [246]: df.rename(
.....: columns={"one": "foo", "two": "bar"},
.....: index={"a": "apple", "b": "banana", "d": "durian"},
.....: )
.....:
Out[246]:
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
如果传入的映射不包含索引和列名标签,则它不会被重命名。
注意,映射中的多出来的标签不会触发异常
也可以为 axis
指定名称,对相应的轴执行映射操作
In [247]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[247]:
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 [248]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[248]:
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()
方法还提供了一个默认为 False
的 inplace
参数,并复制一份数据。
当 inplace=True
时,会在原数据上重命名
rename()
还支持使用标量或列表的方式来更改 Series.name
属性
In [249]: s.rename("scalar-name")
Out[249]:
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 [250]: 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 [251]: df
Out[251]:
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 [252]: df.rename_axis(index={"let": "abc"})
Out[252]:
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 [253]: df.rename_axis(index=str.upper)
Out[253]:
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