На мою думку, відповідь є хибною. Сподіваємось, ніхто не масово імпортує всі панди у свій простір імен from pandas import *
. Також map
метод повинен бути зарезервований для тих часів при передачі йому словника чи серії. Він може приймати функцію, але саме для цього apply
використовується.
Отже, якщо ви повинні використовувати вищезазначений підхід, я б написав це так
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
Тут насправді немає причин використовувати zip. Ви можете просто зробити це:
df["A1"], df["A2"] = calculate(df['a'])
Цей другий метод також набагато швидший у великих DataFrames
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
DataFrame створений з 300 000 рядків
%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
На 60 разів швидше, ніж на блискавці
Загалом, уникайте використання застосунку
Застосувати, як правило, не набагато швидше, ніж повторення над списком Python. Давайте перевіримо працездатність for-циклу, щоб зробити те саме, що описано вище
%%timeit
A1, A2 = [], []
for val in df['a']:
A1.append(val**2)
A2.append(val**3)
df['A1'] = A1
df['A2'] = A2
298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Отже, це вдвічі повільніше, що не є страшним регресом продуктивності, але якщо ми цитонізуємо вище, ми отримаємо набагато кращі показники. Припустимо, ви використовуєте ipython:
%load_ext cython
%%cython
cpdef power(vals):
A1, A2 = [], []
cdef double val
for val in vals:
A1.append(val**2)
A2.append(val**3)
return A1, A2
%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Безпосередньо призначення без застосування
Ви можете отримати ще більші покращення швидкості, якщо використовувати прямі векторизовані операції.
%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Це використовує переваги надзвичайно швидких векторизованих операцій NumPy замість наших циклів. Тепер ми маємо 30-кратну швидкість над оригіналом.
Найпростіший тест на швидкість з apply
Наведений вище приклад повинен чітко показувати, наскільки apply
може бути повільним , але тільки так його надзвичайно зрозуміло, давайте розглянемо найосновніший приклад. Давайте квадрат на серію з 10 мільйонів чисел із застосуванням і без застосування
s = pd.Series(np.random.rand(10000000))
%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Без застосування це на 50 разів швидше
%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)