Как подмножество данных в цикле в R?

#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)