如何克服'NoneType'对象没有属性'lower'错误?

时间:2018-09-26 11:08:57

标签: python pandas

我有DataFrame

df=pd.DataFrame({'id':[1,2,3],'item1':['AK','CK',None],
'item2':['b','d','e'],'item3':['c','e',np.nan]})

我想将列item1的所有值都转换为小写。

我尝试过:

df['item1'].apply(lambda x: x.lower())

那给了我一个错误:

  

AttributeError:'NoneType'对象没有属性'lower'

我知道为什么会这样。我的列值中的一个是None

无论如何我都想忽略该值,并将其余的值转换为小写。

有没有办法克服这个问题?

P.S:我的原始DataFrame可能具有任意数量的值,因为它是由另一个函数返回的。此处不删除行,因为这些记录对我很重要。

3 个答案:

答案 0 :(得分:8)

很简单:

df['item1'].apply(lambda x: x.lower() if x is not None else x)

如果要处理没有lower()方法的其他可能的类型(整型,浮点型等):

df['item1'].apply(lambda x: x.lower() if hasattr(x, "lower") and callable(x.lower)  else x)

答案 1 :(得分:4)

对于char *msg; msg = malloc(2); if (read(sockfd, msg, 1)!=1) { printf("Something wrong\n"); return 0; } msg[1]= '\0'; printf("Sent received: %s \n", msg); None的值,更通用的解决方案是使用NaN函数,另一解决方案是使用列表理解。

pandas字符串函数也可以与notnullNone s一起很好地工作:

NaN

df['new1'] = df['item1'].apply(lambda x: x.lower() if pd.notnull(x) else x)

df['new2'] = [x.lower() if pd.notnull(x) else x for x in df['item1']]

df['new3'] = df['item1'].str.lower()
print (df)
   id item1 item2 item3  new1  new2  new3
0   1    AK     b     c    ak    ak    ak
1   2    CK     d     e    ck    ck    ck
2   3  None     e   NaN  None  None  None

在大型DataFrame中,如果不需要检查列表缺失值,则列表解析速度更快:

df=pd.DataFrame({'id':[1,2,3],'item1':['AK',np.nan,None],
'item2':['b','d','e'],'item3':['c','e',np.nan]})
print (df)
   id item1 item2 item3
0   1    AK     b     c
1   2   NaN     d     e
2   3  None     e   NaN

df['new1'] = df['item1'].apply(lambda x: x.lower() if pd.notnull(x) else x)
df['new2'] = [x.lower() if pd.notnull(x) else x for x in df['item1']]
df['new3'] = df['item1'].str.lower()
print (df)
   id item1 item2 item3  new1  new2  new3
0   1    AK     b     c    ak    ak    ak
1   2   NaN     d     e   NaN   NaN   NaN
2   3  None     e   NaN  None  None  None

答案 2 :(得分:4)

使用.str.*函数,这些函数会自动忽略None和null(逐字包括在内):

>>> df.item1.str.lower()
0      ak
1      ck
2    None
Name: item1, dtype: object

这里不需要进行任何选择。

请参见Working with Text Data documentation

  

Series和Index配备了一组字符串处理方法,这些方法使操作数组的每个元素变得容易。也许最重要的是,这些方法会自动排除丢失/ NA值。

,并来自Series.str documentation

  

系列和索引的向量化字符串函数。除非使用其他特殊方法处理,否则NA保持NA。

尽管Jezrael建议您使用列表理解,但这是错误的优化。对于只有3个值的极小的样本系列,列表理解确实更快:

>>> from timeit import Timer
>>> import pandas as pd
>>> tests = {}
>>> tests['vectorised .str'] = 's.str.lower()'
>>> tests['list comprehension'] = '[v.lower() if pd.notnull(v) else v for v in s]'
>>> small = pd.Series(['AK', 'CK', None])
>>> for name, test in tests.items():
...     count, totaltime = Timer(test, 'from __main__ import small as s, pd').autorange()
...     print(f'{name:>20}: {totaltime / count * 1000:.5f}ms')
...
     vectorised .str: 0.09495ms
  list comprehension: 0.01051ms

几乎10倍的速度差异看起来令人印象深刻,但并不现实或显着(这两个时间之间只有84微秒)

一旦数据集变得更大(仅250行),矢量化字符串操作就已经更快了:

>>> larger = pd.Series([random.choice(string.ascii_uppercase) +
                        random.choice(string.ascii_uppercase)
...                     for _ in range(250)])  # 250 2-character uppercase strings
>>> for name, test in tests.items():
...     count, totaltime = Timer(test, 'from __main__ import larger as s, pd').autorange()
...     print(f'{name:>20}: {totaltime / count * 1000:.5f}ms')
...
     vectorised .str: 0.15494ms
  list comprehension: 0.16758ms

零值的比例在这里没有区别;这是一个示例计时,其中一半的行已设置为None

>>> larger_1in2 = larger.copy()
>>> large_1in2[np.random.random(large_1in2.shape) < 0.5] = None
>>> for name, test in tests.items():
...     count, totaltime = Timer(test, 'from __main__ import larger_1in2 as s, pd').autorange()
...     print(f'{name:>20}: {totaltime / count * 1000:.5f}ms')
...
     vectorised .str: 0.14170ms
  list comprehension: 0.16098ms

每2行中每1行中的比率为null,则计时不会发生实质性的变化。我尝试了不同的比率,包括100%的空值,并且速度差异没有动摇,因为250行矢量化操作更快。

但是矢量化字符串操作和列表理解之间的差异只会随着序列数的增加而变得更大(有利于矢量化字符串操作):

>>> large = pd.Series([random.choice(string.ascii_uppercase) +
...                    random.choice(string.ascii_uppercase)
...                    for _ in range(1000)])
>>> for name, test in tests.items():
...     count, totaltime = Timer(test, 'from __main__ import large as s, pd').autorange()
...     print(f'{name:>20}: {totaltime / count * 1000:.5f}ms')
...
     vectorised .str: 0.28704ms
  list comprehension: 0.56312ms

您甚至根本不会注意到在<250行的小型数据帧上的性能差异,而在实际开始影响性能的大型数据帧上,矢量化方法总是会更快。