【问题标题】:Modify multiple columns of dataset by the same function in R通过R中的相同函数修改数据集的多列
【发布时间】:2016-10-05 01:38:29
【问题描述】:

这是一个生成data.frame的代码:

ref_variables=LETTERS[1:10]
row=100
d0=seq(1:100)
for (i in seq_along(ref_variables)){
  dtemp=sample(seq(1:row),row,TRUE)
  d0=data.frame(d0,dtemp)
}
d0[,1]=NULL
names(d0)=ref_variables

我有一个数据集,data.frame 或 data.table,等等。 假设我想通过将每列除以第一个来修改第 2 到 4 列。当然,我可以这样循环:

columns_name_to_divide=c("B","C","H")
column_divisor="A"
for (i in seq_along(columns_name_to_divide)){        
  ds[columns_name_to_divide[i]] = ds[columns_name_to_divide[i]] / ds[column_divisor]
}

但是有没有更优雅的方法呢?

【问题讨论】:

    标签: r function loops dataframe apply


    【解决方案1】:
      > d0[2:4] <- d0[,2:4]/d0[,1]  
    

    这会将您的原始值替换为将第 2、3、4 列除以第 1 列后得到的结果。其余部分将保持不变。

    如果您想在将第 2、3、4 列除以第 1 列后,在 d0 中创建 3 个新列,这不会替换第 2、3 和 4 列中的原始值。计算的值将是分别在第 11,12 和 13 列中。

      > dim(d0)
      # [1] 100  10
      > d0[11:13] <- d0[,2:4]/d0[,1]
      > dim(d0)
      # [1] 100  13
    

    要对新值进行四舍五入,您只需将 round() 函数添加到小数点后 2 位,如下所示:

      > d0[2:4] <- round(d0[,2:4]/d0[,1],2)  # Original values subtituted at 2,3,4
    
      # OR
    
      > d0[11:13] <- round(d0[,2:4]/d0[,1],2)  # New columns added, original columns are untouched.
    

    【讨论】:

    • 不要认为这个有效,会产生错误:'/' 只为相同大小的数据帧定义。示例:d[,c(25, 26)]
    【解决方案2】:

    我们可以使用data.table 中的set,这样可以提高效率,因为在多次调用时避免了.[data.table 的开销(尽管在这种情况下不是这样)。

    library(data.table)
    setDT(d0)
    for(j in columns_name_to_divide){
       set(d0, i = NULL, j = j, value = d0[[j]]/d0[[column_divisor]])
    }
    

    或使用lapply

    setDT(d0)[, (columns_name_to_divide) := lapply(.SD, `/`, 
                  d0[[column_divisor]]), .SDcols = columns_name_to_divide]
    

    或者使用dplyr的优雅选项

    library(dplyr)
    library(magrittr)
    d0 %<>%
        mutate_each_(funs(./d0[[column_divisor]]), columns_name_to_divide)
    head(d0)
    #  A         B         C  D  E  F  G        H  I  J
    #1 60 0.4000000 1.1500000  6 86 27 19 0.150000 94 97
    #2 11 0.6363636 0.3636364 25 52 44 82 8.818182 84 68
    #3 80 0.8750000 1.1375000 72 34 56 69 0.125000 34 17
    #4 77 0.3116883 1.0259740  9 44 87 61 1.064935 79 40
    #5 18 0.3333333 5.0555556 60 69 62 89 2.166667 21 34
    #6 42 1.3333333 2.3095238 61 20 87 95 1.428571 78 63
    

    基准测试

    set.seed(42)
    d1 <- as.data.frame(matrix(sample(1:9, 1e7*7, replace=TRUE), ncol=7))
    
    d2 <- copy(d1)
    d3 <- copy(d1)
    
    system.time({
    d2 %<>%
       mutate_each(funs(./d2[["V2"]]), V4:V7)
    })
    # user  system elapsed 
    #   0.52    0.39    0.91 
    
    system.time({
    d1[,4:7] <- d1[,4:7]/d1[,2]
    })
    #   user  system elapsed 
    #   1.72    0.72    2.44 
    
    
    system.time({
    
    setDT(d3)
    for(j in 4:7){
       set(d3, i = NULL, j = j, value = d3[[j]]/d3[["V2"]])
    }
    
    })
    #  user  system elapsed 
    #   0.32    0.16    0.47 
    

    【讨论】:

    • [.data.table 开销(通常)在多次调用时发挥作用。它只会在这里被调用一次。
    【解决方案3】:

    你可以这样做:

    library(data.table)
    
    
    cols <- names(df)[2:4]
    col1 <- names(df)[1]
    
    setDT(df)[, (cols) := lapply (cols, function(x)  get(x) / get(col1) )]
    
    
    # sample data for reproducible example:
    df <- data.frame(V1=rep(10,5),
                     V2=rep(20,5),
                     V3=rep(30,5),
                     V4=rep(40,5),
                     V5=rep(50,5))
    

    【讨论】:

    • 从下一个版本开始,您可以:setDT(df)[, (cols) := lapply (.SD, function(x) x * V1), .SDcols=cols]It has been fixed recently.
    猜你喜欢
    • 2020-01-12
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多