【问题标题】:How to add new calculated variables to a data frame如何将新的计算变量添加到数据框中
【发布时间】:2015-07-09 22:27:03
【问题描述】:

我想创建一个循环,现在将变量添加到数据框中。这些变量应该是现有变量的简单二次形式。 在下面的示例中,我想要 3 个新变量:dat$birds_2 <- dat$birds^2; dat$wolfs_2 <- dat$wolfs^2; dat$snakes_2 <- dat$snakes^2。我想一次对多个变量执行此操作。

dat <- read.table(text = " birds    wolfs     snakes
                    3        9         7
                    3        8         4
                    1        2         8
                    1        2         3
                    1        8         3
                    6        1         2
                    6        7         1
                    6        1         5
                    5        9         7
                    3        8         7
                    4        2         7
                    1        2         3
                    7        6         3
                    6        1         1
                    6        3         9
                    6        1         1   ",header = TRUE)

所需的输出 (dat_new) 是(我只显示前 2 行):

 dat_new                      birds    wolfs     snakes birds_2    wolfs_2     snakes_2
                                3        9         7    9        81         49  
                                3        8         4    9        64         16

【问题讨论】:

  • 你试过你的代码了吗?...(因为它很可能给你你想要的 ;-) 当然除了循环部分)
  • 我尝试了 apply 函数,但我只得到了当前变量的值。我是循环新手,所以我不知道应该如何开始。
  • 只需传递您的代码:dat$birds_2&lt;-dat$birds^2; dat$wolfs_2&lt;-dat$wolfs^2 ; dat$snakes_2&lt;-dat$snakes^2,您将在 dat 中看到 3 个新变量,它们是对应变量的平方值。这里不需要loopapply函数,^是矢量化的
  • 没有经验丰富的程序员会用循环来做这件事。它速度较慢,需要更多的代码行。尝试寻找省略循环的内置 R 函数。
  • 如果你想要的不是dat$birds_2[1]==dat$birds[1]^2等等,你应该在你的问题中添加想要的输出

标签: r loops dataframe


【解决方案1】:

作为替代方案,您也可以使用 dplyr 包轻松完成此操作:

library(dplyr)

dat_new <- dat %>% mutate_all(funs(.^2))
names(dat_new) <- paste0(names(dat_new),"_2")
dat_new <- cbind(dat,dat_new)

这给出了:

> head(dat_new)
  birds wolfs snakes birds_2 wolfs_2 snakes_2
1     3     9      7       9      81       49
2     3     8      4       9      64       16
3     1     2      8       1       4       64
4     1     2      3       1       4        9
5     1     8      3       1      64        9
6     6     1      2      36       1        4

