我有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
可能具有任意数量的值,因为它是由另一个函数返回的。此处不删除行,因为这些记录对我很重要。
答案 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字符串函数也可以与notnull
和None
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值。
系列和索引的向量化字符串函数。除非使用其他特殊方法处理,否则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行的小型数据帧上的性能差异,而在实际开始影响性能的大型数据帧上,矢量化方法总是会更快。