据我所知,没有一个类似的问题能够回答我的问题。
我有一个data.frame,例如:
df1 <- data.frame(id = rep(c("a", "b","c"), each = 4),
val = c(NA, NA, NA, NA, 1, 2, 2, 3,NA,2,NA,3))
df1
id val
1 a NA
2 a NA
3 a NA
4 a NA
5 b 1
6 b 2
7 b 2
8 b 3
9 c NA
10 c 2
11 c NA
12 c 3
,我想摆脱所有的NA值(使用filter()可以很容易地做到),但是要确保如果删除了所有一个id值(在这种情况下,它删除了“ a”的每个实例),那插入了额外的一行,例如a = 0
这样:
id val
1 a 0
2 b 1
3 b 2
4 b 2
5 b 3
6 c 2
7 c 3
显然很容易以环形方式完成此操作,但我想知道是否有一种整洁/优雅的方法可以执行此操作。我认为tidyr :: complete()可能会有所帮助,但不能完全确定如何将其应用于此类情况
我不在乎行的顺序
干杯!
edit:更新后具有更清晰的所需输出。可能会使之前提交的所需答案不太清楚
答案 0 :(得分:9)
另一个使用dplyr
的想法,
library(dplyr)
df1 %>%
group_by(id) %>%
mutate(val = ifelse(row_number() == 1 & all(is.na(val)), 0, val)) %>%
na.omit()
给出,
# A tibble: 5 x 2 # Groups: id [2] id val <fct> <dbl> 1 a 0 2 b 1 3 b 2 4 b 2 5 b 3
答案 1 :(得分:3)
我们可能会
df1 %>% group_by(id) %>% do(if(all(is.na(.$val))) replace(.[1, ], 2, 0) else na.omit(.))
# A tibble: 5 x 2
# Groups: id [2]
# id val
# <fct> <dbl>
# 1 a 0
# 2 b 1
# 3 b 2
# 4 b 2
# 5 b 3
按id
分组后,如果val
中的所有内容都是NA
,则仅保留第一行,第二个元素替换为0,否则应用后返回相同的数据na.omit
。
采用一种更具可读性的格式
df1 %>% group_by(id) %>%
do(if(all(is.na(.$val))) data.frame(id = .$id[1], val = 0) else na.omit(.))
(我假设您确实要摆脱所有NA
的值;否则就不需要na.omit
。)
答案 2 :(得分:2)
df1[is.na(df1)] <- 0
df1[!(duplicated(df1$id) & df1$val == 0), ]
id val
1 a 0
5 b 1
6 b 2
7 b 2
8 b 3
答案 3 :(得分:1)
Base R选项是通过将所有NA
更改为0来查找具有所有transform
和val
的组,并仅选择unique
行,以便仅存在一行每组行。我们rbind
将该数据框与!all_NA
的组一起使用。
all_NA <- with(df1, ave(is.na(val), id, FUN = all))
rbind(unique(transform(df1[all_NA, ], val = 0)), df1[!all_NA, ])
# id val
#1 a 0
#5 b 1
#6 b 2
#7 b 2
#8 b 3
dplyr
选项看起来很丑陋,但是一种方法是使两组数据帧分别具有所有NA
值的组和另一组具有所有非NA值的组。对于具有所有NA
值的组,我们添加其id
和val
为0的行,并将其绑定到另一个组。
library(dplyr)
bind_rows(df1 %>%
group_by(id) %>%
filter(all(!is.na(val))),
df1 %>%
group_by(id) %>%
filter(all(is.na(val))) %>%
ungroup() %>%
summarise(id = unique(id),
val = 0)) %>%
arrange(id)
# id val
# <fct> <dbl>
#1 a 0
#2 b 1
#3 b 2
#4 b 2
#5 b 3
答案 4 :(得分:1)
这里也是一个选项:
df1 %>%
mutate_if(is.factor,as.character) %>%
mutate_all(funs(replace(.,is.na(.),0))) %>%
slice(4:nrow(.))
这给出了:
id val
1 a 0
2 b 1
3 b 2
4 b 2
5 b 3
替代:
df1 %>%
mutate_if(is.factor,as.character) %>%
mutate_all(funs(replace(.,is.na(.),0))) %>%
unique()
基于其他要求的更新: 一些用户建议对此数据框进行测试。当然,此答案假设您会手工研究所有内容。如果您必须用手“看”所有东西,可能没什么用,但是这里有:
df1 <- data.frame(id = rep(c("a", "b","c"), each = 4), val = c(NA, NA, NA, NA, 1, 2, 2, 3,NA,2,NA,3))
df1 %>%
mutate_if(is.factor,as.character) %>%
mutate(val=ifelse(id=="a",0,val)) %>%
slice(4:nrow(.))
这将产生:
id val
1 a 0
2 b 1
3 b 2
4 b 2
5 b 3
6 c NA
7 c 2
8 c NA
9 c 3
答案 5 :(得分:1)
更改了df
以使示例更详尽-
df1 <- data.frame(id = rep(c("a", "b","c"), each = 4),
val = c(NA, NA, NA, NA, 1, 2, 2, 3,NA,2,NA,3))
library(dplyr)
df1 %>%
group_by(id) %>%
mutate(case=sum(is.na(val))==n(), row_num=row_number() ) %>%
mutate(val=ifelse(is.na(val)&case,0,val)) %>%
filter( !(case&row_num!=1) ) %>%
select(id, val)
输出
id val
<fct> <dbl>
1 a 0
2 b 1
3 b 2
4 b 2
5 b 3
6 c NA
7 c 2
8 c NA
9 c 3
答案 6 :(得分:1)
另一种基本方法,它不保持行的顺序,并利用了记住丢失值的因素:
df1 <- na.omit(df1)
df1 <- rbind(
df1,
data.frame(
id = levels(df1$id)[!levels(df1$id) %in% df1$id],
val = 0)
)
我个人更喜欢Sotos给出的dplyr方法,因为我不喜欢rbind
-将data.frames放回去,所以这是个问题,但这在我看来并不复杂。使用变量id
适应字符unique(df1$id)
列很容易。
答案 7 :(得分:0)
这是基本的R解决方案。
res <- lapply(split(df1, df1$id), function(DF){
if(anyNA(DF$val)) {
i <- is.na(DF$val)
DF$val[i] <- 0
DF <- rbind(DF[i & !duplicated(DF[i, ]), ], DF[!i, ])
}
DF
})
res <- do.call(rbind, res)
row.names(res) <- NULL
res
# id val
#1 a 0
#2 b 1
#3 b 2
#4 b 2
#5 b 3
编辑。
以下是一个dplyr
解决方案。
使用OP发布的原始数据集,Vivek Kalyanarangan's answer中的数据集和markus' comment中的数据集(分别重命名为df2
和df3
)进行了测试。
library(dplyr)
na2zero <- function(DF){
DF %>%
group_by(id) %>%
mutate(val = ifelse(is.na(val), 0, val),
crit = val == 0 & duplicated(val)) %>%
filter(!crit) %>%
select(-crit)
}
na2zero(df1)
na2zero(df2)
na2zero(df3)
答案 8 :(得分:0)
可以尝试以下方法:
df1 = data.frame(id = rep(c("a", "b","c"), each = 4),
val = c(NA, NA, NA, NA, 1, 2, 2, 3,NA,2,NA,3))
df1
# id val
#1 a NA
#2 a NA
#3 a NA
#4 a NA
#5 b 1
#6 b 2
#7 b 2
#8 b 3
#9 c NA
#10 c 2
#11 c NA
#12 c 3
任务是删除与id
个val
对应的id
IFF NA
对应的所有行,并添加新行与此id
和val = 0
一起。
在此示例中,id = a
。
注意:val
的{{1}}也有c
个,但是与NA
对应的所有val
都不是c
,因此我们需要删除NA
的相应行,其中c
。
因此,让我们创建另一列,例如val = NA
,它指示val2
表示其所有0
,否则为1。
NA
获取library(dplyr)
df1 = df1 %>%
group_by(id) %>%
mutate(val2 = if_else(condition = all(is.na(val)),true = 0, false = 1))
df1
# A tibble: 12 x 3
# Groups: id [3]
# id val val2
# <fct> <dbl> <dbl>
#1 a NA 0
#2 a NA 0
#3 a NA 0
#4 a NA 0
#5 b 1 1
#6 b 2 1
#7 b 2 1
#8 b 3 1
#9 c NA 1
#10 c 2 1
#11 c NA 1
#12 c 3 1
的列表以及所有对应的id
。
val = NA
然后使用all_na = unique(df1$id[df1$val2 == 0])
从数据帧id
中删除df1
。
val = NA
并使用df1 = na.omit(df1)
df1
# A tibble: 6 x 3
# Groups: id [2]
# id val val2
# <fct> <dbl> <dbl>
# 1 b 1 1
# 2 b 2 1
# 3 b 2 1
# 4 b 3 1
# 5 c 2 1
# 6 c 3 1
和id
中的all_na
s创建一个新的数据框
val = 0
然后合并这两个数据帧。
all_na_df = data.frame(id = all_na, val = 0)
all_na_df
# id val
# 1 a 0
希望这会有所帮助,并且最欢迎您进行编辑:-)