【问题标题】:Replace all values with first observation by group用组的第一次观察替换所有值
【发布时间】:2021-05-28 06:35:06
【问题描述】:

对于由“id”定义的每个组,我想选择“x”和“y”列的第一行中的值,并将所有后续值替换为第一个值.

一些数据:

id    Visit   x        y
1      1      0        1
1      2      1        2
1      3      2        8
2      9      1       11
2      10     12      14

我想要:

id    Visit   x        y
1      1      0        1
1      2      0        1  # <- x & y replaced with first values of 'id' 1 
1      3      0        1  # 
2      9      1        11  
2      10     1        11 # <- x & y replaced with first values of 'id' 2 

我试过了:

df1 <- df %>%
  arrange(id, Visit) %>%
  group_by(id) %>%
  fill(x, y, 
       .direction = 'down',)

但是,似乎并没有这样做。有人可以帮忙吗?

【问题讨论】:

  • tidyr::fill 旨在填充缺失值,而不是填充现有值。也就是说,当您有 NA 存在时,它就可以工作。
  • 哦,我明白了。谢谢你的澄清!

标签: r dataframe dplyr


【解决方案1】:

如果您想要 @akrun 的精彩答案的 base-R 版本:

df[c("x","y")] <- lapply(df[c("x","y")], function(z) ave(z, df$id, FUN = function(y) y[1]))
df
#   id Visit x  y
# 1  1     1 0  1
# 2  1     2 0  1
# 3  1     3 0  1
# 4  2     9 1 11
# 5  2    10 1 11

(我故意避免使用dplyr::firstdata.table::first,因为这会破坏使用这个基本 R 版本的意义。)

或 data.table 变体:

library(data.table)
setDT(df)
df[, c("x","y") := lapply(.SD[,c("x","y")], first), by = .(id)]

正如@Henrik 刚刚提到的,在此处使用.SDcols (好多了!)更好:

df[, c("x","y") := lapply(.SD, first), by = .(id), .SDcols = c("x","y")]

【讨论】:

  • 嗨@r2evans!您应该避免使用.SD[...]:对于by 中的每次迭代,它都会调用[.data.table,这会产生很大的开销。而是使用df[, c("x","y") := lapply(.SD, first), .SDcols = c("x", "y"), by = id],这在处理较大数据时要快得多。干杯
  • 谢谢@Henrik,我不知道那里的性能问题。我对.SDcols 大体上很熟悉,我可以通过这些数据看到它可以在性能上产生 3 倍的差异。我没有在data.table docs 中看到它的注释,有没有提到它?
  • @Henrik,你知道是否有讨论过在 LHS 中使用 .SDcols 名称而不预先定义向量?例如,df[, (.SDcolnms) := lapply(.SD,...), .SDcols=c('x','y')]。我知道可以通过预先分配nms &lt;- c('x','y') 并在两个地方使用它来轻松处理它......只是好奇。再次感谢。
  • 关于名称,有一个流行的功能请求:names(.SD) := ... should work。敬请期待!
【解决方案2】:

使用duplicatedbase 替代方案:

df[, c("x", "y")] = df[(i = !duplicated(df$id)), c("x", "y")][cumsum(i), ]
#   id Visit x  y
# 1  1     1 0  1
# 2  1     2 0  1
# 3  1     3 0  1
# 4  2     9 1 11
# 5  2    10 1 11

使用data.table 滚动联接“填充”每个组中的第一个值(在较大数据上快速):

library(data.table)
setDT(df)
df[ , c("x", "y") := df[!duplicated(id)][.SD, on = .(id, Visit), .(x, y), roll = Inf]]
df
#    id Visit x  y
# 1:  1     1 0  1
# 2:  1     2 0  1
# 3:  1     3 0  1
# 4:  2     9 1 11
# 5:  2    10 1 11

【讨论】:

    【解决方案3】:

    另一个基本 R 选项

    with(
      df,
      cbind(
        df[c("id", "Visit")],
        cbind(x, y)[ave(1:nrow(df), id, FUN = function(x) head(x, 1)), ]
      )
    )
    

    给予

      id Visit x  y
    1  1     1 0  1
    2  1     2 0  1
    3  1     3 0  1
    4  2     9 1 11
    5  2    10 1 11
    

    【讨论】:

      【解决方案4】:

      我们可以在mutate 上使用firstacross

      library(dplyr)
      df %>%
          arrange(id, Visit) %>% 
          group_by(id) %>% 
          mutate(across(c(x, y), first)) %>%
          ungroup
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-20
        • 2013-07-21
        • 2021-11-15
        • 2020-09-13
        相关资源
        最近更新 更多