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

У меня есть фрейм данных, состоящий из трех столбцов (см. Пример в коде). первый столбец содержит категории (a), второй столбец - количество наблюдений (b), а третий столбец - среднее значение этих наблюдений (c).

    #create a test df
    a<-factor(c("aaa","aaa","aaa","ddd","eee","ddd","aaa","ddd"))
    b<-c(3,4,1,3,5,7,3,2)
    c<-c(1,2,NA,4,5,6,7,NA)
    df.abc<-data.frame(a=a,b=b,c=c)
    df.abc

Если количество наблюдений было 1 или 2, записи помечались как пропущенные значения (NA).

Итак, цель моей функции - заменить эти пропущенные значения средним значением каждой категории.

Я взял меня, но у меня заработала функция, которая заменяет все недостающие значения для одной категории (в случае, если наблюдение было 1). Это выглядит так:

    #function to substitue the missing values in row c by their means 
    #according to their categories
    function.abc<-function(x){
        ifelse(
            (df.abc[,1]==x)&(df.abc[,2]==1),
            mean(df.abc$c[df.abc$a ==x],na.rm=TRUE),
            df.abc[,3]
        )
    }

Тестирование этой функции:

    #test the function for the category "ccc"
    function.abc("aaa")

Он работает довольно хорошо (но это только среднее, а не среднее значение). Результат:

[1] 1.000000 2.000000 3.333333 4.000000 5.000000 6.000000 7.000000 NA

Теперь моя проблема в том, что у меня довольно много категорий (n = 32), и я попытался применить эту функцию к вектору, содержащему мои категории. Простым примером в этом случае будет:

    #test the function for a testvector
    test.vector<-c("aaa","ddd")
    function.abc(test.vector)

вывод:

[1] 1.0 2.0 4.5 4.0 5.0 6.0 7.0 NA

Так что, очевидно, это не сработает ...

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

Изменить:

Я хотел бы, чтобы результат был: [1] 1.000000 2.000000 3.20000 4.000000 5.000000 6.000000 7.000000 5.000000

так что среднее значение группы aaa (3.20000) заменяет значение NA в aaa, а среднее значение группы ddd (5,0000000) заменяет NA в ddd ...


person Joschi    schedule 21.11.2012    source источник
comment
Непонятно, что вы хотите вернуть в последнем случае.   -  person Matthew Lundberg    schedule 21.11.2012
comment
Я верю, что он захочет [1] 1.000000 2.000000 3.333333 4.000000 5.000000 6.000000 7.000000 5.00000.   -  person Señor O    schedule 21.11.2012
comment
Я не думаю, что ни один из предложенных ответов верен с учетом постановки проблемы. Если три элемента находятся в категории aaa со значениями =c(1,2,7), с подсчетами =c(3,4,3), то средневзвешенное значение не 3,3333, а скорее 3,2. Если мое понимание постановки задачи неверно, то, возможно, вопрос можно изменить, чтобы прояснить, почему счетчики не должны использоваться при вычислении среднего значения?   -  person IRTFM    schedule 21.11.2012
comment
@ SeñorO & Dwin Вопрос не был ясен, поэтому я отредактировал его выше. Двин был прав в том, что на самом деле я рассчитывал только среднее, а не среднее значение. Он правильно понял вопрос, и его решение сработало очень хорошо.   -  person Joschi    schedule 22.11.2012


Ответы (3)


Чтобы одновременно работать с несколькими столбцами в категории, вам нужно будет использовать что-то, что разбивает фрейм данных, а затем работает с компонентами. Для этого хорошо работает парадигма lapply( split(df, fac), function(x) {...}). Или вы можете использовать transform или plyr пакет.

> lapply( split( df.abc, df.abc$a), 
               function(dfrm) { dfrm[is.na(dfrm$c), "c"] <- 
                  weighted.mean(dfrm[!is.na(dfrm$c) , "c"], dfrm[!is.na(dfrm$c), "b"])
                         dfrm} )  
                # need to evaluate dfrm in order to return the full value.
$aaa
    a b   c
1 aaa 3 1.0
2 aaa 4 2.0
3 aaa 1 3.2
7 aaa 3 7.0

$ddd
    a b   c
4 ddd 3 4.0
6 ddd 7 6.0
8 ddd 2 5.4

$eee
    a b c
5 eee 5 5

Затем вы можете rbind их использовать с помощью `do.call:

 do.call( rbind, lapply( split( df.abc, df.abc$a), 
          function(dfrm) { dfrm[is.na(dfrm$c), "c"] <-
                 weighted.mean(dfrm[!is.na(dfrm$c) , "c"], dfrm[!is.na(dfrm$c), "b"])
                   dfrm} ) )
        a b   c
aaa.1 aaa 3 1.0
aaa.2 aaa 4 2.0
aaa.3 aaa 1 3.2
aaa.7 aaa 3 7.0
ddd.4 ddd 3 4.0
ddd.6 ddd 7 6.0
ddd.8 ddd 2 5.4
eee   eee 5 5.0
person IRTFM    schedule 21.11.2012
comment
у меня получилось. Даже не думал сначала о разделении, но это имеет смысл. Благодарность! - person Joschi; 22.11.2012

Я не совсем понимаю, что вы имеете в виду, но если вы хотите включить все такие строки, вы можете использовать% в%.

function.abc<-function(x){
  ifelse(
    (df.abc[,1] %in% x)&(df.abc[,2]==1),
    mean(df.abc$c[df.abc$a %in% x],na.rm=TRUE),
    df.abc[,3]
  )
}

> function.abc("aaa")
[1] 1.000000 2.000000 3.333333 4.000000 5.000000 6.000000 7.000000       NA

> test.vector<-c("aaa","ddd")
> function.abc(test.vector)
[1]  1  2  4  4  5  6  7 NA

Последний элемент - NA, потому что столбец «b» не равен 1.

person Matthew Lundberg    schedule 21.11.2012

CatMeans <- tapply(df.abc$c, df.abc$a, function(x) mean(x, na.rm==T)) даст вам средства по категориям.

     aaa      ddd      eee 
3.333333 5.000000 5.000000 

Итак, сделаем это для всех:

> CatMeans <- tapply(df.abc$c, df.abc$a, function(x) mean(x, na.rm==T))
> ifelse(is.na(df.abc$c), CatMeans[df.abc$a], df.abc$c)
[1] 1.000000 2.000000 3.333333 4.000000 5.000000 6.000000 7.000000 5.000000

Я уверен, что вы можете превратить это в функцию. Если вам нужны только "aaa" и "ddd", то вы могли бы иметь ifelse(is.na(df.abc$c) & df.abc$a %in% c("aaa","ddd"),...

person Señor O    schedule 21.11.2012