pandas 将函数应用于基于列名的DataFrame-row

xkrw2x1b  于 2023-08-01  发布在  其他
关注(0)|答案(3)|浏览(106)

假设我有一个DataFrame,其中有很多列df,以及可以应用于这些列以计算不同结果的不同函数fn(...)。因此,列名适合函数的参数:

import pandas as pd

def f1(A, C):
    return(A + C)

def f2(B, C):
    return(C - B)

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

字符串
要将函数应用到每行,我可以使用下面的代码行:

df['result'] = df.apply(lambda row: f1(row['A'], row['C']), axis = 'columns')


然而,由于不同的函数采用不同的输入参数,显式地指定它们有点烦人,所以我正在寻找一种方法,以便apply根据列名识别传递给函数的值。在R中,上面的行要短得多,我发现这非常方便:

df$result = pmap(df, f1)


Python中有类似的东西吗?
顺便说一句,我知道不推荐使用apply,但是这些函数太复杂了,无论如何都不能进行vertorizable(至少对我来说是这样),上面的函数定义使调试变得容易得多。

nhjlsmyf

nhjlsmyf1#

我可能忽略了一些更简单的方法,但是如果你的目标是能够在apply大小上简单地将一行传递给函数,并且在函数代码大小上使用参数name,那么一种方法可能是使用装饰器。
我的意思是,假设你不能把你的函数写成以行作为参数
手动(不带装饰器)方式

def f1(row):
    return row['A']+row['C']

def f2(row):
    C=row['C']
    B=row['B']
    return C-B

字符串
你可以用这个装饰器来帮助创建

def pmapize(*args):
    def inner(f):
        def g(row):
            return f(**{a:row[a] for a in args})
        return g
    return inner

@pmapize('A', 'C')
def f1(A, C):
    return(A + C)

@pmapize('B', 'C')
def f2(B, C):
    return(C - B)


然后,您可以使用这些函数,并将行作为参数传递。因为这是创建一个函数f1(row),其结果是您所期望的

f1({'A':1, 'C':5})
# 6

f2({'A':1, 'C':18, 'B':10})
# 8
# Note that extra argument 'A' is ignored. Only B and C are passed to 
# the "inner" f2 
# And that argument order doesn't matter


当然你可以按预期使用它

df.apply(lambda row: f1(row), axis = 'columns')

Edit:使用内省的简化

使用这个依赖于内省的装饰器,您甚至可以跳过参数名的传递,而直接依赖于给参数的名称

def pmapize2(f):
    argsName=list(f.__code__.co_varnames)[:f.__code__.co_argcount]
    def inner(row):
        return f(**{a:row[a] for a in argsName})
    return inner


然后你可以定义你的函数

@pmapize2
def f3(D, F):
    return (D+2)*F


该函数实际上是一个期望一行作为参数的函数,并且期望在该行中找到字段D和F。
因此,

# With some random order, and extra (ignored) fields in the row
f3({'F':5, 'C':1, 'D':2, 'E':3})
# Returns 20


所以,你可以像以前一样,把它传给Pandas。

tl;dr

从代码中总结

import pandas as pd

def pmapize2(f):
    argsName=list(f.__code__.co_varnames)[:f.__code__.co_argcount]
    def inner(row):
        return f(**{a:row[a] for a in argsName})
    return inner

@pmapize2
def f1(A, C):
    return(A + C)

@pmapize2
def f2(B, C):
    return(C - B)

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

df['result'] = df.apply(f1, axis = 'columns') # Since `lambda row: f1(row)` is just `f1`


结果:

>>> df
   A  B  C  result
0  1  2  3       4
1  4  5  6      10
2  7  8  9      16

kadbb459

kadbb4592#

chrslg's answer之后,我认为最简单的方法是解压缩row并重写f1,以便将未使用的参数传递给**kwargs

import pandas as pd

def f1(A, C, **kwargs):
    return(A + C)

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

df['result'] = df.apply(lambda row: f1(**row), axis = 'columns')

字符串

svmlkihl

svmlkihl3#

您可以在一个函数下一起执行它们,并在那里调用正确的列,然后使用result_type = 'expand'将输出拆分为多个列:

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

def fn(row):
    outputs = []

    if 'A' and 'C' in row.index:
        outputs.append(f1(row.A, row.C))
    if 'B' and 'C' in row.index:
        outputs.append(f2(row.B, row.C))
        
    return outputs

def f1(A, C):
    return(A + C)

def f2(B, C):
    return(C - B)

# Adjusted for variable number of resulting columns
df[[f'results{i}' for i in range(len(results.columns))]] = results

字符串

相关问题