在R中舍入选定的data.table列

时间:2014-12-23 02:35:26

标签: r data.table

我有以下数据和代码来舍入此数据的选定列。表:

> dput(mydf)
structure(list(vnum1 = c(0.590165705411504, -1.39939534199836, 
0.720226053660755, -0.253198380120377, -0.783366825121657), vnum2 = c(0.706508400384337, 
0.526770398486406, 0.863136084517464, 0.838245498016477, 0.556775856064633
), vch1 = structure(c(2L, 4L, 1L, 3L, 3L), .Label = c("A", "B", 
"C", "E"), class = "factor")), .Names = c("vnum1", "vnum2", "vch1"
), row.names = c(NA, -5L), class = c("data.table", "data.frame"
))

> mydf[,round(.SD,1),]
Error in Math.data.frame(list(vnum1 = c(0.590165705411504, -1.39939534199836,  : 
  non-numeric variable in data frame: vch1

> cbind(mydf[,3,with=F], mydf[,1:2,with=F][,round(.SD,1),])
   vch1 vnum1 vnum2
1:    B   0.6   0.7
2:    E  -1.4   0.5
3:    A   0.7   0.9
4:    C  -0.3   0.8
5:    C  -0.8   0.6

有更好的方法(更短的代码)吗?谢谢你的帮助。

8 个答案:

答案 0 :(得分:43)

使用dplyr

如果您想一次舍入多个列:

mydf %>% mutate_at(vars(vnum1, vnum2), funs(round(., 1)))

或者,如果要更改“vch1”以外的所有列:

mydf %>% mutate_at(vars(-vch1), funs(round(., 1)))

或者,如果要更改以“vnum”开头的所有列:

mydf %>% mutate_at(vars(starts_with("vnum")), funs(round(., 1)))

或者,如果您只想更改数字列:

mydf %>% mutate_if(is.numeric, ~round(., 1))

你得到:

  vnum1 vnum2 vch1
1   0.6   0.7    B
2  -1.4   0.5    E
3   0.7   0.9    A
4  -0.3   0.8    C
5  -0.8   0.6    C

答案 1 :(得分:26)

如果您不介意覆盖原始mydf

cols <- names(mydf)[1:2]
mydf[,(cols) := round(.SD,1), .SDcols=cols]
mydf

#   vnum1 vnum2 vch1
#1:   0.6   0.7    B
#2:  -1.4   0.5    E
#3:   0.7   0.9    A
#4:  -0.3   0.8    C
#5:  -0.8   0.6    C

答案 2 :(得分:23)

鉴于dplyr::mutate_each已被弃用,请使用mutate_if,只有在数字为数字时才能将列舍入为额外好处

mydf %>% mutate_if(is.numeric, round, 1)

答案 3 :(得分:6)

需要(data.table)

简短而明确的解决方案:

mydf[, lapply(.SD, round, 1), vch1]

#   vch1 vnum1 vnum2
#1:    B   0.6   0.7
#2:    E  -1.4   0.5
#3:    A   0.7   0.9
#4:    C  -0.3   0.8
#5:    C  -0.8   0.6

相同,但有描述性细节:

mydf[, lapply(.SD, round, digits = 1), by = vch1]
  

如果我有很多列,请说:(vnum1,vnum2,vch1,vch2,vbin1,vbin2,vbin3),我想只舍入vnum1和vnum2?

在这种情况下,您可以使用:=运算符和.SDcols =参数指定要舍入的列:

mydf[, 1:2 := lapply(.SD, round, digits = 1), by = vch1]

如果您需要对某些列进行舍入并从输出中排除其他列,则可以使用.SDcols =参数同时执行这两个操作:

mydf[, lapply(.SD, round, digits = 1), by = vch1, .SDcols = "vnum1"]

.SDcols =可以提供列名或其编号,
作为名称.SDcols = "vnum1"或数字.SDcols = 1的单个列 作为名称.SDcols = c("vnum2", "vnum1")或数字.SDcols = c(2, 1)的多列 列为名称.SDcols = vnum1:vnum2或数字.SDcols = 1:2

的列

答案 4 :(得分:2)

截至dplyr 0.8.0,funs()soft deprecated。这意味着应该使用list(name = ~f(.))而不是funs(name = f(.))

这里可以用作(通过其名称显式选择列):

mydf %>% 
 mutate_at(vars(vnum1, vnum2), list(~ round(., 1))) 

  vnum1 vnum2 vch1
1   0.6   0.7    B
2  -1.4   0.5    E
3   0.7   0.9    A
4  -0.3   0.8    C
5  -0.8   0.6    C

或(选择以vnum开头的列)

mydf %>% 
 mutate_at(vars(starts_with("vnum")), list(~ round(., 1)))

或(选择包含vnum的列)

mydf %>% 
 mutate_at(vars(contains("vnum")), list(~ round(., 1)))

或(选择与vnum匹配的列):

mydf %>% 
 mutate_at(vars(matches("vnum")), list(~ round(., 1)))

或(按名称明确排除列):

mydf %>% 
 mutate_at(vars(-vch1), list(~ round(., 1)))

或(不包括与vch匹配的列):

mydf %>% 
 mutate_at(vars(-matches("vch")), list(~ round(., 1)))

或(选择前两列):

mydf %>% 
 mutate_at(1:2, list(~ round(., 1)))

或(不包括第三栏):

mydf %>% 
 mutate_at(-3, list(~ round(., 1)))

或(如果列为数字,则执行操作):

mydf %>% 
 mutate_if(is.numeric, list(~ round(., 1)))

答案 5 :(得分:1)

到目前为止最短:

mydf[, vch1, round(mydf[, 1:2], 1)]

#   vnum1 vnum2 vch1
#1:   0.6   0.7    B
#2:  -1.4   0.5    E
#3:   0.7   0.9    A
#4:  -0.3   0.8    C
#5:  -0.8   0.6    C
  

有趣的方法。但是,如果我有很多列,比如说:(vnum1,vnum2,vch1,vch2,vbin1,vbin2,vbin3),我想只舍入vnum1和vnum2?此外,关于它如何工作的一些解释将是非常有用的

使用&#34; by =&#34;按舍入列进行分组data.table。

以下是基于此方法解决二级任务的示例。

内置数据集:

>dt <- data.table(names = rownames(datasets::ability.cov$cov), datasets::ability.cov$cov)
>dt
#     names general picture  blocks   maze reading   vocab
#1: general  24.641   5.991  33.520  6.023  20.755  29.701
#2: picture   5.991   6.700  18.137  1.782   4.936   7.204
#3:  blocks  33.520  18.137 149.831 19.424  31.430  50.753
#4:    maze   6.023   1.782  19.424 12.711   4.757   9.075
#5: reading  20.755   4.936  31.430  4.757  52.604  66.762
#6:   vocab  29.701   7.204  50.753  9.075  66.762 135.292

简短解决方案:

> dt_round <- dt[, .SD, by = round(dt[, blocks:maze], 1)]
> dt_round
#   blocks maze   names general picture reading   vocab
#1:   33.5  6.0 general  24.641   5.991  20.755  29.701
#2:   18.1  1.8 picture   5.991   6.700   4.936   7.204
#3:  149.8 19.4  blocks  33.520  18.137  31.430  50.753
#4:   19.4 12.7    maze   6.023   1.782   4.757   9.075
#5:   31.4  4.8 reading  20.755   4.936  52.604  66.762
#6:   50.8  9.1   vocab  29.701   7.204  66.762 135.292

初始列顺序:

> whatever <- setcolorder(dt_round, names(dt))
> whatever
#     names general picture blocks maze reading   vocab
#1: general  24.641   5.991   33.5  6.0  20.755  29.701
#2: picture   5.991   6.700   18.1  1.8   4.936   7.204
#3:  blocks  33.520  18.137  149.8 19.4  31.430  50.753
#4:    maze   6.023   1.782   19.4 12.7   4.757   9.075
#5: reading  20.755   4.936   31.4  4.8  52.604  66.762
#6:   vocab  29.701   7.204   50.8  9.1  66.762 135.292

答案 6 :(得分:1)

如果您希望能够返回副本,则可以使用一个函数

功能:

auto_round_dt<- function(dt, ndigits=3, return_copy=TRUE){
  dt<- data.table::setDT(dt)
  roundme<- names(sapply(dt, class))[which(sapply(dt, class) == "numeric")]
  if(return_copy == TRUE){
    tmp<- data.table::copy(dt)
    out<- tmp[, (roundme):=round(.SD, ndigits), .SDcols=roundme]
    return(out)
  } else{
    return(dt[, (roundme):=round(.SD, ndigits), .SDcols=roundme])
  }
}

用法

要返回表的副本而不修改原始表:

newdt<- auto_round_dt(dt=mydt, ndigits = 3, return_copy = TRUE)

并在适当位置修改对象:

auto_round_dt(dt=mydt, ndigits = 3, return_copy = FALSE)

注意:如果将auto_round_dt设置为data.table,则不必将return_copy=的结果分配给新的FALSE

答案 7 :(得分:0)

我认为,从解决方案来看,Steven Baupre使用dplyr的方法是最优雅的,可以选择性地应用于数据帧中的不同列,特别是在计算物理中。

library(dplyr)
gasCriticals %>%
  mutate_each(funs(round(., 0)), depth, pres, temp) %>%
  mutate_each(funs(round(., 2)), pres.pr, temp.pr, temp.r) %>%
  mutate_each(funs(round(., 1)), pres.pc, temp.pc)

如您所见,压力和温度将四舍五入为0位小数;假减压和温度为2分钟;最后,伪临界压力和温度为1位小数。