Pandas DataFrame将功能应用于多列并输出多列

时间:2019-11-11 09:09:16

标签: python pandas dataframe apply

我一直在寻找应用函数的最佳方法,该函数采用多个单独的Pan​​das DataFrame列并在相同的所说DataFrame中输出多个新列。假设我有以下内容:

def apply_func_to_df(df):
    df[['new_A', 'new_B']] = df.apply(lambda x: transform_func(x['A'], x['B'], x['C']), axis=1)

def transform_func(value_A, value_B, value_C):
    # do some processing and transformation and stuff
    return new_value_A, new_value_B

我正在尝试将上述功能应用于整个DataFrame df,以便输出2个NEW列。但是,这可以推广到一个用例/函数,该用例/函数接受n个DataFrame列,并将m个新列输出到同一DataFrame。

以下是我一直在关注的事情(取得不同程度的成功):

  • 为函数调用创建Pandas系列,然后附加到现有的DataFrame中,
  • 压缩输出列(但在当前的实现中会发生一些问题)
  • 重新编写基本功能transform_func,以明确期望行(即字段)ABC,如下所示,然后将其应用于df:

def transform_func_mod(df_row):
    # do something with df_row['A'], df_row['B'], df_row['C]
    return new_value_A, new_value_B

我希望以一种非常通用的Python方式来完成此任务,同时兼顾性能(包括内存和时间)。我对此表示感谢,因为由于对熊猫不熟悉,我一直在为此苦苦挣扎。

2 个答案:

答案 0 :(得分:3)

通过以下方式编写 transform_func

  • 它应该具有一个参数-当前行,
  • 此功能可以读取当前行中的各个列 并充分利用它们,
  • 返回的对象应该是具有以下内容的 Series
    • 值-您要返回的任何值
    • 索引-目标列名称。

示例:假设所有3列均为 string 类型,并置 A B 列,请在 C :

def transform_func(row):
    a = row.A; b = row.B; c = row.C;
    return pd.Series([ a + b, c + '_xx'], index=['new_A', 'new_B'])

要仅获取新值,请将此功能应用于每一行:

df.apply(transform_func, axis=1)

请注意,生成的DataFrame保留原始行的键 (我们稍后会使用此功能)。

或者,如果您想添加这些新列到您的DataFrame中,请加入您的 df 使用上述应用程序的结果,将连接结果保存在 原始的 df

df = df.join(df.apply(transform_func, axis=1))

按照截至03:36:34Z的评论进行编辑

使用 zip 可能是最慢的选择。 基于行的功能应该更快,并且结构更直观。 最快的方法可能是为每个列分别编写2个向量化表达式。在这种情况下,类似:

df['new_A'] = df.A + df.B
df['new_B'] = df.C + '_xx'

但是通常问题是是否基于行的函数 可以表示为矢量化表达式(如上所述)。 在“负”情况下,您可以应用基于行的函数。

要比较每个解决方案的速度,请使用%timeit

答案 1 :(得分:1)

该问题似乎与this question有关。我参考了@ spen.smith在this answer上提出的评论。

df = pd.DataFrame([[1,2,3], [2,3,4], [3,5,7]], columns = ['A', 'B', 'C'])
print(df)

   A  B  C
0  1  2  3
1  2  3  4
2  3  5  7

不像修改函数的返回那样,只需照常创建

def add_subtract(args):
    arg1, arg2 = args

    ret1 = arg1 + arg2
    ret2 = arg1 - arg2

    return ret1, ret2

检查使用apply的输出。选项result_type='expand'以数据框而不是一系列元组的形式返回结果。

print(df[['B', 'C']].apply(add_subtract, axis=1, result_type='expand'))

    0  1
0   5 -1
1   7 -1
2  12 -2

然后我们可以通过转置然后访问值来将apply输出的列分配给两个新系列。必须进行转置,因为调用values的默认行为会将每一行都视为一个列表,而我们希望将每一列都视为一个列表。所以最终的表达式是:

df['D'], df['E'] = df[['B', 'C']].apply(add_subtract, axis=1, result_type='expand').transpose().values
print(df)

   A  B  C   D  E
0  1  2  3   5 -1
1  2  3  4   7 -1
2  3  5  7  12 -2