R Как я могу вычислить групповые средние значения для списка кадров данных, используя разные условия подмножества для вычисления каждого среднего значения?

У меня есть список из трех фреймов данных, и я хотел бы создать еще один список из трех фреймов данных, строки которого состоят из каждого из значений группирующей переменной (g1) и среднего значения шести переменных по переменной g1. Суть в том, что я хотел бы вычислять средние значения для трех непрерывных переменных только тогда, когда значение соответствующей фиктивной переменной равно 1.

Воспроизводимый пример:

    a <- data.frame(c("fj","fj","fj","a","fj","a","g","g","g","g"),c(1,1,1,1,0,0,0,1,0,0),c(0,0,1,0,1,0,0,1,0,1),c(0,0,0,1,0,0,1,1,0,0),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
b <- data.frame(c("fj","a","fj","a","fj","fj","fj","g","g","g"),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
c <- data.frame(c("fj","fj","fj","a","fj","a","g","g","g","g"),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
u <- list(a,b,c)
u <- lapply(u, setNames, nm = c('g1','dummy1','dummy2','dummy3','contin1','contin2','contin3'))

u[[1]]

> u
[[1]]
   g1 dummy1 dummy2 dummy3  contin1 contin2 contin3
1  fj      1      0      0       199      18      61
2  fj      1      0      0        91     158      28
3  fj      1      1      0       147      67     190
4   a      1      0      1       181     105      22
5  fj      0      1      0        14      16     156
6   a      0      0      0       178      14      98
7   g      0      0      1       116      97      30
8   g      1      1      1        48      31     144
9   g      0      0      0        60      21     112
10  g      0      1      0        95     145     199

Я хотел бы вычислить среднее значение contin1 только при dummy1 = 1, среднее значение contin2 только при dummy2 = 1 и среднее значение contin3 только при dummy3 = 1

Вывод, который я ХОЧУ для первого списка:

> rates
[[1]]
  x[, 1]   V1  V2  V3 x[, 1] x[, 6] x[, 1] x[, 7] x[, 1] x[, 8]
1      a 0.50 0.0 0.5      a 181         a  NA         a  22
2     fj 0.75 0.5 0.0     fj 145.67     fj  41.5      fj  NA
3      g 0.25 0.5 0.5      g  48         g  88         g  87

Что я пробовал:

rates <- lapply(u, function(x) {
    cbind(aggregate(cbind(x[,2],x[,3],x[,4]) ~ x[,1], FUN = mean, na.action = NULL),
    aggregate(x[,6] ~ x[,1], FUN = mean, na.action = NULL, subset = (x[,2] == 1)),
    aggregate(x[,7] ~ x[,1], FUN = mean, na.action = NULL, subset = (x[,3] == 1)),
    aggregate(x[,8] ~ x[,1], FUN = mean, na.action = NULL, subset = (x[,4] == 1)))
    })
Error in data.frame(..., check.names = FALSE) : 
  arguments imply differing number of rows: 3, 2

Я понимаю, что эта ошибка исходит от cbind, потому что cbind терпит неудачу всякий раз, когда вы пытаетесь связать объекты с разным количеством строк. (Столбец x[, 6] имеет три строки, а x[, 7] и x[, 8] — две.) Наверное, я надеялся, что у агрегата есть какой-то способ сохранить одну строку для каждой группирующей переменной, что означало бы что у меня будет такое же количество строк, и cbind будет работать. Возможно, это невозможно в соответствии с документацией R?: «Строки с отсутствующими значениями в любой из переменных by будут исключены из результата».

Я внимательно прочитал документацию по агрегату. В следующих двух сообщениях рассматриваются аналогичные вопросы, но не используются разные подмножества данных для расчета средних значений.

R: вычислить средства для подмножества группы и Средства из списка фреймов данных в R

Любые предложения будут очень признательны.


person IamWill    schedule 22.03.2015    source источник
comment
Я бы предложил заменить 0 в манекенах на NA, затем умножить значения continX на соответствующие значения dummyX, а затем использовать na.rm=T в агрегатной функции. (И также предложил бы dplyr, если вы специально не ищете базовое решение r.)   -  person JonMinton    schedule 23.03.2015
comment
@JonMinton: ваша идея работает до тех пор, пока ни один из фиктивных столбцов не содержит все нули для одной из групп, что является проблемой, с которой я сталкиваюсь в своем коде.   -  person IamWill    schedule 23.03.2015


Ответы (2)


Если у вас установлен dplyr, следующий код решит вашу проблему.

library(dplyr)

set.seed(1234)

a <- data.frame(c("fj","fj","fj","a","fj","a","g","g","g","g"),c(1,1,1,1,0,0,0,1,0,0),c(0,0,1,0,1,0,0,1,0,1),c(0,0,0,1,0,0,1,1,0,0),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
b <- data.frame(c("fj","a","fj","a","fj","fj","fj","g","g","g"),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
c <- data.frame(c("fj","fj","fj","a","fj","a","g","g","g","g"),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 0, max = 2)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)),floor(runif(10, min = 10, max = 200)))
u <- list(a,b,c)
u <- lapply(u, setNames, nm = c('g1','dummy1','dummy2','dummy3','contin1','contin2','contin3'))


rates <- lapply(u, function(x)
  x %>% 
    mutate( contin1_ = ifelse(dummy1==1, contin1, NA) ) %>%
    mutate( contin2_ = ifelse(dummy2==1, contin2, NA) ) %>%
    mutate( contin3_ = ifelse(dummy3==1, contin3, NA) ) %>%
    group_by(g1) %>%
    summarize( 
              V1 = mean(dummy1, na.rm=TRUE),
              V2 = mean(dummy2, na.rm=TRUE),
              V3 = mean(dummy3, na.rm=TRUE),
              mean1 = mean(contin1_, na.rm=TRUE),
              mean2 = mean(contin2_, na.rm=TRUE),
              mean3 = mean(contin3_, na.rm=TRUE)
               )
)

print(rates[[1]])

Что дает мне это:

Source: local data frame [3 x 7]

  g1   V1  V2  V3     mean1 mean2 mean3
1  a 0.50 0.0 0.5 128.00000   NaN    17
2 fj 0.75 0.5 0.0  94.66667    64   NaN
3  g 0.25 0.5 0.5  54.00000    57   146

Число, которое я получаю, кажется примерно правильным, и NA находятся во всех нужных местах. К сожалению, ваш пример воспроизводим не полностью, поскольку вы не указали начальное число для генерации случайных переменных, и поэтому мой runif дает мне значения, отличные от ваших.

person akhmed    schedule 23.03.2015
comment
Спасибо. Работает отлично. С промежутком, не устанавливая семя :) - person IamWill; 23.03.2015

Другим вариантом может быть изменение формата с «широкого» на «длинный» и повторное преобразование обратно в «широкий» после получения «средних» значений. Для столбцов с несколькими значениями это теперь возможно с melt, dcast из версии devel data.table, т.е. v1.9.5. Его можно установить с here. (Использован тот же набор данных из поста @akhmed).

Мы можем melt установить наборы данных в списке ("u"), указав индекс столбцов ("dummy" и "contin") в measure.vars в виде списка. Получите среднее значение столбцов «dummy» и «contin», сгруппированных по «g1» и «variable» (созданный из «melt»), dcast от long до wide, указав value.vars как «dummyMean» и «continMean». .

 res <-  lapply(u, function(x) {
   x1 <- melt(setDT(x), measure.vars=list(2:4,5:7),
                        value.name=c('dummy', 'contin'))
   x2 <- x1[, list(dummyMean = mean(dummy, na.rm=TRUE),
             continMean = mean(contin[dummy==1], na.rm=TRUE)), 
                           by=list(g1, variable)]

  dcast(x2, g1~variable, value.var=c('dummyMean', 'continMean'))})

 res[[1]]
 #   g1 1_dummyMean 2_dummyMean 3_dummyMean 1_continMean 2_continMean
 #1:  a        0.50         0.0         0.5    128.00000          NaN
 #2: fj        0.75         0.5         0.0     94.66667           64
 #3:  g        0.25         0.5         0.5     54.00000           57
 #    3_continMean
 #1:           17
 #2:          NaN
 #3:          146

Или вариант base R с использованием Map. Созданы функции «fdummy», «fcontin» для подмножества столбцов «dummy» и «contin». Цикл через 'u' (lapply(...)). Используйте Map, чтобы получить соответствующие столбцы "dummy" и "contin", сгруппированные по столбцу "g1", получите mean из "dummy" и mean из "contin" столбцов с "dummy==1", используя tapply, cbind результаты. .

 fdummy <- function(x) x[grep('dummy', names(x))]
 fcontin <- function(x) x[grep('contin', names(x))]
 res2 <- lapply(u, function(x) {
        do.call(cbind.data.frame,
           Map(function(x,y,z) cbind(tapply(x,z, FUN=mean), 
                              tapply(y[x==1],z[x==1], FUN=mean)), 
                             fdummy(x), fcontin(x), x['g1']))})


lapply(res2, setNames, c(rbind(paste0('dummyMean', 1:3), 
                    paste0('continMean',1:3))))[[1]]
#    dummyMean1 continMean1 dummyMean2 continMean2 dummyMean3 continMean3
#a        0.50   128.00000        0.0          NA        0.5          17
#fj       0.75    94.66667        0.5          64        0.0          NA
#g        0.25    54.00000        0.5          57        0.5         146
person akrun    schedule 23.03.2015