R: Очистка широкого и неопрятного кадра данных

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

d<-data.frame(id=(1:9), 
                  grp_id=(c(rep(1,3), rep(2,3), rep(3,3))), 
                  a=rep(NA, 9), 
                  b=c("No", rep(NA, 3), "Yes", rep(NA, 4)), 
                  c=c(rep(NA,2), "No", rep(NA,6)), 
                  d=c(rep(NA,3), "Yes", rep(NA,2), "No", rep(NA,2)), 
                  e=c(rep(NA, 7), "No", NA), 
                  f=c(NA, "No", rep(NA,3), "No", rep(NA,2), "No"))
>d
  id grp_id  a    b    c    d    e    f
1  1      1 NA   No <NA> <NA> <NA> <NA>
2  2      1 NA <NA> <NA> <NA> <NA>   No
3  3      1 NA <NA>   No <NA> <NA> <NA>
4  4      2 NA <NA> <NA>  Yes <NA> <NA>
5  5      2 NA  Yes <NA> <NA> <NA> <NA>
6  6      2 NA <NA> <NA> <NA> <NA>   No
7  7      3 NA <NA> <NA>   No <NA> <NA>
8  8      3 NA <NA> <NA> <NA>   No <NA>
9  9      3 NA <NA> <NA> <NA> <NA>   No

В каждой группе (grp_id) есть только 1 значение "Да" или "Нет", связанное с каждым из столбцов a: f.

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

grp_id  a    b    c    d    e    f
     1 NA   No   No <NA> <NA>   No
     2 NA  Yes <NA>  Yes <NA>   No
     3 NA <NA> <NA>   No   No   No

Я признаю, что пакет tidyr, вероятно, лучший инструмент, и первые шаги, вероятно, будут

d %>% 
   group_by(grp_id) %>%
     summarise()

Я был бы признателен за помощь с командами в суммировании, или любое решение на самом деле. Благодарю.

3 ответа

Мы можем использовать summarise_at и подмножество первого не-NA элемента

library(dplyr)
d %>%
   group_by(grp_id) %>%
   summarise_at(2:7, funs(.[!is.na(.)][1]))
# A tibble: 3 x 7
#   grp_id     a      b      c      d      e      f
#    <dbl> <lgl> <fctr> <fctr> <fctr> <fctr> <fctr>
#1      1    NA     No     No   <NA>   <NA>     No
#2      2    NA    Yes   <NA>    Yes   <NA>     No
#3      3    NA   <NA>   <NA>     No     No     No

В примере набора данных все столбцы от "a" до "f" factors с некоторыми, имеющими только уровни "Нет". Если это должно быть стандартизировано со всеми столбцами, имеющими одинаковые levelsтогда нам может понадобиться позвонить factor с levels указано как c('Yes', 'No') в summarise_at т.е. summarise_at(2:7, funs(factor(.[!is.na(.)][1], levels = c('Yes', 'No'))))

Мы можем использовать aggregate, Пакеты не используются.

 YN <- function(x) c(na.omit(as.character(x)), NA)[1]
 aggregate(d[3:8], d["grp_id"], YN)

давая:

##   grp_id    a    b    c    d    e  f
## 1      1 <NA>   No   No <NA> <NA> No
## 2      2 <NA>  Yes <NA>  Yes <NA> No
## 3      3 <NA> <NA> <NA>   No   No No

Выше приведены столбцы символов. Если вы предпочитаете факторные столбцы, используйте это:

YNfac <- function(x) factor(YN(x), c("No", "Yes"))
aggregate(d[3:8], d["grp_id"], YNfac)

Примечание. Другие альтернативные реализации YN:

YN <- function(x) sort(as.character(x), na.last = TRUE)[1]

YN <- function(x) if (all(is.na(x))) NA_character_ else na.omit(as.character(x))[1]

library(zoo)
YN <- function(x) na.locf0(as.character(x), fromLast = TRUE)[1]

Вы получили несколько хороших ответов, но ни один из них не использует tidyr пакет. (The summarize() а также summarize_at() Семейство функций из dplyr.)

На самом деле, tidyrТолько решение вашей проблемы очень выполнимо.

d %>%
    gather(col, value, -id, -grp_id, factor_key=TRUE) %>%
    na.omit() %>%
    select(-id) %>%
    spread(col, value, fill=NA, drop=FALSE)

Единственная трудная часть заключается в том, чтобы вы получили a колонка в вашем выводе. Для вашего примера данных, это полностью NA, Трюк это factor_key=TRUE аргумент gather() и drop=FALSE аргумент spread(), Без этих двух аргументов вывод не будет иметь a столбец, и будет иметь только столбцы по крайней мере с однимNA запись.

Вот описание того, как это работает:

gather(col, value, -id, -grp_id, factor_key=TRUE) %>%

Это приводит в порядок ваши данные - оно эффективно заменяет столбцы a - f с новыми колоннами col а также value, формируя длинноформатный "аккуратный" фрейм данных. Записи в col столбец буквы a - f, И потому что мы использовали factor_key=TRUEЭтот столбец является фактором с уровнями, а не просто символьным вектором.

na.omit() %>%

Это удаляет все NA значения из длинных данных.

select(-id) %>%

Это устраняет id колонка.

spread(col, value, fill=NA, drop=FALSE)

Это расширяет данные, используя значения в col столбец, чтобы определить новые имена столбцов, а также значения в value столбец для заполнения записей новых столбцов. Когда данные отсутствуют, значение fill (Вот NA) используется вместо. И drop=FALSE означает, что когда col является фактором, будет один столбец на уровень фактора, независимо от того, присутствует ли этот уровень в данных или нет. Это, наряду с настройкой col быть фактором, это то, что получает a в качестве выходного столбца.

Лично я считаю этот подход более читабельным, чем подходы, требующие подмножества или lapply вещи. Кроме того, этот подход потерпит неудачу, если ваши данные на самом деле не являются "горячими", тогда как другие могут "работать" и давать вам неожиданный вывод. Недостатком этого подхода является то, что выходные столбцы a - f не факторы, а векторы символов. Если вам нужен коэффициент вывода, вы должны быть в состоянии сделать (не проверено)

mutate(value = factor(value, levels=c('Yes', 'No', NA))) %>%

где-нибудь между gather() а также spread() функции для обеспечения фактора производства.

Другие вопросы по тегам