Заполнить пропущенные значения по строкам (вправо /влево)

#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 к другому ответу.