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()
функции для обеспечения фактора производства.