我有一个具有一些不完整和重复特征的个人DF,如下:
name <- c("A", "A", "B", "B", "B", "C", "D", "D")
age <- c(28,NA,NA,NA,NA,NA,53,NA)
birthplace <- c("city1",NA, "city2",NA,NA,NA,NA,NA)
value <- 100:107
df <- data.frame(name,age,birthplace,value)
name age birthplace value
1 A 28 city1 100
2 A NA <NA> 101
3 B NA city2 102
4 B NA <NA> 103
5 B NA <NA> 104
6 C NA <NA> 105
7 D 53 <NA> 106
8 D NA <NA> 107
由于该值对于行是唯一的。我希望每一行都有可行的人的详细信息:
name age birthplace value
1 A 28 city1 100
2 A 28 city1 101
3 B NA city2 102
4 B NA city2 103
5 B NA city2 104
6 C NA <NA> 105
7 D 53 <NA> 106
8 D 53 <NA> 107
我尝试使用
library(zoo)
library(dplyr)
df <- df %>% group_by(name) %>% na.locf(na.rm=F)
但它确实运作良好。是否按组实现功能?
答案 0 :(得分:9)
作为另一个基础R解决方案,这里是一个穷人的na.locf
fill_down <- function(v) {
if (length(v) > 1) {
keep <- c(TRUE, !is.na(v[-1]))
v[keep][cumsum(keep)]
} else v
}
要按组填写,方法是使用tapply()
拆分并应用于每个组,并使用split<-
将组合并到原始几何图形,如
fill_down_by_group <- function(v, grp) {
## original 'by hand':
## split(v, grp) <- tapply(v, grp, fill_down)
## v
## done by built-in function `ave()`
ave(v, grp, FUN=fill_down)
}
要处理多个列,可能会有
elts <- c("age", "birthplace")
df[elts] <- lapply(df[elts], fill_down_by_group, df$name)
我有兴趣了解一个dplyr解决方案如何处理许多列,而不需要对每个列进行硬编码?回答我自己的问题,我猜这是
library(dplyr); library(tidyr)
df %>% group_by(name) %>% fill_(elts)
当群组已经“分组”(例如identical(grp, sort(grp))
)时,更有效的基础解决方案是
fill_down_by_grouped <- function(v, grp) {
if (length(v) > 1) {
keep <- !(duplicated(v) & is.na(v))
v[keep][cumsum(keep)]
} else v
}
对我来说,fill_down()
对载有大约10M元素的向量需要大约225ms; fill_down_by_grouped()
需要约300毫秒,与群组数量无关; fill_down_by_group()
与群组数量相称; 10000组~2s,10M组约36s
答案 1 :(得分:3)
也可以是:
library(dplyr)
library(tidyr)
df %>% group_by(name) %>% fill(age, birthplace)
# Source: local data frame [8 x 4]
# Groups: name [4]
# name age birthplace value
# <fctr> <dbl> <fctr> <int>
# 1 A 28 city1 100
# 2 A 28 city1 101
# 3 B NA city2 102
# 4 B NA city2 103
# 5 B NA city2 104
# 6 C NA NA 105
# 7 D 53 NA 106
# 8 D 53 NA 107
答案 2 :(得分:2)
您可以将na.locf
打包在do
df %>% group_by(name) %>% do(na.locf(., na.rm = FALSE))
答案 3 :(得分:2)
根据您接下来的操作,您可能更喜欢嵌套表单中的数据。
(nested <- df %>%
group_by(name) %>%
summarize(
age = na.omit(age)[1],
birthplace = na.omit(birthplace)[1],
value = list(value)
)
)
## # A tibble: 4 x 4
## name age birthplace value
## <fctr> <dbl> <fctr> <list>
## 1 A 28 city1 <int [2]>
## 2 B NA city2 <int [3]>
## 3 C NA NA <int [1]>
## 4 D 53 NA <int [2]>
如果您需要计算个别value
,您可以随时将其取消。
nested %>% tidyr::unnest()
## # A tibble: 8 x 4
## name age birthplace value
## <fctr> <dbl> <fctr> <int>
## 1 A 28 city1 100
## 2 A 28 city1 101
## 3 B NA city2 102
## 4 B NA city2 103
## 5 B NA city2 104
## 6 C NA NA 105
## 7 D 53 NA 106
## 8 D 53 NA 107
答案 4 :(得分:1)
这是基础R解决方案:
do.call(rbind,lapply(split(df, df$name), function(x) {
tempdf <- x
if (nrow(tempdf) > length(which(is.na(x$birthplace)))) {
tempdf[which(is.na(x$birthplace)),c("age","birthplace")] <- tempdf[which(is.na(x$birthplace))[1]-1,c("age","birthplace")]
}
return(tempdf)
}))
输出:
name age birthplace value
A 28 city1 100
A 28 city1 101
B NA city2 102
B NA city2 103
B NA <NA> 104
C NA <NA> 105
D 53 <NA> 106
D NA <NA> 107
答案 5 :(得分:1)
这是基础R解决方案。 fill
函数使用ave
调用na.omit(x)[1]
,就像Richie Cotton的解决方案一样。
fill <- function(...) ave(..., FUN = function(x) na.omit(x)[1])
transform(df, birthplace = fill(birthplace, name), age = fill(age, name))
注意:这也适用于na.locf
。将fill
替换为:
library(zoo)
fill <- function(...) ave(..., FUN = function(x) na.locf(x, na.rm = FALSE))
答案 6 :(得分:0)
你也可以通过合并。只需在名称列上进行连接即可。然后根据价值进行分组。
library(sqldf)
sqldf('select t1.name, t2.age, t2.birthplace,t1.value from df t1 inner join df t2 on t1.name=t2.name group by t1.value')
答案 7 :(得分:0)
还要考虑为每列运行滚动head()
的嵌套应用基础解决方案:
df <- setNames(data.frame(lapply(names(df), function(d)
sapply(1:nrow(df), function(i)
head(df[df[1:i, c("name")] == df$name[i], c(d)], 1))
)), names(df))