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

时间:2018-04-10 08:08:01

标签: python pandas dataframe

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

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

作为参考,这是我将如何在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的问题是它看起来非常可怕。此外,您需要自我引用回到起始数据集,这意味着如果您想要将多个派生链接在一起,则必须将数据集写出到中间变量,即使您无意保留它。

Attempt2具有明显更清晰的语法,但仍然存在中间变量问题。另一个问题是该函数需要一个数据帧,这使得函数更难以进行单元测试,灵活性降低,输入应该更加清晰。

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

非常感谢任何帮助或建议。

2 个答案:

答案 0 :(得分:1)

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

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,以便替换&#34; a&#34;到10和&#34; b&#34;按-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})

答案 1 :(得分:1)

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

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

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

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

相关:Replace values in a pandas series via dictionary efficiently