为什么此lambda操作不起作用?

时间:2019-06-07 07:18:16

标签: python pandas lambda

我想获取数据框中所有显示为“小于”的值,并将其报告为小于值一半的数字。

例如<1变为0.5,<0.5变为0.25,<5变为2.5等。 普通数字和文本应保持不变。

我有以下lambda函数适用于我认为可以正常工作的数据框,但并非如此:

df_no_less_thans= df.apply(lambda x: x if str(x[0])!='<' else float(x[1:])/2)  

我仍然在新的df中获得'<'值,没有错误消息。

我做错了什么?

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]
df.apply(lambda x: x if str(x)[0]!='<' else float(str(x)[1:])/2) 
df

给予

    Cu
0   3.7612
1   1.3693
2   2.7502
3   1.407
4   4.2066
5   6.4409
6   6.8136
7   <0.05
8   <0.05
9   0.94
10  0.07
11  1.82
12  2.63
13  1.36
14  0.7 ```

5 个答案:

答案 0 :(得分:2)

您的代码不适用于整数或浮点数之类的非字符串,因为您必须先将它们转换为字符串才能索引它们。您可以将所有内容显式转换为字符串并执行索引

在执行lambda操作之前,您还希望检查空字符串

#Explicitly cast to string and perform the indexing
func = lambda x: x if  str(x)[0]!='<' else float(str(x)[1:])/2

li = ['<1', '<0.5', '<5', 1, 'hello', 4.0, '']

#Filter out empty strings
print([func(item) for item in li if item])

输出将为

[0.5, 0.25, 2.5, 1, 'hello', 4.0]

答案 1 :(得分:1)

方法apply有一个axis参数。默认情况下,axis=0,这意味着您的lambda函数将依次应用于数据帧中的每一列。在您的情况下,lambda函数将应用于列'Cu',这意味着参数x实际上是一列,而str(x)[0]不是您所想的。

您应该改用applymap来逐个应用lambda函数:

df.applymap(lambda x: x if str(x)[0] != '<' else float(str(x)[1:])/2)

答案 2 :(得分:1)

我认为您只需要对Cu列应用lambda函数,因此正确的解决方案是使用Series.apply

df['Cu'] = df['Cu'].apply(lambda x: x if str(x)[0]!='<' else float(str(x)[1:])/2) 
print (df)

        Cu
0   3.7612
1   1.3693
2   2.7502
3   1.4070
4   4.2066
5   6.4409
6   6.8136
7   0.0250
8   0.0250
9   0.9400
10  0.0700
11  1.8200
12  2.6300
13  1.3600
14  0.7800

如果需要对所有列应用功能,请使用IanS解决方案。

答案 3 :(得分:0)

这是它的工作方式:

import pandas as pd

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]

df['Cu'] = df.apply(lambda x: x if not isinstance(x[0],str) else float(x[0][1:])/2, axis=1, raw=True)

print(df)

结果:

        Cu
0   3.7612
1   1.3693
2   2.7502
3    1.407
4   4.2066
5   6.4409
6   6.8136
7    0.025
8    0.025
9     0.94
10    0.07
11    1.82
12    2.63
13    1.36
14    0.78

答案 4 :(得分:-1)

在您的问题中您说

  

例如<1变为0.5,<0.5变为0.25,<5变为2.5,依此类推。普通数字和文本应保持不变

现在在示例中,您只给您提供了前两种类型的数据:<1float之类的字符串,但是您似乎希望能够保留任何其他类型的文本也一样但是我看到将不同的dtypes混入一列中是一种不好的数据框布局,这只会在将来引起麻烦。

例如,如果您的列中有一些文本hello,则可以执行以下简单操作:

df['Cu'] * 2
# [...]
# 6        13.6272
# 7     hellohello
# 8           0.05
# 9           1.88
# [...]
# Name: Cu, dtype: object

这很可能不是您想要的。

现在我不知道您还拥有其他类型的文本,但是对于给出的示例,我建议您首先对dtypes进行规范化:为此,我们从“不确定性信息”中创建新列df['less_than']:< / p>

import pandas as pd

df=pd.DataFrame()
df['Cu']=[3.7612,1.3693, 2.7502,1.407,4.2066,6.4409,6.8136,"<0.05","<0.05",0.94,0.07,1.82,2.63,1.36,0.78]

df['less_than'] = df['Cu'].str.startswith('<', False)
df.loc[df['less_than'], 'Cu'] = df.loc[df['less_than'], 'Cu'].str.slice(1)

df['Cu'] = df['Cu'].astype(float)
#         Cu  less_than
# 0   3.7612      False
# 1   1.3693      False
# 2   2.7502      False
# 3   1.4070      False
# 4   4.2066      False
# 5   6.4409      False
# 6   6.8136      False
# 7   0.0500       True
# 8   0.0500       True
# 9   0.9400      False
# 10  0.0700      False
# 11  1.8200      False
# 12  2.6300      False
# 13  1.3600      False
# 14  0.7800      False

这使我们能够对整个列df['Cu']进行相同处理,并使您的“ <1变成0.5”操作变得简单:

df.loc[df['less_than'], 'Cu'] /= 2