#r
Вопрос:
Мне нужно нормализовать некоторые значения данных (например, вычесть из одного значения A другое значение B) в соответствии с двумя факторами. Предположим, что фрейм данных, подобный этому:
mydf <- data.frame("countries" = c(rep("USA",3), rep("China",3), rep("France",3)), "Type" = rep(c("Grass","Cow", "Bunny"), 3), "W1" = rnorm(9, 20, 10), "W2" = rnorm(9, 60, 10))
Для каждой страны я хотел бы вычесть значения как W1, так и W2 травы у коров и кроликов, например: W1_Norm_Cows = W1_Cows - W1_Grass
.
Я попытался создать трубу с помощью dplyr
, но я застрял.
normalized_mydf <- mydf %>%
group_by(Country) %>%
mutate(W1_norm = W1-??) #How can I specify which values to use?
mutate(W2_norm = W2-??) #How can I specify which values to use?
Я пытался использовать group_by
дважды, но, конечно, сценарий неверен (или не завершен), потому что он вычитает значение W1 для себя, давая ноль всем наблюдениям.
Ответ №1:
Я предлагаю изменить структуру данных таким образом:
mydf %>%
gather("key", "value", -c(countries, Type)) %>%
spread(Type, value) %>%
mutate(Norm_cows = Cow - Grass,
Norm_bunny = Bunny - Grass)
countries key Bunny Cow Grass Norm_cows Norm_bunny
1 China W1 25.347062 20.003938 19.71476 0.2891776 5.632301
2 China W2 69.627360 49.867221 61.53735 -11.6701307 8.090008
3 France W1 9.150308 18.257695 26.26750 -8.0098078 -17.117195
4 France W2 58.396270 65.252741 76.46905 -11.2163100 -18.072781
5 USA W1 18.803997 6.361533 30.94399 -24.5824534 -12.139989
6 USA W2 77.703504 69.438751 60.23095 9.2078025 17.472556
И повернитесь еще немного, чтобы принять желаемую форму:
mydf %>%
select(-c(Bunny, Cow, Grass)) %>%
gather('new_key', 'value', c(Norm_cows, Norm_bunny)) %>%
mutate(key = paste0(key, "_", new_key)) %>%
select(-new_key) %>%
spread(key, value)
countries W1_Norm_bunny W1_Norm_cows W2_Norm_bunny W2_Norm_cows
1 China 5.632301 0.2891776 8.090008 -11.670131
2 France -17.117195 -8.0098078 -18.072781 -11.216310
3 USA -12.139989 -24.5824534 17.472556 9.207803
Обновить:
Когда у вас есть длинный список типов, я предлагаю следующий подход. Получите список типов из вашего исходного набора данных в виде уникальных значений Type
столбца, за исключением Grass
:
types <- mydf$Type %>% unique()
types <- types[types != "Grass"]
types
[1] "Cow" "Bunny"
Измените форму, а затем используйте mutate_at
над переменными, определенными types
вектором:
mydf %>%
gather("key", "value", -c(countries, Type)) %>%
spread(Type, value) %>%
mutate_at(.vars = types,
.funs = list(Norm = ~ . - Grass))
countries key Bunny Cow Grass Cow_Norm Bunny_Norm
1 China W1 19.838156 21.38302 17.06494 4.318081 2.773215
2 China W2 70.739764 75.91797 66.75675 9.161219 3.983009
3 France W1 22.347127 20.83980 24.59448 -3.754679 -2.247350
4 France W2 66.006313 66.47073 73.04356 -6.572828 -7.037244
5 USA W1 1.862306 22.58228 18.61895 3.963325 -16.756644
6 USA W2 67.939794 79.84611 61.75339 18.092715 6.186404
Поворот и изменение формы:
mydf %>%
select(-c(types), -Grass) %>%
gather('new_key', 'value', ends_with("_Norm")) %>%
mutate(key = paste0(key, "_", new_key)) %>%
select(-new_key) %>%
spread(key, value)
countries W1_Bunny_Norm W1_Cow_Norm W2_Bunny_Norm W2_Cow_Norm
1 China 2.773215 4.318081 3.983009 9.161219
2 France -2.247350 -3.754679 -7.037244 -6.572828
3 USA -16.756644 3.963325 6.186404 18.092715
Не очень элегантное, но рабочее решение.
Комментарии:
1. Спасибо, оба работают отлично. Мне интересно, как я могу это сделать, если у меня больше типов (в данном случае больше животных, чем коров и кроликов)
2. @Strobila, я обновляю свой ответ