【讨论】:

    【解决方案2】:

    如果您想获得某些变量的二次值例如变量 1 到 10 of dat,您可以这样做:

    ncol_ori <- ncol(dat)
    dat <- cbind(dat, apply(dat[, 1:10], 2, "^", 2)) # or actually just cbind(dat, dat[, 1:10]^2)
    colnames(dat)[(ncol_ori+1):ncol(dat)] <- paste(colnames(dat)[1:10], 2, sep="_")
    

    如果您想对原始 data.frame 的所有变量执行此操作,您可以这样做:

    ncol_ori <- ncol(dat)
    dat <- cbind(dat, apply(dat, 2, "^", 2)) # or just cbind(dat, dat^2)
    colnames(dat)[(ncol_ori+1):ncol(dat)] <- paste(colnames(dat)[1:ncol_ori], 2, sep="_")
    

    使用您的dat,您将获得:

    head(dat)
    #   birds wolfs snakes birds_2 wolfs_2 snakes_2
    #1      3     9      7       9      81       49
    #2      3     8      4       9      64       16
    #3      1     2      8       1       4       64
    #4      1     2      3       1       4        9
    #5      1     8      3       1      64        9
    #6      6     1      2      36       1        4
    

    【讨论】:

    • 这很好,但是如何更改新变量名称,例如在名称末尾添加“_2”?
    • @mql4beginner,我添加了 2 行,因此您可以使用新变量命名列,最后使用“_2”
    • 您好@CathG,我发现 dat 出现错误 [.data.frame(dat, , 1:10) 中:未定义的列被选中”
    • @mql4beginner,可能是因为您在 dat 中的列数少于 10?如果是这样,请将 1:10 替换为您想要二次值的列的索引。如果您希望所有列都使用它,您可以使用我的答案中的第二段代码
    • 是的,我更改了正确的列数,现在它正在工作..谢谢
    【解决方案3】:

    setNames

    setNames(as.data.frame(cbind(dat, dat^2)), c(names(dat), paste0(names(dat),'_2')))
    
    #   birds wolfs snakes birds_2 wolfs_2 snakes_2
    #1      3     9      7       9      81       49
    #2      3     8      4       9      64       16
    

    【讨论】:

      【解决方案4】:

      使用data.table的选项

      library(data.table)
      setDT(dat)[, paste0(names(dat),"_2") := lapply(.SD, '^', 2)]
      head(dat,2)
      #   birds wolfs snakes birds_2 wolfs_2 snakes_2
      #1:     3     9      7       9      81       49
      #2:     3     8      4       9      64       16
      

      或者你可以使用set(这样会更有效率),因为有多个列

      setDT(dat)
      dat_new <- copy(dat)
      for(j in 1:ncol(dat_new)){
         set(dat_new, i=NULL, j=j, value=dat_new[[j]]^2)
       }
       cbind(dat, dat_new)
      

      基准测试

      set.seed(24)
      dat <- as.data.frame(matrix(sample(0:20, 1e6*200, replace=TRUE), 
             ncol=200))
      
      dat1 <- copy(dat)
      dat2 <- copy(dat)
      
      
      Colonel <- function() { setNames(as.data.frame(cbind(dat, dat^2)),
          c(names(dat), paste0(names(dat),'_2')))}
      akrun1 <- function() {setDT(dat1)[, paste0(names(dat1),"_2") := 
                  lapply(.SD, '^', 2)]}
      akrun2 <- function() {setDT(dat2)
                        dat_new <- copy(dat2)
                        for(j in 1:ncol(dat_new)){
                            set(dat_new, i=NULL, j=j, value=dat_new[[j]]^2)
                         }
                        cbind(dat2, dat_new)}
      
      jaap <- function() {dat_new <- dat %>% 
                           mutate_each(funs(.^2))
                      names(dat_new) <- paste0(names(dat_new),"_2")
                      dat_new <- cbind(dat,dat_new)}
      
      
      
       cathG <- function() {ncol_ori <- ncol(dat)
                      datN <- cbind(dat, apply(dat, 2, "^", 2))
                      colnames(datN)[(ncol_ori+1):ncol(datN)] <- 
                  paste(colnames(datN)[1:ncol_ori], 2, sep="_")
      
           }
      
      system.time(Colonel())
      #   user  system elapsed 
      #  5.589   1.472  46.843 
      
       system.time(akrun1())
       #   user  system elapsed 
       #  2.125   1.238  10.065 
      
      system.time(akrun2())
      #   user  system elapsed 
      #  1.522   0.744   3.922 
      
      system.time(jaap())
      #   user  system elapsed 
      #  1.597   0.926  11.153 
      
      system.time(cathG())
      #   user  system elapsed 
      #  9.386   3.536  94.360 
      

      【讨论】:

      • 有趣你真的很喜欢基准测试!当然as.data.frame 转换需要时间。我修改为data.frame
      • @ColonelBeauvel 我认为as.data.frame 更快
      【解决方案5】:

      您可以执行以下操作(不使用循环):

      dat_2 <- dat^2
      colnames(dat_2) <- paste0(colnames(dat),"_2")
      dat_tot <- cbind(dat, dat_2)
      

      【讨论】:

      • 我很抱歉造成误解。我的意思是我想得到一个每个原始而不是每列的二次值的变量。
      • 然后尝试rbind() 而不是cbind()。但是,我不确定我是否理解了编辑后的问题。
      • 上面的代码与您的输出示例完全相同
      • 你是对的,但是如何更改新的变量名称?
      猜你喜欢
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-29
      • 1970-01-01
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      相关资源
      最近更新 更多