从多个其他变量创建熊猫变量的pythonic方法是什么



我是一名R程序员,目前正在尝试学习Python/Pandas。目前,我正在尝试解决如何从使用多个现有变量的函数清晰干净地创建新变量的问题。

请注意,我示例中使用的函数并不那么复杂,但我试图推广到任意函数的情况,该函数可能要复杂得多或需要更多变量,也就是说,我试图避免针对此特定函数优化的解决方案,并更多地寻找如何处理一般情况。

作为参考,这是我如何在 R 中执行此操作的示例。

library(tidyverse)
df <- data_frame(
num = c(15, 52 , 24 , 29),
cls = c("a" , "b" , "b", "a")
)
attempt1 <- function( num , cls){
if ( cls == "a") return( num + 10)
if ( cls == "b") return( num - 10)
}
## Example 1
df %>% 
mutate( num2 = map2_dbl( num , cls , attempt1))
## Example 2
df %>% 
mutate( num = ifelse( num <= 25 , num + 10 , num)) %>% 
mutate( num2 = map2_dbl( num , cls , attempt1))

阅读 pandas 文档以及各种 SO 帖子,我发现了多种在 python 中实现这一目标的方法,但没有一种适合我。作为参考,我在下面发布了我当前的 3 个解决方案:

import pandas as pd
import numpy as np
df = pd.DataFrame({
"num" : [14, 52 , 24 , 29],
"cls" : ["a" , "b" , "b" ,"a"]
})
### Example 1
def attempt1( num, cls):
if cls == "a":
return num + 10
if cls == "b":
return num - 10
df.assign( num2 = df.apply( lambda x: attempt1(x["num"] , x["cls"]) , axis = 1))

def attempt2( df):
if df["cls"] == "a":
return df["num"] + 10
if df["cls"] == "b":
return df["num"] - 10
df.assign( num2 = df.apply(attempt2, axis=1))

def attempt3(df):
df["num2"] = attempt1(df["num"], df["cls"])
return df
df.apply( attempt3 , axis = 1)

### Example 2
df.assign( num = np.where( df["num"] <= 25 , df["num"] + 10 , df["num"]))
.apply( attempt3 , axis = 1)

我对尝试 1 的问题是它看起来非常冗长。此外,您需要自引用回起始数据集,这意味着如果您想将多个派生链接在一起,即使您无意保留数据集,也必须将数据集写出到中间变量。

Try2 具有明显更简洁的语法,但仍然受到中间变量问题的困扰。另一个问题是,该函数需要一个数据帧,这使得该函数更难进行单元测试,不太灵活,并且不太清楚输入应该是什么。

就功能而言,Try3 对我来说似乎是最好的,因为它为您提供了清晰的可测试功能,并且不需要保存中间数据集。主要的缺点是你现在必须有 2 个感觉像冗余代码的函数。

任何帮助或建议将不胜感激。

一种有效的方法是使用pd.Series.map

df['num2'] += df['cls'].map({'a': 10, 'b': -10})

这使用字典将cls的值映射到 10 或 -10。

还有许多其他方法(参见@Guybrush的答案),但基于字典的方法对于较大的数据帧是可扩展且有效的。在我看来,它也是可读的。

相关:通过字典有效地替换熊猫系列中的值

您可以依靠Series.where来完成这项工作,方法是创建一个包含10的列,并根据cls的值将其更改为-10。然后,您可以使用该列根据需要执行算术运算。

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.where.html

分步(详细)示例:

df['what_to_add'] = 10
df['what_to_add'] = df['what_to_add'].where(df['cls'] == 'a', -10)
df['num'] = df['num'] + df['what_to_add']

假设您的两个数字相反,另一种可能性是为操作数的符号定义一列:

df['sign'] = 1 - 2 * (df['cls'] == 'a').astype(int)
df['num'] = df['num'] + df['sign'] * 10

第三种方法是使用replace,以便将 "a" 替换为 10,将 "b" 替换为 -10:

df['what_to_add'] = df['cls'].replace(['a', 'b'], [10, -10])
df['num'] = df['num'] + df['what_to_add']

已编辑: 或者,正如JPP(https://stackoverflow.com/a/49748695/4582949)所建议的那样,使用map

df['num2'] += df['cls'].map({'a': 10, 'b': -10})

最新更新