管道中的函数式编程和 python pandas 数据帧

functional programming and python pandas dataframes in pipelines

我想知道编写处理 pandas 数据帧 - 或任何其他可变输入类型 - 作为函数输入的函数管道的函数编程的最佳实践。

这里有 2 个想法,但希望存在更好的东西:)

想法 # 1 - 没有函数式编程但节省内存

def foo(df, param):
    df['col'] = df['col'] + param

def pipeline(df):
    foo(df, 1)
    foo(df, 2)
    foo(df, 3)

idea # 2 - 更多函数式编程,但通过执行 .copy()

浪费内存
def foo(df, param):
    df = df.copy()
    df['col'] = df['col'] + param
    return df

def pipeline(df):
    df1 = foo(df, 1)
    df2 = foo(df1, 2)
    df3 = foo(df2, 3)

您可以链接对数据帧进行操作的函数调用。另请查看 pandas 中的 DataFrame.pipe。像这样,添加几个非 foo 操作:

df = (df.pipe(foo,1)
      .pipe(foo,2)
      .pipe(foo,3)
      .drop(columns=['drop','these'])
      .assign(NEW_COL=lambda x: x['OLD_COL'] / 10))
当您使用 pipe.

时,

df 将是传递给 foo 的第一个参数

如果您的数据框是一维的(即它是一个项目列表),那么您可以不用应用多个 'pipes' 一次性完成所有事情。例如我们有一个数据框

>>> table
     Name  Year of Birth      City
0    Mike           1970  New York
1   Chris           1981     Miami
2  Janine           1975   Seattle

我们想计算年龄并检查是否有人住在迈阿密。您可以应用 2 个管道,但可以使用单个 lambda 来完成:

>>> f = lambda s: pd.Series([2019 - s['Year of Birth'], s['City'] == 'Miami'])

然后应用它

>>> table[['Age', 'Lives in Miami']] = table.apply(f, axis=1)
>>> table
     Name  Year of Birth      City  Age  Lives in Miami
0    Mike           1970  New York   49           False
1   Chris           1981     Miami   38            True
2  Janine           1975   Seattle   44           False

所有工作都在 f 完成。

再举个例子:假设你想查询谁超过了45岁。这包含两个串行操作(前面的例子是2个并行操作):

  1. 计算年龄
  2. 检查年龄是否大于 45

您可以使用 2 个管道,但同样可以通过应用一个 lambda 来完成:

>>> table
     Name  Year of Birth      City   
0    Mike           1970  New York            
1   Chris           1981     Miami           
2  Janine           1975   Seattle      
>>> g = lambda s: pd.Series([2019 - s['Year of Birth'] > 45])              
>>> table['Older than 45'] = table.apply(g, axis=1)
>>> table
     Name  Year of Birth      City  Older than 45
0    Mike           1970  New York           True
1   Chris           1981     Miami          False
2  Janine           1975   Seattle          False

如果您更喜欢不变性,那么

>>> table
     Name  Year of Birth      City
0    Mike           1970  New York
1   Chris           1981     Miami
2  Janine           1975   Seattle 
>>> pd.concat([table, table.apply(f, axis=1)], axis=1)
     Name  Year of Birth      City   0      1
0    Mike           1970  New York  49  False
1   Chris           1981     Miami  38   True
2  Janine           1975   Seattle  44  False

如果你不喜欢 lambda,那么定义

def f(s):
    return pd.Series([2019 - s['Year of Birth'], 
                      s['City'] == 'Miami'])

这一切仅在您想要应用只需要在单个行上工作的映射时才有效。另外,我对速度没有任何要求,管道可能会更快,但这是非常可读的。

(最后你意识到你最好使用 Haskell。)