管道中的函数式编程和 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个并行操作):
- 计算年龄
- 检查年龄是否大于 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。)
我想知道编写处理 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个并行操作):
- 计算年龄
- 检查年龄是否大于 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。)