用等于0的一行替换变量的所有NA值

时间:2019-01-03 12:43:15

标签: r na complete

据我所知,没有一个类似的问题能够回答我的问题。

我有一个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:更新后具有更清晰的所需输出。可能会使之前提交的所需答案不太清楚

9 个答案:

答案 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来查找具有所有transformval的组,并仅选择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值的组,我们添加其idval为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中的数据集(分别重命名为df2df3)进行了测试。

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

任务是删除与idval对应的id IFF NA对应的所有行,并添加新行与此idval = 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

希望这会有所帮助,并且最欢迎您进行编辑:-)