Python 数据处理(十)—— 迭代与 dt 访问器

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 对象还具有类似于 dictitems() 方法,用于迭代键值对。

要遍历 DataFrame 的行,可以使用以下方法:

  • iterrows(): 以 (索引,Series) 对的形式遍历 DataFrame 的行。这会将每行转换为 Series 对象,这会改变数据类型并影响性能。

  • itertuples(): 将 DataFrame 的行作为值的命名元组进行迭代。这比 iterrows() 的速度快很多,并且在大多数情况下,最好使用它来遍历 DataFrame 的值

警告

遍历 pandas 对象通常都很慢。在多数情况下,不需要手动在行上进行迭代,并且可以通过以下方法来避免:

  • 寻找向量化解决方案:可以使用内置方法或 NumPy 函数,索引等执行操作
  • 当您无法一次性在 DataFrame/Series 上运行的函数时,最好使用 apply() 而不是遍历值。
  • 如果必须要对值进行迭代操作,但又要追求性能,请考虑使用 cythonnumba 来编写内部循环

警告
永远不要在迭代内部修改数据内容,根据数据类型,迭代器返回一份拷贝(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 访问器适用于 periodtimedelta 类型数据

# 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

同时还提供了强大的模式匹配方法,这里模式匹配通常默认情况下使用的是正则表达式。

详细的操作细节将会在后面的章节中介绍

你可能感兴趣的:(Python 数据处理(十)—— 迭代与 dt 访问器)