最近开始学pandas了,这两天一直在做练习,总结一下今天碰到的两个问题。
第一个是apply函数
问题是这样的:我需要把Mjob和Fjob这两个字段的值转换成首字母的形式,这两个字段都是字符串形式。
写了一个函数如下
str_function = lambda str:str.capitalize()
打算用apply函数执行的时候碰到了问题
df[['Mjob','Fjob']].apply(str_function)
结果报错了:说是Series对象没有capitalize()这个方法
image.png
刚开始的时候,不太懂什么意思,只是觉得两个字段都是字符串,应该都可以用capitalize()这个方法。后来,看到这种方法可以运行成功
df['Mjob'].apply(str_function)
df['Fjob'].apply(str_function)
结果为
image.png
image.png
刚看到这个写法运行成功的时候,我更懵了,df[['Mjob','Fjob']]加上apply方法,可以按列执行相应的函数,每一列的类型也都是Series;df['Mjob']这种写法也是Series,为什么同样是Series结果却不一样呢?查看一下,传入apply之后,参数的类型
def get_type(s):
print(type(s))
df['Mjob'].apply(get_type)
结果是
image.png
df[['Mjob','Fjob']].apply(get_type)
image.png
由此可见,第一个Series对象传入apply的时候是一次传入每个值,是元素级别的操作;第二个数据框对象传入apply的时候是依次传入每一列,如果要对数据框进行元素级别的操作,可以用applymap方法
df[['Mjob','Fjob']].applymap(str_function)
结果为
image.png
applymap()是数据框的元素级别操作方法,map()是Series的元素级别操作方法。
既然,apply传入的是列,那么我是不是在函数中加入一个遍历Series对象的语句就可以了呢?然后,我又做了一次试验
num = pd.DataFrame({'A':[1,2,3,4,5,6],
'B':[7,8,9,10,11,12],
'C':list('abcdef')})
def change_number(df):
if type(df) is int:
return df * 10
else:
return df
我先尝试了applymap方法
print(num.applymap(change_number))
结果是正确的
image.png
再用apply,当然num数据肯定不会变,因为type(df)的结果为Series,返回的全是原值:
num.apply(change_number)
image.png
然后,修改函数,加入遍历Series的语句:
def change_number(df):
for item in range(len(df)):
if type(df[item]) is int:
df[item] = df[item] * 10
return df
print(num.apply(change_number))
结果
image.png
结果还是没变
这里就遇到了今天的第二个问题,来查看一下遍历列的时候,每个元素的类型,以其中一个元素为例
A = num['A']
type(A[1])
type(A[1]) is int
type(2)
返回
image.png
image.png
A[1]是数字2,A[1]的数据类型是numpy.int64,根据第二个返回结果,在Python中numpy.int64和int不是一个数据类型,但是单独的数字2类型是int,Series中的元素2类型就是numpy.int64。因此修改函数将判断语句中的int改为numpy.int64,结果就对了。
def get_series_number(df):
for item in range(len(df)):
if type(df[item]) is np.int64:
df[item] = df[item] * 10
return df
print(num.apply(get_series_number))
image.png
另:用int()就可以把numpy.int64类型改为int类型,即int(A[1])。
网友评论