【问题标题】:Inserting new rows using dplyr / data.table使用 dplyr / data.table 插入新行
【发布时间】:2019-03-06 08:22:05
【问题描述】:

样本数据

set.seed(123)
df <- data.frame(year = c(rep(1980:1994, each = 9), rep(1995, times = 8), rep(1996:2012, each = 9), 
                          rep(2013, times = 7), rep(2014, times = 9)),
                 ref.doy = sample(120:180, 312, replace = T),
                 x = rnorm(312))

每年,如果没有。 ref.doy 不是 9,那么我想插入额外的新行,这只是最后一行的副本。

例如如果 1995 年只有 8 个ref.doy,我想复制第 8 行使其成为第 9 行。如果 2013 年只有 8 个ref.doy,那么我想复制第 7 行并将其复制为第 8 行和第 9 行等等。

我目前的解决方案是for循环:

x <- df %>% group_by(year) %>% dplyr::mutate(y.length = n())
year.vec <- 1980:2014
temp.list <- list()

for(y in seq_along(year.vec)){

  yr <- year.vec[y]
  temp <- x %>% dplyr::filter(year == yr)  

  if(unique(temp$y.length) != 9) {

    lastrow <- temp[nrow(temp), ]
    lastrow.repeat <- as.data.frame(lapply(lastrow, rep, 9 - nrow(temp))) 
    full.data  <- rbind(data.frame(temp), lastrow.repeat)         
    temp.list[[y]] <- full.data

    } else {
    temp.list[[y]] <- temp
  }
}

newdata <- rbindlist(temp.list)

我需要一些帮助才能在 dplyrdata.table 本身内完成。

【问题讨论】:

  • 如果您使用的是示例,请在使用set.seed() 运行代码之前设置种子。否则结果无法重现。
  • 好的。我去做。谢谢

标签: r dplyr data.table


【解决方案1】:

以您最近两年的数据(分别在 2013 年和 2014 年有 5 个和 9 个条目)作为样本。我们filter 少于 9 行的组,对于这些组,我们重复最后一行 9 - n() 次,并使用 bind_rows 将这些行添加到原始数据帧中。

df1 <- tail(df, 14)

library(dplyr)

df1 %>% 
    bind_rows(df1 %>%
               group_by(year) %>%
               #suggested by @Henrik
               filter(n() < 9) %>%
               slice(rep(n(), 9 - n()))) %>%
     arrange(year)


#   year ref.doy          x
#1  2013     126  0.9171749
#2  2013     168 -2.6609228
#3  2013     167  1.1102771
#4  2013     120 -0.4849876
#5  2013     167  0.2306168
#6  2013     167  0.2306168
#7  2013     167  0.2306168
#8  2013     167  0.2306168
#9  2013     167  0.2306168
#10 2014     164 -0.2951578
#11 2014     158  0.8719650
#12 2014     149 -0.3484724
#13 2014     129  0.5185038
#14 2014     120 -0.3906850
#15 2014     147 -1.0927872
#16 2014     150  1.2100105
#17 2014     143  0.7409000
#18 2014     148  1.7242622

将此应用于原始数据帧,我们会检查每个 year 的行数。

df2 <- df %>% 
          bind_rows(df %>%
                      group_by(year) %>%
                      filter(n() < 9) %>%
                      slice(rep(n(), 9 - n()))) %>%
          arrange(year)


df2 %>%
   group_by(year) %>%
   summarise(no_of_rows = n())
# A tibble: 35 x 2
# year no_of_rows
#   <dbl>      <int>
# 1  1980          9
# 2  1981          9
# 3  1982          9
# 4  1983          9
# 5  1984          9
# 6  1985          9
# 7  1986          9
# 8  1987          9
# 9  1988          9
#10  1989          9
# ... with 25 more rows

或者正如@Henrik 所提到的,最简单的方法是取每组的最后一行并重复9 - n() 次,而不管。

df %>% 
   group_by(year) %>% 
   slice(c(1:n(), rep(n(), 9 - n())))

【讨论】:

  • 1995年也有8年的数据。我希望答案可以推广到ref.doy 的任何组合
  • @Henrik 是的..我认为在这种情况下可以考虑。如果 OP 需要 9 个不同的 ref.doy 或 9 行,我感到很困惑。谢谢:)
  • 很抱歉回复晚了,但是是的,我只需要将每年的行数或条目数设为 9。
  • @RonakShah 也许df %&gt;% group_by(year) %&gt;% slice(c(1:n(), rep(n(), 9 - n()))) 会起作用(类似于我的comment above)?
  • @Henrik 太棒了!完全有道理。用您的建议更新了答案。我之前没看评论。
【解决方案2】:

使用

library(data.table)
setDT(df)

df[, ri := rowid(year)]

df2 <- df[CJ(year = year, ri = 1:9, unique = TRUE), on = .(year, ri)
          ][, (2:3) := lapply(.SD, zoo::na.locf), .SDcols = 2:3
            ][, ri := NULL][]

它给出了预期的结果:

> df2[year %in% c(1995,2013)]
    year ref.doy           x
 1: 1995     160  1.05418102
 2: 1995     170  1.14526311
 3: 1995     167 -0.57746800
 4: 1995     179  2.00248273
 5: 1995     146  0.06670087
 6: 1995     139  1.86685184
 7: 1995     144 -1.35090269
 8: 1995     120  0.02098359
 9: 1995     120  0.02098359
10: 2013     179  0.43528895
11: 2013     126  0.71517841
12: 2013     126  0.91717492
13: 2013     168 -2.66092280
14: 2013     167  1.11027710
15: 2013     120 -0.48498760
16: 2013     167  0.23061683
17: 2013     167  0.23061683
18: 2013     167  0.23061683

这是做什么的:

  1. df[, ri := rowid(year)] 通过year 添加行号
  2. 然后加入一个每年有九行的参考表 (CJ(year = year, ri = 1:9, unique = TRUE))。结果现在将包含没有九行的年份的空行。
  3. (2:3) := lapply(.SD, zoo::na.locf), .SDcols = 2:3 填充空行
  4. 最后,删除 ri 列,因为 ri := NULL 不再需要它

@Henrik 在 cmets 中发布的更好的选择:

df2 <- df[ , .SD[c(1:.N, rep(.N, 9 - .N))], by = year]

【讨论】:

    猜你喜欢
    • 2020-08-06
    • 1970-01-01
    • 2013-05-15
    • 2015-02-15
    • 2017-01-16
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 2019-11-18
    相关资源
    最近更新 更多