Pandas 应用返回两个新列的函数

Posted

技术标签:

【中文标题】Pandas 应用返回两个新列的函数【英文标题】:Pandas Apply Function That returns two new columns 【发布时间】:2018-06-06 18:36:03 【问题描述】:

我有一个pandas 数据框,我想在其上使用应用函数来根据现有数据生成两个新列。我收到此错误: ValueError: Wrong number of items passed 2, placement implies 1

import pandas as pd
import numpy as np

def myfunc1(row):
    C = row['A'] + 10
    D = row['A'] + 50
    return [C, D]

df = pd.DataFrame(np.random.randint(0,10,size=(2, 2)), columns=list('AB'))

df['C', 'D'] = df.apply(myfunc1 ,axis=1)

开始 DF:

   A  B
0  6  1
1  8  4

所需的 DF:

   A  B  C   D
0  6  1  16  56
1  8  4  18  58

【问题讨论】:

让它df[['C', 'D']] 你的函数总是需要两列作为输入吗? @coldspeed,传递的数据帧可能有很多列,但计算只需要两列 Apply pandas function to column to create multiple new columns?的可能重复 【参考方案1】:

查询多列时添加额外的括号。

import pandas as pd
import numpy as np

def myfunc1(row):
    C = row['A'] + 10
    D = row['A'] + 50
    return [C, D]

df = pd.DataFrame(np.random.randint(0,10,size=(2, 2)), columns=list('AB'))

df[['C', 'D']] = df.apply(myfunc1 ,axis=1)

【讨论】:

【参考方案2】:

df['C','D'] 被视为 1 列而不是 2 列。因此对于 2 列,您需要一个切片数据框,因此请使用 df[['C','D']]

df[['C', 'D']] = df.apply(myfunc1 ,axis=1)

    A  B   C   D
0  4  6  14  54
1  5  1  15  55

或者你可以使用链式分配,即

df['C'], df['D'] = df.apply(myfunc1 ,axis=1)

【讨论】:

这适用于我的示例数据集(如此赞成),但不适用于我的真实数据集,尽管代码相同。错误:KeyError: "['C' 'D'] not in index" 我需要看看你是如何分配数据的。可能是您的实际代码。 同样,唯一不同的代码是从 CSV 读取数据帧与使用 numpy 生成假数据df[['C', 'D']] = df.apply(myfunc1 ,axis=1) 你的myfunc1和上面的一样吗? @user2242044。您的错误消息显示“C”和“D”之间缺少逗号。【参考方案3】:

根据您的最新错误,您可以通过将新列作为系列返回来避免错误

def myfunc1(row):
    C = row['A'] + 10
    D = row['A'] + 50
    return pd.Series([C, D])

df[['C', 'D']] = df.apply(myfunc1 ,axis=1)

【讨论】:

请注意所接受答案的巨大内存消耗和低速,下面的替代解决方案【参考方案4】:

请注意已接受答案的巨大内存消耗和低速:https://ys-l.github.io/posts/2015/08/28/how-not-to-use-pandas-apply/!

使用那里提出的建议,正确答案是这样的:

def run_loopy(df):
    Cs, Ds = [], []
    for _, row in df.iterrows():
        c, d, = myfunc1(row['A'])
        Cs.append(c)
        Ds.append(d)
    return pd.Series('C': Cs,
                      'D': Ds)

def myfunc1(a):
    c = a + 10
    d = a + 50
    return c, d

df[['C', 'D']] = run_loopy(df)

【讨论】:

我认为您应该将Cs, Ds = [], []run_loopy 的第一行)编辑为v1s, v2s = [], [],反之亦然 @codkelden 感谢您的关注!我会把 v1s 和 v2s 改成 Cs 和 Ds,所以谁读了它很快就会明白我们在谈论专栏 这确实快很多【参考方案5】:

它对我有用:

def myfunc1(row):
    C = row['A'] + 10
    D = row['A'] + 50
    return C, D

df = pd.DataFrame(np.random.randint(0,10,size=(2, 2)), columns=list('AB'))

df[['C', 'D']] = df.apply(myfunc1, axis=1, result_type='expand')
df

添加:==>> result_type='expand',

问候!

【讨论】:

【参考方案6】:

我相信在不使用 for 循环的情况下可以达到与@Federico Dorato 回答类似的结果。返回一个列表而不是一个系列,并使用 lambda-apply + to_list() 来扩展结果。

它的代码更简洁,并且在 10,000,000 行的随机 df 上执行得一样好或更快。

费德里科的代码

run_time = []

for i in range(0,25):
    df = pd.DataFrame(np.random.randint(0,10000000,size=(2, 2)), columns=list('AB'))
    def run_loopy(df):
        Cs, Ds = [], []
        for _, row in df.iterrows():
            c, d, = myfunc1(row['A'])
            Cs.append(c)
            Ds.append(d)
        return pd.Series('C': Cs,
                        'D': Ds)

    def myfunc1(a):
        c = a / 10
        d = a + 50
        return c, d

    start = time.time()
    df[['C', 'D']] = run_loopy(df)
    end = time.time()

    run_time.append(end-start) 
print(np.average(run_time)) # 0.001240386962890625

使用 lambda 和 to_list

run_time = []

for i in range(0,25):
    df = pd.DataFrame(np.random.randint(0,10000000,size=(2, 2)), columns=list('AB'))

    def myfunc1(a):
        c = a / 10
        d = a + 50
        return [c, d]

    start = time.time()
    df[['C', 'D']] = df['A'].apply(lambda x: myfunc1(x)).to_list()
    end = time.time()
run_time.append(end-start)
print(np.average(run_time)) #output 0.0009996891021728516

【讨论】:

以上是关于Pandas 应用返回两个新列的函数的主要内容,如果未能解决你的问题,请参考以下文章

python pandas-将带有两个参数的函数应用于列

将 Pandas 列传递给函数时出现“ValueError:Series 的真值不明确”

如何向 pandas df 添加一个新列,该列从另一个数据帧返回同一组中更大的最小值

Pandas - 使用 PostCoder 在每一行中查找纬度和经度,然后在新列中返回 Postcode

根据其他两列的值在 Pandas 中创建一个新列[重复]

Python pandas - 如果项目在列表中,则为新列的值