【问题标题】:Generate new variable with conditional means and NA's使用条件均值和 NA 生成新变量
【发布时间】:2019-10-12 01:41:11
【问题描述】:

我一直在为应该是一个非常简单的操作而苦苦挣扎,并尝试了不同的方法,但似乎都没有结果。

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

df <- data.frame(name = c("john", "paul", "ringo", "george", "john", "paul", "ringo", "george", "john", "paul", "ringo", "george"), 
                 year = c(2018, 2018, 2018, 2018, 2017, 2017, 2017, 2017, 2016, 2016, 2016, 2016),
                 station1 = c(1, 2, 3, NA, 2, NA, 5, 6, 7, 8, 9, 0),
                 station2 = c(NA, 6, 8, 1, 2, 6, NA, 1, NA, 1, 5, 3),
                 station3 = c(NA, 2, 3, 5, 1, NA, 1, 5, 3, 1, 2, 3),
                 station4 = c(9, 8, 7, 6, NA, 8, 12, 8, 83, 4, 3, NA))

现在,我需要的是创建一个新变量,我们称之为 new_station,它在每个给定年份的每个名称上取一个值。例如:

  • 对于 john,我需要 station1station3 的平均值。
  • 对于 paul,我只需要 station 4
  • 对于 ringo 我需要 station1、station2 的平均值, 站3;和
  • 对于 george,我只需要 station4

我尝试了几种过滤器、选择和变异的组合,大致如下:

df %>%
  filter(name == "john") %>%
  select(station1, station3) %>%
  mutate(new_station = rowMeans(c(station1, station3)))

但它不会让我将值仅分配给单行的值。当我只需要该特定年份的平均值时,其他一些尝试导致新列中的每一行成为所有 6 个单元格(2 个站 x 3 年)的平均值。我尝试过的其他方法无法处理存在一些缺失值的事实,我需要省略这些值。

我需要一种可扩展的循环,只需更改每个名称的条件,因为在现实生活中我有一个包含 21 个名称和 30 个站点的数据集。

有什么想法吗?

注意:如果它说明了我想要做什么,我知道如何在 Stata 中执行此操作。在 Stata 中,对于名称 john,它看起来像:

egen new_station = rowmean(station1 station3) if name == "john"

我只需要在 R 中做类似的事情。

谢谢!

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    我会将数据转换为长格式,然后使用case_when。完成后,您可以根据需要将其转换回宽幅。

    df$id = 1:nrow(df)
    
    library(tidyr) 
    df %>% pivot_longer(
        cols = starts_with("station"), 
        names_to = "station", names_prefix = "station",
        values_to = "value"
      ) %>%
      group_by(name, year) %>%
      mutate(result = case_when(
        name == "john" ~ mean(value[station %in% c(1, 3)], na.rm = TRUE),
        name %in% c("paul", "george") ~ value[station == 4],
        name == "ringo" ~ mean(value[station %in% c(1, 2, 3)], na.rm = TRUE)
      ))
    # # A tibble: 48 x 6
    # # Groups:   name, year [12]
    #    name   year    id station value result
    #    <fct> <dbl> <int> <chr>   <dbl>  <dbl>
    #  1 john   2018     1 1           1   1   
    #  2 john   2018     1 2          NA   1   
    #  3 john   2018     1 3          NA   1   
    #  4 john   2018     1 4           9   1   
    #  5 paul   2018     2 1           2   8   
    #  6 paul   2018     2 2           6   8   
    #  7 paul   2018     2 3           2   8   
    #  8 paul   2018     2 4           8   8   
    #  9 ringo  2018     3 1           3   4.67
    # 10 ringo  2018     3 2           8   4.67
    # # ... with 38 more rows
    

    【讨论】:

    • 非常感谢,这似乎可以完成工作!我对 tidyr 不是很了解,但要恢复到广泛我正在尝试这个,但不能完全到达那里:df %&gt;% pivot_wider(id_cols = id, names_prefix = "station", names_sep = "", values_from = "value")
    • 在这种情况下,我认为指定names_from 更容易:` ... %&gt;% pivot_wider(names_from = "station", names_prefix = "station", values_from = "value")
    【解决方案2】:

    这是 解决方案。它依赖于创建查找表并获取数据子集子集的rowMeans()。 :

    library(data.table)
    
    dt <- as.data.table(DF)
    dt[, name := as.character(name)]
    
    lookup <- list(john = c('station1', 'station3'),
                   paul = 'station4',
                   ringo = c('station1','station2','station3'),
                   george = 'station4')
    
    dt[,
       new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
       by = name]
    dt
    

    基于 OP cmets,在lookup 表的名称上子集dt 会更安全:

    dt <- as.data.table(DF)
    dt[, name := as.character(name)]
    
    lookup[[4]] <- NULL
    setdiff(dt[, name], names(lookup))
    
    # error
    dt[,
       new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
       by = name]
    # OK
    dt[name %in% names(lookup),
       new_station := .SD[, rowMeans(.SD), .SDcols = lookup[[unlist(.BY)]]],
       by = name]
    
    dt
    

    为了更好地了解正在发生的事情,我建议运行以下几行:

    dt <- as.data.table(DF)
    # what is .SD?
    dt[, print(.SD), by = name]
    dt[, .SD[,print(.SD) , .SDcols = lookup[[unlist(.BY)]]], by = name]
    
    #what is .BY?
    dt[, print(.BY), by = name]
    dt[, print(unlist(.BY)), by = name]
    dt[, name := as.character(name)]
    dt[, print(unlist(.BY)), by = name]
    

    参考资料:

    Data.table 的 S子集的精彩解释:What does .SD stand for in data.table in R

    【讨论】:

    • 谢谢!它适用于这个数据集,但是当我尝试我的实际数据集(形状几乎相同)时,我得到了这个错误:Error in [.data.table(.SD, , .SD, .SDcols = lookup[[as.character(unlist(.BY))]]) : .SDcols should be column numbers or names 有什么想法吗?跨度>
    • @alf10087 查看编辑。使用dt[name %in% names(lookup), ...] 更安全,应该可以防止错误。听起来dt 中的当前名称不在查找表中。
    猜你喜欢
    • 1970-01-01
    • 2020-12-09
    • 2013-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多