R:如何根据列

时间:2018-03-15 18:53:59

标签: r

我有一个如下所示的数据框:

line = c(1, 2, NA, 4 ,5, NA, 7)
group = c("1.0 Group A", "2.0 Group B", "3.0 Group C", "4.0 Group D", "5.0  Group E", "6.0 Group F", "7.0 Group G")
df <- data.frame(line, group)

view(df)
   line    group
1    1   1.0 Group A
2    2   2.0 Group B
3   NA   3.0 Group C
4    4   4.0 Group D
5    5   5.0 Group E
6   NA   6.0 Group F
7    7   7.0 Group G

我想要做的是找到“line”列中的所有NA值,并在“group”列中的该行下面写一行“Not Applicable”。这样新的数据框应如下所示:

view(df)
    line    group
1    1   1.0 Group A
2    2   2.0 Group B
3   NA   3.0 Group C
4   NA   Not Applicable
5    4   4.0 Group D
6    5   5.0 Group E
7   NA   6.0 Group F
8   NA  Not Applicable
9    7   7.0 Group G

我正在考虑使用ifelse语句或使用dplyr中的case_when。但我不知道如何解决这个问题。有没有人有任何建议?

谢谢!

4 个答案:

答案 0 :(得分:4)

这是一个基本R方法:按累计NA计数拆分数据,添加新行,重新组合。

    df$group = as.character(df$group)
    split_df = split(df, cumsum(is.na(df$line)))
    split_df[-1] = lapply(split_df[-1], function(d) rbind(d[1, ], data.frame(line = NA, group = "Not applicable"), d[-1, ]))
    do.call(rbind, split_df)
    #     line          group
    # 0.1    1    1.0 Group A
    # 0.2    2    2.0 Group B
    # 1.3   NA    3.0 Group C
    # 1.1   NA Not applicable
    # 1.4    4    4.0 Group D
    # 1.5    5   5.0  Group E
    # 2.6   NA    6.0 Group F
    # 2.1   NA Not applicable
    # 2.7    7    7.0 Group G

请注意,我已将group转换为character,以便轻松添加新值,并将NA列入line列 - 您无法做到在数字向量中有空格,每个元素都需要是数字或NA

答案 1 :(得分:3)

使用dplyr可以实现解决方案。

方法很简单。添加一个代表row number的列。取出行值为NA的行。将group替换为Not Applicable,将row number列增加0.5。绑定这两个数据帧。

library(dplyr)

df %>% mutate(rownum = row_number()) %>% 
  bind_rows(., filter(., is.na(line)) %>% 
  mutate(group = "Not Applicable", rownum = rownum+.5)) %>% 
  arrange(rownum) %>%
  select(-rownum)


# line          group
# 1    1    1.0 Group A
# 2    2    2.0 Group B
# 3   NA    3.0 Group C
# 4   NA Not Applicable
# 5    4    4.0 Group D
# 6    5   5.0  Group E
# 7   NA    6.0 Group F
# 8   NA Not Applicable
# 9    7    7.0 Group G

@Gregor提到的限制是有效的。数字列的值可以为NA,但不能为blank

答案 2 :(得分:3)

创建一个单独的data.frame,ds_blank,然后使用联合查询进行堆叠,然后通过名为index的临时变量对其进行排序。

library(magrittr)
na_index <- which(is.na(df$line))

ds_blank <- tibble::tibble(
  index   = na_index + .5,
  line    = rep(NA_real_          , length(na_index)),
  group   = rep("Not Applicable"  , length(na_index))
)

df <- df %>% 
  tibble::rowid_to_column("index") %>% 
  dplyr::union(ds_blank) %>% 
  dplyr::arrange(index) %>% 
  dplyr::select(-index)

结果

> df
  line          group
1    1    1.0 Group A
2    2    2.0 Group B
3   NA    3.0 Group C
4   NA Not Applicable
5    4    4.0 Group D
6    5   5.0  Group E
7   NA    6.0 Group F
8   NA Not Applicable
9    7    7.0 Group G

我想尝试tibble::add_row(),但如果您指定了一个位置,那么显然不允许插入多行。

次要方法

...使用@Gregor的提示使用for循环。请注意,na_index现在已反向排序。

na_index <- sort(which(is.na(df$line)), decreasing = T)
for( i in na_index ) {
  df <- df %>% 
    tibble::add_row(
      line    = NA_integer_,
      group   = "Not Applicable",
      .after  = i
    )
}

答案 3 :(得分:0)

我觉得tidyr::uncount函数也可能是您想要的。只需在新列中用2标记line == NA行,我们将其称为n,然后uncount将根据n中的值复制每一行。通过对设置了NA行ifelse == 2的n进行变异,我们基本上只复制了NA行的正下方,而不是df的底部,因此需要{{1 }}。最后,如果arrange和上一行的dplyr::mutate_at(即lag(line))均为NA(告诉我们只关注这些重复的行)。我想也可以扩展这种方法!

group