Используйте функции .str.*
, которые автоматически распознают игнорирование как None
, так и нулевых значений (они включены дословно):
>>> df.item1.str.lower()
0 ak
1 ck
2 None
Name: item1, dtype: object
Нет необходимости делать какой-либо выбор здесь.
См. Работа с текстовыми данными Документация :
Серия и индекс оснащены набором методов обработки строк, облегчающих егоработать с каждым элементом массива.Возможно, наиболее важно то, что эти методы автоматически исключают пропущенные значения / значения NA.
и из Series.str
документации ;
Векторизованные строковые функции для Seriesи индекс.NA остаются NA, если не обрабатываются каким-либо другим способом определенным способом.
Хотя «Джезраэль» предлагает вам использовать понимание списка, это ложная оптимизация.Для вашей чрезвычайно малой серии выборок только с 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 * 1030)* микро секунд между этими двумя временами)
Как только набор данных становится немного больше (всего 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 строк и на больших фреймах данных, где производительность фактически начинает растиВ любом случае, векторизованный метод всегда будет быстрее. </p>