私の意見では、トップの答えに欠陥があります。うまくいけば、誰もがすべてのパンダを名前空間に大量にインポートしていないfrom pandas import *。また、mapメソッドは、ディクショナリまたはシリーズを渡すときのために予約する必要があります。関数をとることができますが、これapplyが使用されます。
したがって、上記のアプローチを使用する必要がある場合は、次のように記述します
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
ここでzipを使用する理由は実際にはありません。あなたは単にこれを行うことができます:
df["A1"], df["A2"] = calculate(df['a'])
この2番目の方法は、より大きなDataFrameでもはるかに高速です
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
300,000行で作成されたDataFrame
%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)
zipより60倍高速
一般に、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)
したがって、これは2倍遅く、これはひどいパフォーマンスの低下ではありませんが、上記をCythonizeすると、パフォーマンスが大幅に向上します。仮定すると、あなたは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、速度がどれほど遅いかを明確に示しているはずですが、そのために最も明確な例として、最も基本的な例を見てみましょう。適用ありとなしの1000万個のシリーズを2乗しましょう
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)