На мою думку, відповідь є хибною. Сподіваємось, ніхто не масово імпортує всі панди у свій простір імен 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)