【问题标题】:How to dynamically "mutate" a data frame in R?如何在 R 中动态“变异”数据框?
【发布时间】:2016-06-19 02:37:18
【问题描述】:

假设我想在数据框中记录转换列,例如 iris 数据并为每个所需列动态创建后缀为 _log 的新列。

我想要实现的是:

df$Sepal.Length_log <- log (df$Sepal.Length)
df$Sepal.Width_log <- log (df$Sepal.Width)
df$Petal.Length_log <- log (df$Petal.Length)
df$Sepal.Width_log <- log (df$Sepal.Width)  

但是当您的数据有许多列要转换时,这将是一项繁琐的任务,所以我想使用循环和 dplyr 包的 mutate 函数动态实现这一点,我不成功 天真 试验是:

library (dplyr)
data(iris)
varLabel <- c('Sepal.Length','Sepal.Width','Petal.Length','Petal.Width')
for (i in 1:length (varLabel)) {
  varNew <- paste (varLabel[i],'log',sep='_')
  iris <- dplyr::mutate (iris,varNew=log (varLabel[i])) # problem arises here
}

我收到此错误:Error: non-numeric argument to mathematical function

我搜索了一个解决方案,最相关的似乎是这个tutorial 标准和非标准评估,这个postthat 也是一个,但我不知道如何借用从那里解决。任何帮助将不胜感激。

注意:
我想在数据集中同时拥有新旧列。

