如果上面缺少值,将列中的值上移

时间:2019-01-11 07:07:42

标签: r dataframe

我有一个像这样的数据框:

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 :(得分:5)

以下是使用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

答案 1 :(得分:4)

不要以为这是最有效的方法,而是一种选择

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的值,最后使用rbinddo.call将数据帧列表合并为一个。

答案 2 :(得分:3)

这里是data.table的一个选项。根据NA值,将'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

答案 3 :(得分:0)

这是一个基本的解决方案,如果您的实际案例不具备因素,则可以跳过第一行和最后一行:

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