cre*_*ion 7 method-chaining pandas
在 Pandas 中转换列的最流畅(或易于阅读)的方法链接解决方案是什么?
\n(\xe2\x80\x9cmethod chaining\xe2\x80\x9d 或 \xe2\x80\x9c Fluent\xe2\x80\x9d 是Tom Augspurger 等人流行的编码风格。)
\n为了示例,让我们设置一些示例数据:
\nimport pandas as pd\nimport seaborn as sns\n\ndf = sns.load_dataset("iris").astype(str) # Just for this example\ndf.loc[1, :] = "NA"\n\ndf.head()\n# \n# sepal_length sepal_width petal_length petal_width species\n# 0 5.1 3.5 1.4 0.2 setosa\n# 1 NA NA NA NA NA\n# 2 4.7 3.2 1.3 0.2 setosa\n# 3 4.6 3.1 1.5 0.2 setosa\n# 4 5.0 3.6 1.4 0.2 setosa\nRun Code Online (Sandbox Code Playgroud)\n仅举此示例:我想通过函数(sepal_length使用pd.to_numeric)映射某些列,同时保持其他列不变。以方法链接方式实现这一点的最简单方法是什么?
我已经可以使用分配,但我在这里重复列名,这是我不想要的。
\nnew_result = (\n df.assign(sepal_length = lambda df_: pd.to_numeric(df_.sepal_length, errors="coerce"))\n .head() # Further chaining methods, what it may be\n )\nRun Code Online (Sandbox Code Playgroud)\n我可以使用转换,但转换会删除(!)未提及的列。对其他列进行直通转换将是理想的选择:
\n# Columns not mentioned in transform are lost\nnew_result = (\n df.transform({\'sepal_length\': lambda series: pd.to_numeric(series, errors="coerce")})\n .head() # Further chaining methods...\n )\nRun Code Online (Sandbox Code Playgroud)\n是否有 \xe2\x80\x9cbest\xe2\x80\x9d 方法以流畅的方式将转换应用于某些列,并传递其他列?
\n编辑:在这一行下面,是阅读劳伦特的想法后的建议。
\n添加一个辅助函数,允许将映射仅应用到一列:
\nnew_result = (\n df.assign(sepal_length = lambda df_: pd.to_numeric(df_.sepal_length, errors="coerce"))\n .head() # Further chaining methods, what it may be\n )\nRun Code Online (Sandbox Code Playgroud)\n现在,这允许在前面的示例中进行以下整齐的链接:
\n# Columns not mentioned in transform are lost\nnew_result = (\n df.transform({\'sepal_length\': lambda series: pd.to_numeric(series, errors="coerce")})\n .head() # Further chaining methods...\n )\nRun Code Online (Sandbox Code Playgroud)\n然而,我仍然对如何在本地 Pandas 中执行此操作而无需粘合代码的方法持开放态度。
\n编辑 2 以进一步适应 Laurent 的想法,作为替代方案。独立的示例:
\nimport functools\n\ncoerce_numeric = functools.partial(pd.to_numeric, errors=\'coerce\')\n\ndef on_column(column, mapping):\n """\n Adaptor that takes a column transformation and returns a "whole dataframe" function suitable for .pipe()\n \n Notice that columns take the name of the returned series, if applicable\n Columns mapped to None are removed from the result.\n """\n def on_column_(df):\n df = df.copy(deep=False)\n res = mapping(df[column])\n # drop column if mapped to None\n if res is None:\n df.pop(column)\n return df\n df[column] = res\n # update column name if mapper changes its name\n if hasattr(res, \'name\') and res.name != col:\n df = df.rename(columns={column: res.name})\n return df\n return on_column_\nRun Code Online (Sandbox Code Playgroud)\n
这是我对你有趣的问题的看法。
我不知道 Pandas 中还有比组合pipeline、allocate或transform更惯用的方法链接方式。但我知道“通过其他列的直通进行转换将是理想的”。
因此,我建议将其与高阶函数一起使用来处理其他列,通过利用 Python 标准库functools模块进行更多类似函数的编码。
例如,使用以下玩具数据框:
df = pd.DataFrame(
{"col1": ["4", "1", "3", "2"], "col2": [9, 7, 6, 5], "col3": ["w", "z", "x", "y"]}
)
Run Code Online (Sandbox Code Playgroud)
您可以定义以下部分对象:
from functools import partial
from typing import Any, Callable
import pandas as pd
def helper(df: pd.DataFrame, col: str, method: Callable[..., Any]) -> pd.DataFrame:
funcs = {col: method} | {k: lambda x: x for k in df.columns if k != col}
# preserve original order of columns
return {key: funcs[key] for key in df.columns}
on = partial(helper, df)
Run Code Online (Sandbox Code Playgroud)
然后使用 进行各种链分配transform,例如:
df = (
df
.transform(on("col1", pd.to_numeric))
.sort_values(by="col1")
.transform(on("col2", lambda x: x.astype(str) + "0"))
.transform(on("col3", str.upper))
.reset_index(drop=True)
)
print(df)
# Ouput
col1 col2 col3
0 1 70 Z
1 2 50 Y
2 3 60 X
3 4 90 W
Run Code Online (Sandbox Code Playgroud)