【问题讨论】:

    标签: r loops dplyr


    【解决方案1】:

    试试这个

    logiris<-data.frame(lapply(varLabel,function(x){log(iris[,x])}))
    names(logiris)<-paste0("Log-",varLabel)
    iris<-cbind(iris,logiris) 
    

    【讨论】:

    • 太棒了!不需要额外的包,只需几行代码即可完成工作!
    【解决方案2】:

    data.table 的解决方案:

    library(data.table)
    data(iris)
    DT <- as.data.table(iris)
    varLabel <- c('Sepal.Length','Sepal.Width','Petal.Length','Petal.Width')
    
    NewColumn <- paste0(varLabel, "_log")
    
    DT[, (NewColumn) := lapply(.SD, log), .SDcols = varLabel]
    
    DT
    #>      Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
    #>   1:          5.1         3.5          1.4         0.2    setosa
    #>   2:          4.9         3.0          1.4         0.2    setosa
    #>   3:          4.7         3.2          1.3         0.2    setosa
    #>   4:          4.6         3.1          1.5         0.2    setosa
    #>   5:          5.0         3.6          1.4         0.2    setosa
    #>  ---                                                            
    #> 146:          6.7         3.0          5.2         2.3 virginica
    #> 147:          6.3         2.5          5.0         1.9 virginica
    #> 148:          6.5         3.0          5.2         2.0 virginica
    #> 149:          6.2         3.4          5.4         2.3 virginica
    #> 150:          5.9         3.0          5.1         1.8 virginica
    #>      Sepal.Length_log Sepal.Width_log Petal.Length_log Petal.Width_log
    #>   1:         1.629241       1.2527630        0.3364722      -1.6094379
    #>   2:         1.589235       1.0986123        0.3364722      -1.6094379
    #>   3:         1.547563       1.1631508        0.2623643      -1.6094379
    #>   4:         1.526056       1.1314021        0.4054651      -1.6094379
    #>   5:         1.609438       1.2809338        0.3364722      -1.6094379
    #>  ---                                                                  
    #> 146:         1.902108       1.0986123        1.6486586       0.8329091
    #> 147:         1.840550       0.9162907        1.6094379       0.6418539
    #> 148:         1.871802       1.0986123        1.6486586       0.6931472
    #> 149:         1.824549       1.2237754        1.6863990       0.8329091
    #> 150:         1.774952       1.0986123        1.6292405       0.5877867
    

    dplyrmutate_each 的简短解决方案。只需使用命名向量来保留所有变量

    library(dplyr)
    data(iris)
    varLabel <- c('Sepal.Length','Sepal.Width','Petal.Length','Petal.Width')
    names(varLabel) <- paste0(varLabel,'_log')
    
    res <- iris %>% mutate_each_(funs(log(.)), vars = varLabel)
    head(res)
    #>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
    #> 1          5.1         3.5          1.4         0.2  setosa
    #> 2          4.9         3.0          1.4         0.2  setosa
    #> 3          4.7         3.2          1.3         0.2  setosa
    #> 4          4.6         3.1          1.5         0.2  setosa
    #> 5          5.0         3.6          1.4         0.2  setosa
    #> 6          5.4         3.9          1.7         0.4  setosa
    #>   Sepal.Length_log Sepal.Width_log Petal.Length_log Petal.Width_log
    #> 1         1.629241        1.252763        0.3364722      -1.6094379
    #> 2         1.589235        1.098612        0.3364722      -1.6094379
    #> 3         1.547563        1.163151        0.2623643      -1.6094379
    #> 4         1.526056        1.131402        0.4054651      -1.6094379
    #> 5         1.609438        1.280934        0.3364722      -1.6094379
    #> 6         1.686399        1.360977        0.5306283      -0.9162907
    

    【讨论】:

    • 这似乎是另一个优雅的解决方案,但对我不利,我不熟悉data.table 词典。
    • nm1 &lt;- paste0(varLabel,'_log')names(varLabel) &lt;- nm1 还可以缩短为 names(varLabel) &lt;- paste0(varLabel,'_log')
    【解决方案3】:

    我们可以使用mutate_each

     nm1 <- paste0("varNew_", varLabel)
     res <- iris %>% 
               mutate_each_(funs(log(.)), varLabel) %>% 
               setNames(., c(nm1, setdiff(names(.), varLabel))) %>% 
               bind_cols(iris[intersect(names(iris), varLabel)], .)
    
    head(res,2)
    #Source: local data frame [2 x 9]
    
    #  Sepal.Length Sepal.Width Petal.Length Petal.Width varNew_Sepal.Length varNew_Sepal.Width varNew_Petal.Length varNew_Petal.Width Species
    #         (dbl)       (dbl)        (dbl)       (dbl)               (dbl)              (dbl)               (dbl)              (dbl)  (fctr)
    #1          5.1         3.5          1.4         0.2            1.629241           1.252763           0.3364722          -1.609438  setosa
    #2          4.9         3.0          1.4         0.2            1.589235           1.098612           0.3364722          -1.609438  setosa
    

    如果 OP 正在寻找 base R 解决方案,这也可以工作

    iris[nm1] <- log(iris[varLabel])
    head(iris,2)
    # Sepal.Length Sepal.Width Petal.Length Petal.Width Species varNew_Sepal.Length
    #1          5.1         3.5          1.4         0.2  setosa            1.629241
    #2          4.9         3.0          1.4         0.2  setosa            1.589235
    #  varNew_Sepal.Width varNew_Petal.Length varNew_Petal.Width
    #1           1.252763           0.3364722          -1.609438
    #2           1.098612           0.3364722          -1.609438
    

    【讨论】:

    • 抱歉,我可能不够清楚,无法在帖子中声明这个意图,还想保留数据集中的原始列,因为我也需要它们。
    • 我在您的回答中发现了许多惊喜:1. 不需要 for 循环 2. mutate_each 有一个 vars= 参数来重命名变量。你能解释一下setNames这行代码的功能吗?我知道. 的意思是iris
    • @doctorate mutate_each 返回的列名与原始列名相同。所以我使用setNames 将其更改为包含前缀,如'nm1'。它可以在没有包的情况下轻松完成,也可以在其他包中完成。但是,我认为问题是关于 dplyr。
    • @doctorate 添加了另一个基础 R 解决方案
    猜你喜欢
    • 2023-04-05
    • 1970-01-01
    • 2022-01-02
    • 1970-01-01
    • 2020-04-18
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多