【问题标题】:Pandas: create two new columns in a dataframe with values calculated from a pre-existing columnPandas:在数据框中创建两个新列,其值是从预先存在的列中计算出来的
【发布时间】:2012-09-03 14:29:23
【问题描述】:

我正在使用 pandas 库,我想将两个新列添加到具有 n 列 (n > 0) 的数据框 df
这些新列是通过将函数应用于数据框中的某一列而产生的。

要应用的函数是这样的:

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) 返回一个熊猫系列,每个项目由一个元组 z, y 组成。并尝试将其分配给两个数据框列会产生 ValueError。*

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    我只会使用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
    

    【讨论】:

    • 谢谢,太好了,它有效。我在 0.8.1 的文档中没有找到类似的东西...我想我应该始终将 Series 视为元组列表...
    • 这样做有什么不同的表现吗? zip(*map(calculate,df["a"])) 而不是 zip(*df["a"].map(calculate)),它也给出(如上)[(2, 4, 6), ( 3, 6, 9)] ?
    • 我在像这样创建新列时收到以下警告:“SettingWithCopyWarning:试图在 DataFrame 的切片副本上设置值。尝试使用 .loc[row_indexer,col_indexer] =取而代之的是价值。”我应该为此担心吗?熊猫 v.0.15
    【解决方案2】:

    在我看来,最佳答案是有缺陷的。希望没有人使用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)
    

    比 zip 快 60 倍


    一般情况下,避免使用 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)
    

    所以这慢了一倍,这并不是一个糟糕的性能回归,但如果我们对上述内容进行 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 的速度有多慢,但是为了更加清楚,让我们看一下最基本的例子。让我们对包含和不包含 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)
    

    【讨论】:

    • 这是一个非常好的答案。我想问:对于必须对数据帧的每个元素实现特定功能的情况,您如何看待applymap
    • 虽然这个答案中有一些很好的建议,但我认为使用func(series) 而不是series.apply(func) 的主要建议仅适用于使用对个人行为相似的操作完全定义函数的情况值和系列。在第一个答案的示例中就是这种情况,但在 OP 的问题中并非如此,它更普遍地询问有关将函数应用于列的问题。 1/2
    • 举个例子,如果 df 是:DataFrame({'a': ['Aaron', 'Bert', 'Christopher'], 'b': ['Bold', 'Courageous', 'Distrusted']})calc 是:def calc(x): return x[0], len(x) 那么tdf.a.apply(calc))calc(tdf.a) 返回非常不同的东西。
    • @Graham Lea 是的,但您总是可以(可能或不可能)对函数进行矢量化。我试过了,效果很好tdf['A1'], tdf['A2'] = np.vectorize(calc)(tdf.a)
    猜你喜欢
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 2021-03-19
    • 1970-01-01
    • 2020-06-15
    相关资源
    最近更新 更多