#r
#r
Вопрос:
Я пытаюсь написать цикл в R, который оказывает такое же влияние на набор данных, как следующий код:
AD1 <- subset(data, AD1==1)
AD2 <- subset(data, AD2==1)
AD3 <- subset(data, AD3==1)
AD4 <- subset(data, AD4==1)
AD5 <- subset(data, AD5==1)
После нескольких попыток я застрял:
for (i in 1:5) {
adNum <- paste("AD", i, sep="")
aSubset <- paste(adNum, " <- subset(data, ", adNum, "==1)", sep="")
aSubset
}
В идеале, я хотел бы иметь возможность применять тот же принцип для зацикливания этого кода для всех 5 подмножеств:
uAD1 <- mean(AD1$u)
vAD1 <- mean(AD1$v)
wAD1 <- mean(AD1$w)
xAD1 <- mean(AD1$x)
yAD1 <- mean(AD1$y)
zAD1 <- mean(AD1$z)
Ответ №1:
Похоже, вы находитесь на территории «разделить-применить- объединить»: вы хотите разделить большой набор данных на подмножества и применить те же операции к этим подмножествам (или разным столбцам этих подмножеств). Хотя использование зацикленного кода и проанализированной оценки не является неправильным само по себе, этот метод имеет тенденцию быть хрупким (ломается, если что-то незначительно меняется), подверженным ошибкам и трудным для чтения.
R имеет такие функции, как aggregate
и lapply/vapply
, которые полезны для этих целей. В настоящее время также существует несколько зрелых пакетов R, которые помогают пользователю выполнять именно эти типы операций (ознакомьтесь с пакетами data.table и tidyverse).
Рассмотрим набор данных «diamonds» (который поставляется с пакетами tidyverse). Первые несколько строк выглядят следующим образом:
carat cut color clarity depth table price x y z
1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
4 0.29 Premium I VS2 62.4 58 334 4.20 4.23 2.63
5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
Предположим, нам нужны средняя глубина, таблица и цена для каждого типа разреза. В базовом R вы могли бы использовать aggregate
:
data.agg <- aggregate(diamonds[c('depth', 'table', 'price')], by = diamonds['cut'], FUN = mean)
cut depth table price
1 Fair 64.04168 59.05379 4358.758
2 Good 62.36588 58.69464 3928.864
3 Very Good 61.81828 57.95615 3981.760
4 Premium 61.26467 58.74610 4584.258
5 Ideal 61.70940 55.95167 3457.542
Или использовать пакет dplyr от tidyverse для манипулирования данными:
library(dplyr)
data.dplyr <- diamonds %>%
group_by(cut) %>%
select(depth, table, price) %>%
summarize_all(mean)
cut depth table price
<ord> <dbl> <dbl> <dbl>
1 Fair 64.04168 59.05379 4358.758
2 Good 62.36588 58.69464 3928.864
3 Very Good 61.81828 57.95615 3981.760
4 Premium 61.26467 58.74610 4584.258
5 Ideal 61.70940 55.95167 3457.542
Хотя dplyr более подробен, чем базовая aggregate
функция (больше ввода), он более гибкий и позволяет выполнять более сложные операции обработки данных и суммирования. Обратите внимание, что обе версии намного более сжаты, чем альтернатива циклирования, и их легко читать и следовать.
Комментарии:
1. Это именно то, что мне было нужно. Большое спасибо!
Ответ №2:
vapply
кажется, это та функция, которая вам нужна.
Ответ №3:
Я бы предложил попробовать что-нибудь в строках следующего кода, с изменениями, если это необходимо, в зависимости от того, какую структуру вывода вы хотите.
#' subsets to list
aSubset <- lapply(1:5, FUN = function(X){
eval(parse(text = sprintf("subset(data, AD%d == 1)", X)))
})
#' assuming your original data came from a data.frame,
colNames <- c('u','v','w','x','z')
#' for each list element, calculate column means. I suggest modifying
#' the output to the structure you want, i.e. by using llply or etc
require(plyr)
adMeans <- ldply(aSubset, function(x){
colMeans(x[, colNames])
})
#' for a clearer output, these might be useful:
colnames(adMeans) <- colNames
rownames(adMeans) <- sprintf('AD%d', 1:5)