Сдвиг значений в столбцах вверх, если выше отсутствуют значения - PullRequest
0 голосов
/ 11 января 2019

У меня есть такой фрейм данных:

df <- data.frame(id = c("A", "A", "A", "A", "A", "A", "A", "A", 
                    "B", "B", "B", "B", "B", "B"),
             var1 = c("100", "200", "300", NA, NA, NA, NA, NA,
                      "100", "200", "300", NA, NA, NA), 
             var2 = c("100", NA, NA, "400", "500", "600", NA, NA,
                      NA, NA, NA, "400", NA, NA),
             var3 = c("200", NA, NA, NA, NA, NA, "700", "800",
                      "500", NA, NA, NA, "500", "600"))

, который выглядит так:

  id var1 var2 var3
   A  100  100  200
   A  200 <NA> <NA>
   A  300 <NA> <NA>
   A <NA>  400 <NA>
   A <NA>  500 <NA>
   A <NA>  600 <NA>
   A <NA> <NA>  700
   A <NA> <NA>  800
   B  100 <NA>  500
   B  200 <NA> <NA>
   B  300 <NA> <NA>
   B <NA>  400 <NA>
   B <NA> <NA>  500
   B <NA> <NA>  600

Я хотел бы сдвинуть значения в столбцах вверх, если выше отсутствуют значения (по группам). Результат должен выглядеть так:

  id var1 var2 var3
   A  100  100  200
   A  200  400  700
   A  300  500  800
   A <NA>  600 <NA>
   B  100  400  500
   B  200 <NA>  500
   B  300 <NA>  600

Понятия не имею, как это сделать. Есть мысли?

Ответы [ 4 ]

0 голосов
/ 14 января 2019

вот базовое решение, если ваш реальный случай не содержит факторов, вы можете пропустить первую и последнюю строки:

df[] <- lapply(df,as.character)
. <- lapply(split(df,df$id),lapply, na.omit)
. <- lapply(., function(x) lapply(x, `length<-`, max(lengths(x[-1]))))
df <- do.call(rbind,lapply(., do.call, what = data.frame))
df[] <- lapply(df, factor)

#     id var1 var2 var3
# A.1  A  100  100  200
# A.2  A  200  400  700
# A.3  A  300  500  800
# A.4  A <NA>  600 <NA>
# B.1  B  100  400  500
# B.2  B  200 <NA>  500
# B.3  B  300 <NA>  600
0 голосов
/ 11 января 2019

Не думаю, что это самый эффективный способ сделать это, кроме одного варианта

library(rowr)

df1 <- do.call(rbind, lapply(split(df, df$id), function(x) {
    data.frame(id = x$id[1], do.call(cbind.fill,c(sapply(x[-1], na.omit),fill = NA)))
}))
names(df1) <- names(df)
df1


#    id   var1   var2   var3
#A.1  A    100    100    200
#A.2  A    200    400    700
#A.3  A    300    500    800
#A.4  A   <NA>    600   <NA>
#B.1  B    100    400    500
#B.2  B    200   <NA>    500
#B.3  B    300   <NA>    600

Мы split помещаем кадр данных в список данных для каждого id, а для каждого кадра данных удаляем значения NA, используя na.omit, и используем cbind.fill, чтобы заполнить значения с помощью NA и, наконец, объединить список кадров данных обратно в один, используя rbind с do.call.

0 голосов
/ 11 января 2019

Вот вариант с data.table. Преобразуйте data.frame в data.table (setDT(df)), сгруппированный по id, order другой столбец на основе значений NA, затем создайте индекс для удаления строк, в которых находятся все элементы NA

library(data.table)
df1 <- setDT(df)[,  lapply(.SD, function(x) x[order(is.na(x))]), id]
df1[df1[,!Reduce(`&`, lapply(.SD, is.na)), .SDcols = var1:var3]]
#   id var1 var2 var3
#1:  A  100  100  200
#2:  A  200  400  700
#3:  A  300  500  800
#4:  A <NA>  600 <NA>
#5:  B  100  400  500
#6:  B  200 <NA>  500
#7:  B  300 <NA>  600

Или используя ту же логику с tidyverse. Сгруппированные по 'id', измените order или элементы во всех других столбцах с помощью mutate_all на order в логическом векторе (is.na(column)) и оставьте строки, имеющие хотя бы одну не-NA (filter_at) )

library(tidyverse)
df %>% 
   group_by(id) %>% 
   mutate_all(funs(.[order(is.na(.))])) %>% 
   filter_at(vars(var1:var3), any_vars(!is.na(.)))
# A tibble: 7 x 4
# Groups:   id [2]
#  id    var1  var2  var3 
#  <fct> <fct> <fct> <fct>
#1 A     100   100   200  
#2 A     200   400   700  
#3 A     300   500   800  
#4 A     <NA>  600   <NA> 
#5 B     100   400   500  
#6 B     200   <NA>  500  
#7 B     300   <NA>  600  

Упорядочить вектор / столбец на основе логической индексации просто.

v1 <- c(1:3, NA, 5, NA, 7)
order(is.na(v1)) #gives the index of order
#[1] 1 2 3 5 7 4 6

использовать этот индекс для изменения порядка значений

v1[order(is.na(v1))]
#[1]  1  2  3  5  7 NA NA
0 голосов
/ 11 января 2019

Вот грубая концепция с использованием data.table, которую можно уточнить:

library(data.table)
# Helper function:
shift_up <- function(x) {
  n <- length(x)
  x <- x[!is.na(x)]
  length(x) <- n
  x
}

setDT(df)
df[, lapply(.SD, shift_up), id][!(is.na(var1) & is.na(var2) & is.na(var3))]

   id var1 var2 var3
1:  A  100  100  200
2:  A  200  400  700
3:  A  300  500  800
4:  A <NA>  600 <NA>
5:  B  100  400  500
6:  B  200 <NA>  500
7:  B  300 <NA>  600
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...