На мой взгляд, главный ответ ошибочен. Надеюсь, никто не будет массово импортировать все панды в свое пространство имен с помощью 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 раз быстрее, чем zip
В общем, избегайте использования apply
Apply обычно не намного быстрее, чем итерация по списку 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)