Создать новую переменную с условными средними и NA - PullRequest
2 голосов
/ 12 октября 2019

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

У меня есть набор данных, который выглядит следующим образом:

df <- data.frame(name = c("john", "paul", "ringo", "george", "john", "paul", "ringo", "george", "john", "paul", "ringo", "george"), 
                 year = c(2018, 2018, 2018, 2018, 2017, 2017, 2017, 2017, 2016, 2016, 2016, 2016),
                 station1 = c(1, 2, 3, NA, 2, NA, 5, 6, 7, 8, 9, 0),
                 station2 = c(NA, 6, 8, 1, 2, 6, NA, 1, NA, 1, 5, 3),
                 station3 = c(NA, 2, 3, 5, 1, NA, 1, 5, 3, 1, 2, 3),
                 station4 = c(9, 8, 7, 6, NA, 8, 12, 8, 83, 4, 3, NA))

Теперь мне нужно создать новую переменную, назовем ее new_station, которая принимает значение, зависящее от каждого имени в каждый данный год. Например:

  • Для Джон Мне нужно среднее значение station1 и station3 .
  • Для Пол Мне нужно всего лишь станция 4 .
  • Для ringo Мне нужно среднее значение station1, station2, station3
  • Для Джордж Мне нужно всего лишь station4 .

Я пробовал несколько комбинаций фильтра, выбора и изменения, вдоль линийиз:

df %>%
  filter(name == "john") %>%
  select(station1, station3) %>%
  mutate(new_station = rowMeans(c(station1, station3)))

Но это не позволит мне присвоить значение только значениям одной строки. Некоторые другие попытки приводят каждую строку в новом столбце к среднему значению для всех 6 ячеек (2 станции по 3 года), когда мне просто нужно среднее значение для этого конкретного года. Другие методы, которые я пробовал, не в состоянии справиться с тем фактом, что есть некоторые пропущенные значения, и я нуждаюсь в тех, которые пропущены.

Мне нужен своего рода цикл, который масштабируется, просто меняя условия для каждого имени,поскольку в реальной жизни у меня есть что-то вроде набора данных из 21 имени и 30 станций.

Есть мысли?

Примечание: если это иллюстрирует то, что я пытаюсь сделать, я знаю, каксделать это в Stata. В Stata для имени john это будет выглядеть примерно так:

egen new_station = rowmean(station1 station3) if name == "john"

Мне просто нужно сделать что-то подобное в R.

Спасибо!

Ответы [ 2 ]

3 голосов
/ 12 октября 2019

Я бы преобразовал данные в длинный формат, затем использовал бы case_when. Если хотите, вы можете преобразовать его обратно в широкоформатный.

df$id = 1:nrow(df)

library(tidyr) 
df %>% pivot_longer(
    cols = starts_with("station"), 
    names_to = "station", names_prefix = "station",
    values_to = "value"
  ) %>%
  group_by(name, year) %>%
  mutate(result = case_when(
    name == "john" ~ mean(value[station %in% c(1, 3)], na.rm = TRUE),
    name %in% c("paul", "george") ~ value[station == 4],
    name == "ringo" ~ mean(value[station %in% c(1, 2, 3)], na.rm = TRUE)
  ))
# # A tibble: 48 x 6
# # Groups:   name, year [12]
#    name   year    id station value result
#    <fct> <dbl> <int> <chr>   <dbl>  <dbl>
#  1 john   2018     1 1           1   1   
#  2 john   2018     1 2          NA   1   
#  3 john   2018     1 3          NA   1   
#  4 john   2018     1 4           9   1   
#  5 paul   2018     2 1           2   8   
#  6 paul   2018     2 2           6   8   
#  7 paul   2018     2 3           2   8   
#  8 paul   2018     2 4           8   8   
#  9 ringo  2018     3 1           3   4.67
# 10 ringo  2018     3 2           8   4.67
# # ... with 38 more rows
0 голосов
/ 12 октября 2019

Вот решение . Он основан на создании справочной таблицы и получении rowMeans() подмножества подмножества данных. :

library(data.table)

dt <- as.data.table(DF)
dt[, name := as.character(name)]

lookup <- list(john = c('station1', 'station3'),
               paul = 'station4',
               ringo = c('station1','station2','station3'),
               george = 'station4')

dt[,
   new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
   by = name]
dt

Основываясь на комментариях OP, безопаснее установить dt в именах таблицы lookup:

dt <- as.data.table(DF)
dt[, name := as.character(name)]

lookup[[4]] <- NULL
setdiff(dt[, name], names(lookup))

# error
dt[,
   new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
   by = name]
# OK
dt[name %in% names(lookup),
   new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
   by = name]

dt

Чтобы лучше понять, что происходит,Я рекомендую использовать следующие строки:

dt <- as.data.table(DF)
# what is .SD?
dt[, print(.SD), by = name]
dt[, .SD[,print(.SD) , .SDcols = lookup[[unlist(.BY)]]], by = name]

#what is .BY?
dt[, print(.BY), by = name]
dt[, print(unlist(.BY)), by = name]
dt[, name := as.character(name)]
dt[, print(unlist(.BY)), by = name]

Ссылки:

Отличное объяснение S убсет D ата.table: Что означает .SD в data.table в R

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...