Подтвердить что ты не робот

Округление выбранных столбцов data.table в 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

Есть ли лучший метод (более короткий код)? Спасибо за вашу помощь.

4b9b3361

Ответ 1

Если вы не возражаете переписать свой оригинальный 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

Использование 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

Ответ 3

Учитывая, что dplyr::mutate_each устарела, используйте mutate_if с дополнительным преимуществом округления столбца, только если он числовой

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

Ответ 4

требуется (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

Ответ 5

Начиная с dplyr 0.8.0, funs() является мягким устаревшим. Это означает, что вместо funs(name = f(.)) следует использовать list(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

Однако в этом случае нет необходимости использовать какие-либо из них (поскольку существует только одна функция), и подход из @Arthur Yip можно применять ко всем сценариям.

Выбор столбцов явно по их именам:

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

Или выберите столбцы, которые начинаются с vnum:

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

Или выберите столбцы, содержащие vnum:

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

Или выберите столбцы, которые соответствуют vnum:

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

Или исключить столбец явно по его имени:

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

Или исключая соответствующие столбцы vch:

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

Или выбрав первые два столбца:

mydf %>% 
 mutate_at(1:2, round, 1)

Или исключая третий столбец:

mydf %>% 
 mutate_at(-3, round, 1)

Ответ 6

Короче говоря:

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? Кроме того, некоторые объяснения относительно того, как он работает, будут очень полезны

Он группируется округленными столбцами, используя "by =" 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

Ответ 7

Я думаю, что из решений, который Стивен Баупре использует 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 десятичного знака.