8. 迭代
Pandas
对象基于类型进行迭代操作。Series
迭代时被视为数组,每次迭代生成值。
DataFrame
则遵循字典遍历方式,用对象的 key
进行迭代操作。
总之,对于基础迭代(for i in object
)会产生:
-
Series
:值 -
DataFrame
:列名
例如,对 DataFrame
进行迭代会获取到列名
In [254]: df = pd.DataFrame(
.....: {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
.....: )
.....:
In [255]: for col in df:
.....: print(col)
.....:
col1
col2
pandas
对象还具有类似于 dict
的 items()
方法,用于迭代键值对。
要遍历 DataFrame
的行,可以使用以下方法:
iterrows()
: 以(索引,Series)
对的形式遍历DataFrame
的行。这会将每行转换为Series
对象,这会改变数据类型并影响性能。itertuples()
: 将DataFrame
的行作为值的命名元组进行迭代。这比iterrows()
的速度快很多,并且在大多数情况下,最好使用它来遍历DataFrame
的值
警告
遍历
pandas
对象通常都很慢。在多数情况下,不需要手动在行上进行迭代,并且可以通过以下方法来避免:
- 寻找向量化解决方案:可以使用内置方法或
NumPy
函数,索引等执行操作- 当您无法一次性在
DataFrame/Series
上运行的函数时,最好使用apply()
而不是遍历值。- 如果必须要对值进行迭代操作,但又要追求性能,请考虑使用
cython
或numba
来编写内部循环
警告
永远不要在迭代内部修改数据内容,根据数据类型,迭代器返回一份拷贝(copy
)而不是视图(view
),并且对其进行写入将无效例如,在下面的情况下,设置该值将不起作用
In [256]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}) In [257]: for index, row in df.iterrows(): .....: row["a"] = 10 .....: In [258]: df Out[258]: a b 0 1 a 1 2 b 2 3 c
8.1 items
与字典类似,items()
遍历键值对
-
Series
:(索引, 标量值)
对 -
DataFrame
:(列名, Series)
对
例如
In [259]: 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
8.2 iterrows
iterrows()
以 Series
对象的形式遍历 DataFrame
中的行。
它返回一个迭代器,产生索引值以及对应行数据的 Series
:
In [260]: 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 [261]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"]) In [262]: df_orig.dtypes Out[262]: int int64 float float64 dtype: object In [263]: row = next(df_orig.iterrows())[1] In [264]: row Out[264]: int 1.0 float 1.5 Name: 0, dtype: float64
row
中的所有值,作为一个Series
返回,并且都被转换为了浮点数,而列x
中的保留的还是原始整数值In [265]: row["int"].dtype Out[265]: dtype('float64') In [266]: df_orig["int"].dtype Out[266]: dtype('int64')
为了保持原始类型,还是使用
itertuples()
来进行迭代。
例如,对 DataFrame
进行转置
In [267]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
In [268]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [269]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [270]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})
In [271]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
8.3 itertuples
itertuples()
方法会返回一个迭代器,为 DataFrame
中的每一行生成一个 namedtuple
。
元组的第一个元素将是行的索引值,剩余的值是行的数据值,例如
In [272]: 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
对象。它仅返回 namedtuple
中的值。
因此,itertuples()
会保留值的数据类型,并且通常会比 iterrows()
更快
注意
如果列名包含无效的
Python
标识符、重复的列名及以下划线开头的列名,都会被重命名为位置名称。如果列数较大,比如大于
255
列,则返回正则元组。
9 .dt 访问器
Series
有一个可以简单、快速地返回 datetime
属性值的访问器。
这个访问器返回的也是 Series
,具有与现有的 Series
一样的索引
In [273]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))
In [274]: s
Out[274]:
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 [275]: s.dt.hour
Out[275]:
0 9
1 9
2 9
3 9
dtype: int64
In [276]: s.dt.second
Out[276]:
0 12
1 12
2 12
3 12
dtype: int64
In [277]: s.dt.day
Out[277]:
0 1
1 2
2 3
3 4
dtype: int64
这样就可以使用更加便捷的表达式来访问数据
In [278]: s[s.dt.day == 2]
Out[278]:
1 2013-01-02 09:10:12
dtype: datetime64[ns]
也可以轻松的进行时区转换
In [279]: stz = s.dt.tz_localize("US/Eastern")
In [280]: stz
Out[280]:
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 [281]: stz.dt.tz
Out[281]:
也可以使用链式操作
In [282]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[282]:
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 [283]: s = pd.Series(pd.date_range("20130101", periods=4))
In [284]: s
Out[284]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: datetime64[ns]
In [285]: s.dt.strftime("%Y/%m/%d")
Out[285]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
# PeriodIndex
In [286]: s = pd.Series(pd.period_range("20130101", periods=4))
In [287]: s
Out[287]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [288]: s.dt.strftime("%Y/%m/%d")
Out[288]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
.dt
访问器适用于 period
和 timedelta
类型数据
# period
In [289]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))
In [290]: s
Out[290]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: period[D]
In [291]: s.dt.year
Out[291]:
0 2013
1 2013
2 2013
3 2013
dtype: int64
In [292]: s.dt.day
Out[292]:
0 1
1 2
2 3
3 4
dtype: int64
# timedelta
In [293]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))
In [294]: s
Out[294]:
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 [295]: s.dt.days
Out[295]:
0 1
1 1
2 1
3 1
dtype: int64
In [296]: s.dt.seconds
Out[296]:
0 5
1 6
2 7
3 8
dtype: int64
In [297]: s.dt.components
Out[297]:
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
注意
如果您的数据不是类似
datetime
类型的值,那么Series.dt
将引发TypeError
10 向量化字符串方法
Series
还配备了一套字符串处理方法,可以轻松地对数组的每个元素进行操作。
这些方法有一个重要的特性,能够自动排除缺失值和空值。
这些方法是通过 Series.str
属性访问的,其名称一般与内置字符串方法相匹配。例如
In [298]: s = pd.Series(
.....: ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
.....: )
.....:
In [299]: s.str.lower()
Out[299]:
0 a
1 b
2 c
3 aaba
4 baca
5
6 caba
7 dog
8 cat
dtype: string
同时还提供了强大的模式匹配方法,这里模式匹配通常默认情况下使用的是正则表达式。
详细的操作细节将会在后面的章节中介绍