首页 文章

Pandas:在数据框中创建两个新列,其中的值是根据预先存在的列计算的

提问于
浏览
81

我正在使用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 . *

2 回答

  • 26

    我只是使用 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
    
  • 102

    在我看来,最重要的答案是有缺陷的 . 希望没有人用 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})
    

    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)
    

    所以这是两倍慢,这不是一个糟糕的性能回归,但如果我们对上述进行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 的速度有多慢,但正好让它更加清晰,让's look at the most basic example. Let' s方形成为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)
    

相关问题