【问题标题】:Inserting rows before each group在每个组之前插入行
【发布时间】:2017-08-18 13:55:55
【问题描述】:

我有以下列表,我想在每组 ID 保留 ID 并将 A 和 B 设置为 1.00 之前添加一个新行。

       ID      DATEE       A      B 
   102984 2016-11-23      2.0    2.0
   140349 2016-11-23      1.5    1.5
   167109 2017-04-16      2.0    2.0
   167109 2017-06-21      1.5    1.5

最终结果:

  ID      DATEE           A      B     
  102984    NA           1.0    1.0
  102984 2016-11-23      2.0    2.0       
  140349    NA           1.0    1.0      
  140349 2016-11-23      1.5    1.5
  167109    NA           1.0    1.0             
  167109 2017-04-16      2.0    2.0       
  167109 2017-06-21      1.5    1.5       

到目前为止,我使用以下代码在每个组的底部添加一个空行 do.call(rbind, by(df,df$ID,rbind,"")) 但是我无法介绍具体的当我将“”替换为值向量时,它们各自列中的值。

【问题讨论】:

标签: r


【解决方案1】:

这是tidyverse 的一个选项。我们通过'ID'获得distinct的数据集行,mutate变量'A','B'为1,'DATEE'为NA,然后将bind_rows行与原始数据集和@987654325绑定@ by 'ID'

library(tidyverse)
df1 %>%
  distinct(ID, .keep_all= TRUE) %>%
  mutate_at(vars("A", "B"), funs((1))) %>% 
  mutate(DATEE = NA) %>%
  bind_rows(., df1) %>%
  arrange(ID)
#     ID      DATEE   A   B
#1 102984       <NA> 1.0 1.0
#2 102984 2016-11-23 2.0 2.0
#3 140349       <NA> 1.0 1.0
#4 140349 2016-11-23 1.5 1.5
#5 167109       <NA> 1.0 1.0
#6 167109 2017-04-16 2.0 2.0
#7 167109 2017-06-21 1.5 1.5

(我假设日期格式已修复,例如,df1$DATEE = as.Date(df1$DATEE)。)


或者翻译成基础R:

new1 = data.frame(ID = unique(df1$ID), DATEE = Sys.Date()[NA_integer_], A = 1, B = 1)
tabs = list(new1, df1)
res  = do.call(rbind, tabs)
res <- res[order(res$ID), ]

#       ID      DATEE   A   B
# 1 102984       <NA> 1.0 1.0
# 4 102984 2016-11-23 2.0 2.0
# 2 140349       <NA> 1.0 1.0
# 5 140349 2016-11-23 1.5 1.5
# 3 167109       <NA> 1.0 1.0
# 6 167109 2017-04-16 2.0 2.0
# 7 167109 2017-06-21 1.5 1.5

或者用data.table:

library(data.table)
new1 = data.table(ID = unique(df1$ID), DATEE = Sys.Date()[NA_integer_], A = 1, B = 1)
tabs = list(new1, df1)
res  = rbindlist(tabs)
setorder(res)

#       ID      DATEE   A   B
#1: 102984       <NA> 1.0 1.0
#2: 102984 2016-11-23 2.0 2.0
#3: 140349       <NA> 1.0 1.0
#4: 140349 2016-11-23 1.5 1.5
#5: 167109       <NA> 1.0 1.0
#6: 167109 2017-04-16 2.0 2.0
#7: 167109 2017-06-21 1.5 1.5

还有其他一些方法:

# or let DATEE and other cols be filled as NA
library(data.table)
new1 = data.table(ID = unique(df1$ID), A = 1, B = 1)
tabs = list(df1, new1)
res  = rbindlist(tabs, fill = TRUE, idcol = "src")
setorder(res, ID, -src)
res[, src := NULL ]

# or a more compact option (assuming df1$A has no missing values)
library(data.table)
setDT(df1)[, .SD[c(.N+1, seq_len(.N))], ID][is.na(A), c("A", "B") := 1][]

【讨论】:

  • @Frank 感谢您的更新。我正在通话中,所以无法查看此内容
【解决方案2】:

这里有两个基于 R 的解决方案

1

根据ID分成子组,在每个子组的顶部添加一行,rbind所有内容都回到末尾。

do.call(rbind, lapply(split(df, df$ID), function(a){
    rbind(setNames(c(a$ID[1], NA, 1, 1), names(a)), a)
}))
#             ID      DATEE   A   B
#102984.1 102984       <NA> 1.0 1.0
#102984.2 102984 2016-11-23 2.0 2.0
#140349.1 140349       <NA> 1.0 1.0
#140349.2 140349 2016-11-23 1.5 1.5
#167109.1 167109       <NA> 1.0 1.0
#167109.3 167109 2017-04-16 2.0 2.0
#167109.4 167109 2017-06-21 1.5 1.5

2

或者您可以最初复制第一行(通过用 ave 标识它们),然后在每列中替换适当的值。

df = df[sort(c(1:NROW(df), which(ave(df$A, df$ID, FUN = seq_along) == 1))),]
df$DATEE = replace(df$DATEE, which(ave(df$A, df$ID, FUN = seq_along) == 1), NA)
df$A = replace(df$A, which(ave(df$A, df$ID, FUN = seq_along) == 1), 1)
df$B = replace(df$B, which(ave(df$A, df$ID, FUN = seq_along) == 1), 1)
df
#        ID      DATEE   A   B
#1   102984       <NA> 1.0 1.0
#1.1 102984 2016-11-23 2.0 2.0
#2   140349       <NA> 1.0 1.0
#2.1 140349 2016-11-23 1.5 1.5
#3   167109       <NA> 1.0 1.0
#3.1 167109 2017-04-16 2.0 2.0
#4   167109 2017-06-21 1.5 1.5

