用等于 0 的一行替换变量的所有 NA 值
Replace all NA values for variable with one row equal to 0
词组有点难,据我所见none个类似问题回答了我的问题。
我有一个 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 值(在这种情况下它删除了 [=28= 的每个实例]) 插入一个额外的行 (例如) 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
显然很容易以迂回的方式做到这一点,但我想知道是否有 tidy/elegant 的方法来做到这一点。我认为 tidyr::complete() 可能会有所帮助,但不完全确定如何将其应用于这种情况
我不关心行的顺序
干杯!
编辑:更新了更清晰的期望输出。可能会使之前提交的所需答案变得不太清楚
Base R 选项是通过将 val
更改为 0 和 select 仅 unique
行来查找包含所有 NA
和 transform
的组这样每组只有一行。我们 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
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
这是一个基本的 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 发布的原始数据集进行了测试,数据集在 and with the dataset in 中,分别重命名为 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)
我们可以
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
。)
这里也有一个选项:
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()
根据其他要求更新:
一些用户建议在此数据框上进行测试。当然,这个答案假设您会亲手查看所有内容。如果您必须按 "hand" 查看所有内容,可能用处不大,但这里有:
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
更改了 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
使用 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
另一种基本方法,它不维护行的顺序并利用记住丢失值的因素:
df1 <- na.omit(df1)
df1 <- rbind(
df1,
data.frame(
id = levels(df1$id)[!levels(df1$id) %in% df1$id],
val = 0)
)
我个人更喜欢 Sotos 给出的 dplyr 方法,因为我不喜欢 rbind
-ing data.frames 重新组合在一起,所以这是一个品味问题,但这并不是难以忍受的复杂在我眼里。使用 unique(df1$id)
变量适应字符 id
列很容易。
可以试试这个:
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
Task是移除所有对应于任何id
的行 IFF val
for the corresponding id
is all NA
s 并使用 id
和 val = 0
.
添加新行
在这个例子中,id = a
.
注意:c
的val
也有NA
,但是c
对应的所有val
都不是NA
,因此我们需要删除 c
where val = NA
的相应行。
因此,让我们创建另一列,例如,val2
表示 0
表示所有 NA
,否则为 1。
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])
然后用 val = NA
.
从数据帧 df1
中删除 id
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
并在 all_na
和 val = 0
中创建一个包含 id
s 的新数据框
all_na_df = data.frame(id = all_na, val = 0)
all_na_df
# id val
# 1 a 0
然后合并这两个数据帧。
df1 = bind_rows(all_na_df, df1[,c('id', 'val')])
df1
# id val
# 1 a 0
# 2 b 1
# 3 b 2
# 4 b 2
# 5 b 3
# 6 c 2
# 7 c 3
希望这对您有所帮助,欢迎编辑:-)
词组有点难,据我所见none个类似问题回答了我的问题。
我有一个 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 值(在这种情况下它删除了 [=28= 的每个实例]) 插入一个额外的行 (例如) 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
显然很容易以迂回的方式做到这一点,但我想知道是否有 tidy/elegant 的方法来做到这一点。我认为 tidyr::complete() 可能会有所帮助,但不完全确定如何将其应用于这种情况
我不关心行的顺序
干杯!
编辑:更新了更清晰的期望输出。可能会使之前提交的所需答案变得不太清楚
Base R 选项是通过将 val
更改为 0 和 select 仅 unique
行来查找包含所有 NA
和 transform
的组这样每组只有一行。我们 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
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
这是一个基本的 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 发布的原始数据集进行了测试,数据集在 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)
我们可以
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
。)
这里也有一个选项:
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()
根据其他要求更新: 一些用户建议在此数据框上进行测试。当然,这个答案假设您会亲手查看所有内容。如果您必须按 "hand" 查看所有内容,可能用处不大,但这里有:
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
更改了 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
使用 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
另一种基本方法,它不维护行的顺序并利用记住丢失值的因素:
df1 <- na.omit(df1)
df1 <- rbind(
df1,
data.frame(
id = levels(df1$id)[!levels(df1$id) %in% df1$id],
val = 0)
)
我个人更喜欢 Sotos 给出的 dplyr 方法,因为我不喜欢 rbind
-ing data.frames 重新组合在一起,所以这是一个品味问题,但这并不是难以忍受的复杂在我眼里。使用 unique(df1$id)
变量适应字符 id
列很容易。
可以试试这个:
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
Task是移除所有对应于任何id
的行 IFF val
for the corresponding id
is all NA
s 并使用 id
和 val = 0
.
添加新行
在这个例子中,id = a
.
注意:c
的val
也有NA
,但是c
对应的所有val
都不是NA
,因此我们需要删除 c
where val = NA
的相应行。
因此,让我们创建另一列,例如,val2
表示 0
表示所有 NA
,否则为 1。
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])
然后用 val = NA
.
df1
中删除 id
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
并在 all_na
和 val = 0
id
s 的新数据框
all_na_df = data.frame(id = all_na, val = 0)
all_na_df
# id val
# 1 a 0
然后合并这两个数据帧。
df1 = bind_rows(all_na_df, df1[,c('id', 'val')])
df1
# id val
# 1 a 0
# 2 b 1
# 3 b 2
# 4 b 2
# 5 b 3
# 6 c 2
# 7 c 3
希望这对您有所帮助,欢迎编辑:-)