#r #dplyr #na
#r #dplyr #na
Вопрос:
Я ищу способ «заполнить» NA
s вправо (в отличие от вниз / вверх) с помощью dplyr. Другими словами, я хотел бы преобразовать d в d2 без необходимости явно ссылаться на какие-либо столбцы в вызове mutate.
Мой реальный фрейм данных содержит несколько 10 полей с расположенными в шахматном порядке блоками NAS, охватывающими переменное количество столбцов. Мне любопытно, есть ли короткий способ глобально наследовать первое значение, отличное от NA, слева, независимо от того, в каком поле оно встречается.
d<-data.frame(c1=c("a",1:4), c2=c(NA,2,NA,4,5), c3=c(NA,3,4,NA,6))
d2<-data.frame(c1=c("a",1:4), c2=c("a",2,2,4,5), c3=c("a",3,4,4,6))
d
d2
Комментарии:
1. Не приведет ли это к дисбалансу в
type
первом столбцеfactor
, если ‘a’ заменит NA первой строкой, тогда другие типы столбцов также изменятся.2. Это, безусловно, было бы проблемой для многих вариантов использования, но для этого я рассматриваю все как текст перед цепочкой dplyr, которая будет собирать / распространять / изменять данные в приятный «длинный» формат с сочетанием числовых и символьных столбцов. Я работаю с данными из PDF-файла, экспортированного из файла Excel, в котором использовались объединенные ячейки для заголовков нескольких столбцов. Количество столбцов, образующих блок данных, относящихся к одному местоположению, варьируется, но, к счастью, столбец со значением всегда кажется крайним слева от блока.
Ответ №1:
Мы можем преобразовать a gather
в «длинный» формат, fill
сгруппировать по номеру строки, а затем spread
вернуться к «широкому» формату
library(tidyverse)
rownames_to_column(d, 'rn') %>%
gather(key, val, -rn) %>%
group_by(rn) %>%
fill(val) %>%
spread(key, val) %>%
ungroup %>%
select(-rn)
# A tibble: 5 x 3
# c1 c2 c3
# <chr> <chr> <chr>
#1 a a a
#2 1 2 3
#3 2 2 4
#4 3 4 4
#5 4 5 6
или другим вариантом без изменения формы было бы выполнение заполнения по строкам с помощью na.locf
library(zoo)
d %>%
mutate(c1 = as.character(c1)) %>%
pmap_dfr(., ~ na.locf(c(...)) %>%
as.list %>%
as_tibble)
Кроме того, если мы используем na.locf
, он выполняется по столбцам, поэтому данные можно транспонировать и применять na.locf
напрямую
d[] <- t(na.locf(t(d)))
d
# c1 c2 c3
#1 a a a
#2 1 2 3
#3 2 2 4
#4 3 4 4
#5 4 5 6
Как @G.Гротендик упомянул в комментариях, чтобы позаботиться об элементах, которые находятся в начале строки, используйте na.locf0
вместо na.locf
Комментарии:
1. супер. Я попробую оба подхода. Мне нравится простота zoo ::na.loc, и я должен был сам подумать о сборе / распространении по номеру строки. Вся причина, по которой мне нужно выполнить операцию заполнения, заключается в том, чтобы будущие операции сбора / распространения работали должным образом.
2. Может потребоваться использовать.
na.locf0
здесь в случае, если первые элементы в строке являются NA.3. Это прекрасно сработало для моей задачи. Мои исходные данные были структурированы в таблице, которая была перенесена на страницу (т. Е. Заголовок таблицы повторялся 4-5 раз на страницу), и было ~ 20 страниц. Я также понял, что в моих данных было несколько NAS, которые я хотел сохранить. Моя стратегия заключалась в том, чтобы просто добавить row_number в качестве поля, создать один df путем фильтрации по заголовкам полей, для которых я хотел использовать na.locf, и создать 2-й df, содержащий фактические данные. Затем я связал таблицы по строкам, отсортировал по исходному заголовку строки, а затем перешел к моей цепочке tidyr для преобразования в «длинный» формат.
4. @DanStrobridge Рад узнать, что это работает для вас. Что касается других комментариев, я не слежу за этим. Вы хотите сказать, что есть какая-то проблема
5. Никаких проблем. Теперь все работает. Я рад, что узнал о na.locf. Большое спасибо.
Ответ №2:
Мы можем применять zoo::na.locf
по строкам, используя apply
d[] <- t(apply(d, 1, zoo::na.locf))
d
# c1 c2 c3
#1 a a a
#2 1 2 3
#3 2 2 4
#4 3 4 4
#5 4 5 6
Комментарии:
1. Смотрите Комментарий na.locf0 к другому ответу.