Преобразование данных с помощью множества факторов R

#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, я обновляю свой ответ