【讨论】:

    【解决方案3】:

    使用purrr 的另一个想法。首先,我们split()ID 的数据,然后我们使用imap(索引映射)和dfr(返回由行绑定创建的数据帧)循环每个组和add_row() 指定的值.

    library(tidyverse)
    
    df %>%
      split(.$ID) %>%
      # We don't have to specify "DATEE", absent variables get missing values
      imap_dfr(~ add_row(.x, ID = .y, A = 1, B = 1, .before = 1))
    

    这给出了:

    #      ID      DATEE   A   B
    #1 102984       <NA> 1.0 1.0
    #2 102984 2016-11-23 2.0 2.0
    #3 140349       <NA> 1.0 1.0
    #4 140349 2016-11-23 1.5 1.5
    #5 167109       <NA> 1.0 1.0
    #6 167109 2017-04-16 2.0 2.0
    #7 167109 2017-06-21 1.5 1.5
    

    来自文档:

    imap_xxx(x, ...) 是索引映射,如果x 有名称,则为map2(x, names(x), ...) 的简写,如果没有名称,则为map2(x, seq_along(x), ...)。 如果您需要同时计算值和 元素的位置。

    【讨论】:

      【解决方案4】:

      找到不重复的索引u,然后重复这些行并给出DF2。然后在DF2 中找到不重复的uu,并将 NA, 1, 1 插入除第一列之外的那些行中。没有使用任何包。

      u <- !duplicated(DF$ID)
      DF2 <- DF[rep(1:nrow(DF), 1 + u), ]
      uu <- !duplicated(DF2$ID)
      DF2[uu, -1] <- list(NA, 1, 1)
      

      给予:

      > DF2
              ID      DATEE   A   B
      1   102984       <NA> 1.0 1.0
      1.1 102984 2016-11-23 2.0 2.0
      2   140349       <NA> 1.0 1.0
      2.1 140349 2016-11-23 1.5 1.5
      3   167109       <NA> 1.0 1.0
      3.1 167109 2017-04-16 2.0 2.0
      4   167109 2017-06-21 1.5 1.5
      

      注意:可重现形式的输入是:

      Lines <- "
           ID      DATEE       A      B 
         102984 2016-11-23      2.0    2.0
         140349 2016-11-23      1.5    1.5
         167109 2017-04-16      2.0    2.0
         167109 2017-06-21      1.5    1.5"
      DF <- read.table(text = Lines, header = TRUE)
      

      更新:已更正输出(代码正确但输出不对应)以及简化代码。

      【讨论】:

        【解决方案5】:

        加入这个聚会,这是另一个基础 R 解决方案。我们复制行名以扩展我们的数据框,然后简单地替换值

        d1 <- df[rep(rownames(df), (!duplicated(df$ID)) + 1),]
        d1$DATEE <- replace(d1$DATEE, !duplicated(d1$ID), NA)
        d1[-c(1:2)] <- lapply(d1[-c(1:2)], function(i) replace(i, is.na(d1$DATEE), 1))
        

        这给了,

               ID      DATEE   A   B
        1   102984       <NA> 1.0 1.0
        1.1 102984 2016-11-23 2.0 2.0
        2   140349       <NA> 1.0 1.0
        2.1 140349 2016-11-23 1.5 1.5
        3   167109       <NA> 1.0 1.0
        3.1 167109 2017-04-16 2.0 2.0
        4   167109 2017-06-21 1.5 1.5
        

        【讨论】:

        • 最后一行取决于原始表中没有合法的 NA?
        • @Frank 不是原版。我更新的那个DATEE
        • 不确定我是否关注。我的意思是,使用df = data.frame(ID = 1, DATEE = Sys.Date()[NA_integer_], A = 2, B = 3),您将覆盖我对 A 和 B 的值,因为原始日期是 NA...?
        • @Frank 哦,好的,我知道了。我不确定它会如何表现。我将使用duplicated 条件进行更新以确保
        【解决方案6】:

        我们还可以使用您想要使用的by 函数,甚至可以使用基础R 中的tapply 函数。对于tapply,请确保将INDICES 放在一个列表中,因为这是一个数据框。敌人by 没有必要将其放入列表中。因此,在下面的代码中,我们可以将by(A,A$ID... 替换为tapply(A,list(A$ID)...,两者都会得到相同的结果。

        `rownames<-`(do.call(rbind,by(A,A$ID,
                          function(i) rbind(data.frame(ID=i$ID[1],DATEE=NA,A=1,B=1),i))),NULL)
              ID      DATEE   A   B
        1 102984       <NA> 1.0 1.0
        2 102984 2016-11-23 2.0 2.0
        3 140349       <NA> 1.0 1.0
        4 140349 2016-11-23 1.5 1.5
        5 167109       <NA> 1.0 1.0
        6 167109 2017-04-16 2.0 2.0
        7 167109 2017-06-21 1.5 1.5
        

        这不需要排序,因为这可能会扭曲数据之前的顺序。

        【讨论】:

        • rownames&lt;- 对于某些人来说可能很难理解。也许值得用res &lt;- do.call...; rownames(res) &lt;- NULL 展示详细/两行的方式。
        • 你是对的。虽然在 R 中学习新技巧也很好。并且它应该是(rownames(res) &lt;-NULL),以便您能够同时查看结果。
        • 我认为你可以使用 rbind 的参数,make.row.names = FALSE... 所以也许是do.call(rbind, c(by(...), make.row.names = FALSE),但我不确定。你可以检查
        猜你喜欢
        • 2021-12-13
        • 2014-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-27
        相关资源
        最近更新 更多