Рассчитать "групповые характеристики" без ddply и слияния

Интересно, есть ли более простой способ вычислить определенный тип переменных, чем обычно подходит подход, который я обычно делаю....

Пример ниже, вероятно, объясняет это лучше всего. У меня есть dataframe с 2 столбцами (фрукты и плод ли гниль или нет). Я бы хотел, чтобы для каждой строки добавляли, например. процент плодов той же категории, что и гнилой. Например, есть 4 записи для яблок, 2 из них гнилые, поэтому каждая строка для яблока должна читать 0,5. Целевые значения (чисто как иллюстрация) включены в столбец "желаемый результат".

Ранее я столкнулся с этой проблемой * используя команду "ddply" для переменной фрукта (с функцией sum/lenght as), создавая новый 3f 2-й фрейм * используйте команду "merge", чтобы связать эти значения с прежним фреймворком данных.

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

Большое спасибо заранее,

W

    Fruit Rotten Desired_Outcome_PercRotten
1   Apple      1                        0.5
2   Apple      1                        0.5
3   Apple      0                        0.5
4   Apple      0                        0.5
5    Pear      1                       0.75
6    Pear      1                       0.75
7    Pear      1                       0.75
8    Pear      0                       0.75
9  Cherry      0                          0
10 Cherry      0                          0
11 Cherry      0                          0

#create example datagram; desired outcome columns are purely inserted as illustrative of target outcomes
Fruit=c(rep("Apple",4),rep("Pear",4),rep("Cherry",3))
Rotten=c(1,1,0,0,1,1,1,0,0,0,0)
Desired_Outcome_PercRotten=c(0.5,0.5,0.5,0.5,0.75,0.75,0.75,0.75,0,0,0)
df=as.data.frame(cbind(Fruit,Rotten,Desired_Outcome_PercRotten))        
df

Ответ 1

Вы можете сделать это только с помощью ddply и mutate:

# changed summarise to transform on joran suggestion
# changed transform to mutate on mnel suggestion :)
ddply(df, .(Fruit), mutate, Perc = sum(Rotten)/length(Rotten))

#     Fruit Rotten Perc
# 1   Apple      1 0.50
# 2   Apple      1 0.50
# 3   Apple      0 0.50
# 4   Apple      0 0.50
# 5  Cherry      0 0.00
# 6  Cherry      0 0.00
# 7  Cherry      0 0.00
# 8    Pear      1 0.75
# 9    Pear      1 0.75
# 10   Pear      1 0.75
# 11   Pear      0 0.75

Ответ 2

data.table очень быстрый, поскольку он обновляется по ссылке. Как насчет его использования?

library(data.table)

dt=data.table(Fruit,Rotten,Desired_Outcome_PercRotten)

dt[,test:=sum(Rotten)/.N,by="Fruit"]
#dt
#     Fruit Rotten Desired_Outcome_PercRotten test
# 1:  Apple      1                       0.50 0.50
# 2:  Apple      1                       0.50 0.50
# 3:  Apple      0                       0.50 0.50
# 4:  Apple      0                       0.50 0.50
# 5:   Pear      1                       0.75 0.75
# 6:   Pear      1                       0.75 0.75
# 7:   Pear      1                       0.75 0.75
# 8:   Pear      0                       0.75 0.75
# 9: Cherry      0                       0.00 0.00
#10: Cherry      0                       0.00 0.00
#11: Cherry      0                       0.00 0.00

Ответ 3

Одним из решений в базе R является использование ave.

within(df, {
  ## Because of how you've created your data.frame
  ##   Rotten is actually a factor. So, we need to
  ##   convert it to numeric before we can use mean
  Rotten <- as.numeric(as.character(Rotten))
  NewCol <- ave(Rotten, Fruit)
})
    Fruit Rotten Desired_Outcome_PercRotten NewCol
1   Apple      1                        0.5   0.50
2   Apple      1                        0.5   0.50
3   Apple      0                        0.5   0.50
4   Apple      0                        0.5   0.50
5    Pear      1                       0.75   0.75
6    Pear      1                       0.75   0.75
7    Pear      1                       0.75   0.75
8    Pear      0                       0.75   0.75
9  Cherry      0                          0   0.00
10 Cherry      0                          0   0.00

или короче:

transform(df, desired = ave(Rotten == 1, Fruit))

Функция по умолчанию, применяемая с ave, равна mean, поэтому я не включил ее здесь. Однако вы можете указать другую функцию, добавив FUN = some-function-here, если вы хотите сделать что-то другое.

Ответ 4

Поскольку ave уже вышел, позвольте мне добавить одно решение, используя мою базовую функцию R: aggregate.

Вы можете получить нужные данные просто:

aggregate(as.numeric(as.character(Rotten)) ~ Fruit, df, mean)

Однако вам понадобится еще merge после него (или в одной части):

merge(df, aggregate(as.numeric(as.character(Rotten)) ~ Fruit, df, mean))