我正在使用pandas库,我希望向数据框df
添加两个新列,其中包含n列(n> 0)。
这些新列是将函数应用于数据框中的一列的结果。
要应用的功能如下:
def calculate(x):
...operate...
return z, y
为仅返回值的函数创建新列的一种方法是:
df['new_col']) = df['column_A'].map(a_function)
所以,我想要的,并且尝试不成功(*),就像:
(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)
实现这一目标的最佳方法是什么?我毫不掩饰地扫描了documentation。
** df['column_A'].map(calculate)
返回一个pandas系列,每个项目由一个元组z,y组成。尝试将其分配给两个dataframe列会产生ValueError。*
答案 0 :(得分:107)
我只是使用zip
:
In [1]: from pandas import *
In [2]: def calculate(x):
...: return x*2, x*3
...:
In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]})
In [4]: df
Out[4]:
a b
0 1 2
1 2 3
2 3 4
In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate))
In [6]: df
Out[6]:
a b A1 A2
0 1 2 2 3
1 2 3 4 6
2 3 4 6 9
答案 1 :(得分:36)
在我看来,最佳答案是有缺陷的。希望没有人通过from pandas import *
将所有pandas大量导入其命名空间。此外,map
方法应保留在传递字典或系列时的那些时间。它可以采用一个函数,但这是apply
所用的。
所以,如果你必须使用上面的方法,我会像这样写
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
实际上没有理由在这里使用zip。你可以这样做:
df["A1"], df["A2"] = calculate(df['a'])
第二种方法在较大的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)
比拉链快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)
所以这是两倍慢,这不是一个糟糕的性能回归,但如果我们对上述进行cython化,我们会获得更好的性能。假设您正在使用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万个数字的系列,无论是否适用
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)