在Python中,我们可以使用map()函数对list对象中的每一个元素进行循环迭代操作,例如:

In [1]: a = [i for i in range(10)]

In [2]: a
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: list(map(lambda x: x**2 ,a))  # 对list对象a中的每一个元素都进行计算平方值。map() 函数生成的是一个map对象,需要使用list()函数对其强制转换为list对象才可以。
Out[3]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

那么在Pandas操作中,有没有类似的功能可以实现对矩阵或者向量进行操作呢?

当时是有的,这篇笔记来汇总下自己了解的几种方法。

apply()

在Pandas中,无论是矩阵(DataFrame)或者是向量(Series)对象都是有apply()方法的。对DataFrame对象使用该方法的话就是对矩阵中的每一行或者每一列进行遍历操作(通过axis参数来确定是行遍历还是列遍历);对Series对象使用该方法的话,就是对Series中的每一个元素进行循环遍历操作。

对DataFrame对象使用apply()方法:

In [4]: import pandas as pd

In [5]: df = pd.DataFrame({'a': [10, 20, 30], 'b': [20, 30, 40]})    

In [6]: df
Out[6]: 
    a   b
0  10  20
1  20  30
2  30  40

In [7]: df.apply(sum)  # 对df中每一列的Series使用sum函数
Out[7]: 
a    60
b    90
dtype: int64

In [10]: df.apply(lambda s: s.min(), axis=1)  # 对df中的每一行Series使用.min()方法,axis=1设置对df中的行进行操作
Out[10]: 
0    10
1    20
2    30
dtype: int64

对Series对象使用apply()方法:

In [12]: type(df['a'])
Out[12]: pandas.core.series.Series

In [14]: df['a'].apply(lambda x: x * 20)
Out[14]: 
0    200
1    400
2    600
Name: a, dtype: int64

当apply()配合有参数的函数时,也可以为函数传递参数:

In [15]: def calc_n(x, n):
    ...:     return x ** n
    ...:

In [16]: df['a'].apply(calc_n, n=10)
Out[16]: 
0        10000000000
1     10240000000000
2    590490000000000
Name: a, dtype: int64

applymap()

矩阵(DataFrame)的applymap()方法可以对矩阵中每一个元素进行遍历迭代操作:

In [18]: df.applymap(lambda x: x * 2)
Out[18]: 
    a   b
0  20  40
1  40  60
2  60  80

In [19]: df*2
Out[19]: 
    a   b
0  20  40
1  40  60
2  60  80

行、列的迭代

除了对矩阵使用apply()方法进行迭代外,还可以.iteritems().iterrows().itertuples()方法进行行、列的迭代,以便进行更复杂的操作。.iteritems()列迭代每次取出的i是一个元组,在元组中,第[0]项是原来的列名称,第[1]列是由原来该列的元素构成的一个Series:

In [20]: for i in df.iteritems():
    ...:     print(type(i), i)
    ...:
<class 'tuple'> ('a', 0    10  
1    20
2    30
Name: a, dtype: int64)
<class 'tuple'> ('b', 0    20
1    30
2    40
Name: b, dtype: int64)

In [21]: for i in df.iteritems():
    ...:     print(type(i[1]), i[1])
    ...:
<class 'pandas.core.series.Series'> 0    10
1    20
2    30
Name: a, dtype: int64
<class 'pandas.core.series.Series'> 0    20
1    30
2    40
Name: b, dtype: int64

行迭代与列迭代的形式一样:

In [22]: for i in df.iterrows():
    ...:     print(type(i), i , end='\n --------- \n')
    ...:
<class 'tuple'> (0, a    10
b    20
Name: 0, dtype: int64)
 ---------
<class 'tuple'> (1, a    20
b    30
Name: 1, dtype: int64)
 ---------
<class 'tuple'> (2, a    30
b    40
Name: 2, dtype: int64)
 ---------

In [23]: for i in df.iterrows():
    ...:     print(type(i[1]), i[1] , end='\n --------- \n')
    ...:
<class 'pandas.core.series.Series'> a    10
b    20
Name: 0, dtype: int64
 ---------
<class 'pandas.core.series.Series'> a    20
b    30
Name: 1, dtype: int64
 ---------
<class 'pandas.core.series.Series'> a    30
b    40
Name: 2, dtype: int64
 ---------

.itertuples()方法取出的每一行是一个Pandas对象:

In [24]: for i in df.itertuples():
    ...:     print(type(i), i)
    ...:
<class 'pandas.core.frame.Pandas'> Pandas(Index=0, a=10, b=20)
<class 'pandas.core.frame.Pandas'> Pandas(Index=1, a=20, b=30)
<class 'pandas.core.frame.Pandas'> Pandas(Index=2, a=30, b=40)

函数向量化

Series是一个向量,但是其中的元素却是一个个数值,如何将两个Series像两个数值元素一样进行使用?

In [27]: import numpy as np

In [28]: def vs_num(a, b):
    ...:     if a > b:
    ...:         return 'a获胜!'
    ...:     elif a == b:
    ...:         return '平局~'
    ...:     else:
    ...:         return 'b获胜!'
    ...:

In [29]: np.vectorize(vs_num)(df['a'], df['b'])
Out[29]: array(['b获胜!', 'b获胜!', 'b获胜!'], dtype='<U4')

In [30]: df
Out[30]: 
    a   b
0  10  20
1  20  30
2  30  40

在函数前加上@np.vectorize语法糖也是同样的效果。