在pandas中,“apply+lambda函数”是数据批量处理最效率、最简洁的方法。
在实际工作中,我们常会遇到批量处理列数据的情况,比如我们需要将表格中某一列数据的第1个字符提取出来,存入新的一列;或者我们需要将某两列数据进行配对比较,将较大的一个数存放到新的一列中。这就涉及到了列数据按规则批量赋值,其中这个规则,便是lambda函数。
对某一列使用apply+lambda
对一列使用apply+lambda函数非常简单,直接以代码做例子:
df['ApartmentNum'] = df['Apartment'].apply(lambda x:x[-4:])
对一列使用apply的本质即 对一个pandas的Series对象中的内容做函数变换。
对一整个DataFrame使用apply
当需要对每一行的超过两 列/字段 的数据进行批处理,并新生成一列时,便需要对一整个DataFrame使用apply。
比如下图,我们需要将上面数据表的Apartment这一列的后4位数字,和ID这一列的前3位数字合并起来,存放到新的一列“ApartmentID”中。
处理代码为:
df['ApartmentID'] = df.apply(lambda x: x['Apartment'][-4:] + x['ID'][:3], axis=1)
axis=1
设置axis=1是对整个DataFrame使用apply方法的必要参数。
从下图可以理解axis参数对DataFrame处理行列数据的方向的指定。
我们需要从上到下对每一行的数据进行处理,所以设axis=1。
空值陷阱
pandas存在三种空值,在Jupyter Notebook展示的表格中会依次显示为
NaN、None、nan
NaN 是pandas读取数据时(如Excel表)为空格时产生的,类型为<class ‘float’>。
None 是常出现在使用apply+lambda方法时,没被赋值的单元格,它的类型是<class ‘NoneType’>
而我们对DataFrame做循环匹配赋值给一个新列时,如果不先统一对新列赋一个值,直接进行循环匹配赋值,当碰到某个新列的单元格没赋上值,会产生一个 nan 的值。它的类型是<class ‘numpy.str_’>。
例:
for i in df_tofill.index:
if i in df_to_match.index:
df_tofill.at[i, '项目期间'] = df_to_match.at[i,'项目期间']
结果:
右边的新列出现了nan值,由于它的类型是<class ‘numpy.str_’>,pandas的isna()函数判别不出其为空值(上图所示)。
该如何解决呢?
永远记得pandas处理列数据优先使用apply+lambda函数:
def to_fill_match0(input):
if input.name in df_to_match.index:
return df_to_match.at[input.name,'项目期间']
df_tofill['项目期间'] = df_tofill.apply(to_fill_match0, axis=1)
结果:
我们看到这时没赋值的单元格显示为了None,它的类型是<class ‘NoneType’>,可以被pandas的isna()函数识别。
这样就不会在之后的数据处理中出现空值判别bug。
获取index列信息
我们在对DataFrame使用apply函数时,传入自定义的函数时如果需要提取每一行的index时,可以使用input(传入参数).name来获取。
如上文的代码:
def to_fill_match0(input):
if input.name in df_to_match.index:
return df_to_match.at[input.name,'项目期间']
df_tofill['项目期间'] = df_tofill.apply(to_fill_match0, axis=1)
这是因为这时input的对象是一个行数据组成的Series,name属性即为这时的index名。