我有一个像这样的数据框:
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
我不知道该怎么做。有什么想法吗?
答案 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
的值,最后使用rbind
和do.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