使用.str.* 函数,它们会自动知道忽略None 和空值(逐字包含):
>>> 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
由于每 1 行 2 行中的比率为空,时间并没有发生重大变化。我尝试了不同的比率包括 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 行的小型数据帧的性能差异,而在性能开始很重要的大型数据帧上,矢量化方法总是会更快。