pandas:在数据框中创build两个新列,其中的值是根据预先存在的列计算得出的

我正在与pandas图书馆工作,我想添加两个新的列到一个数据框df与n列(n> 0)。
这些新列是将函数应用于数据框中的某一列的结果。

要应用的function如下所示:

 def calculate(x): ...operate... return z, y 

创build一个只返回一个值的函数的新列的一种方法是:

 df['new_col']) = df['column_A'].map(a_function) 

所以,我想要什么,并试图不成功(*),是这样的:

 (df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate) 

什么是完成这个最好的方法呢? 我没有任何线索扫描文档 。

* df['column_A'].map(calculate)返回一个pandas系列的每个项目,包括一个元组z,y。 并试图将其分配给两个dataframe列产生一个ValueError。

我只是使用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]: ab 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]: ab A1 A2 0 1 2 2 3 1 2 3 4 6 2 3 4 6 9 

最重要的答案在我看来是有缺陷的。 有希望的是,没有人会from pandas import *中将所有的pandas大量地导入他们的命名空间。 另外,在传递字典或系列时, map方法应该保留。 它可以采取一个function,但这是apply于什么。

所以,如果你必须使用上面的方法,我会这样写

 df["A1"], df["A2"] = zip(*df["a"].apply(calculate)) 

这里实际上没有理由使用zip。 你可以简单地做到这一点:

 df["A1"], df["A2"] = calculate(df['a']) 

第二种方法在大型数据框上也快得多

 df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000}) 

DataFrame创build了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倍


一般来说,避免使用apply

应用通常不比迭代Python列表快得多。 让我们来testing一个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) 

所以这是慢两倍,这不是一个可怕的业绩回归,但是如果我们把上面的这些集成到一起,我们会得到更好的performance。 假设你正在使用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) 

直接分配没有适用

如果使用直接vector化操作,则可以获得更高的速度提升。

 %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极其快速的vector化操作,而不是我们的循环。 我们现在比原来的速度提高了30倍。


最简单的速度testing与apply

上面的例子应该清楚地表明apply程序的缓慢程度,但是如此清楚,让我们来看一个最基本的例子。 让我们来计算一千万个数字的系列,不论是否适用

 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)