【问题标题】:Extracting one value of a group to new column将组的一个值提取到新列
【发布时间】:2020-09-23 17:31:38
【问题描述】:

我正在尝试在长格式数据结构中提取组的一个值,并将其分散到一列中。最好用一个例子来解释。请参阅下面的示例数据。在这种情况下,我想提取 c 的值并根据数据中存在的分组将其复制到新列中。

我正在寻找一种优雅的方式来实现这一点,尤其是tidyverse 解决方案将是理想的。

  year month location group value
  2019     1 top      a         1
  2019     1 top      b         2
  2019     1 top      c         3
  2019     1 bottom   a         4
  2019     1 bottom   b         5
  2019     1 bottom   c         6
  2019     2 top      a         7
  2019     2 top      b         8
  2019     2 top      c         9
  2019     2 bottom   a        10
  2019     2 bottom   b        11
  2019     2 bottom   c        12

这是预期的输出:

  year month location group value c_value
  2019     1 top      a         1       3
  2019     1 top      b         2       3
  2019     1 top      c         3       3
  2019     1 bottom   a         4       6
  2019     1 bottom   b         5       6
  2019     1 bottom   c         6       6
  2019     2 top      a         7       9
  2019     2 top      b         8       9
  2019     2 top      c         9       9
  2019     2 bottom   a        10      12
  2019     2 bottom   b        11      12
  2019     2 bottom   c        12      12

还有数据:

structure(list(year = c(2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019), month = c(1, 1, 1, 1, 1, 1, 2, 
2, 2, 2, 2, 2), location = c("top", "top", "top", "bottom", "bottom", 
"bottom", "top", "top", "top", "bottom", "bottom", "bottom"), 
    group = c("a", "b", "c", "a", "b", "c", "a", "b", "c", "a", 
    "b", "c"), value = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
    )), row.names = c(NA, -12L), class = c("tbl_df", "tbl", "data.frame"
))

编辑:

我确实想出了一个两部分的解决方案,但我仍然认为有更好的方法。

lookup <- df %>%
  group_by(year, month, location) %>%
  filter(group == "c") %>%
  summarize(c_value = value)


df %>%
  left_join(lookup, by = c("year", "month", "location"))

【问题讨论】:

    标签: r dplyr


    【解决方案1】:
    dat %>%
      group_by(year, month, location) %>%
      mutate(c_value = value[group == "c"][1]) %>%
      ungroup()
    # # A tibble: 12 x 6
    #     year month location group value c_value
    #    <dbl> <dbl> <chr>    <chr> <dbl>   <dbl>
    #  1  2019     1 top      a         1       3
    #  2  2019     1 top      b         2       3
    #  3  2019     1 top      c         3       3
    #  4  2019     1 bottom   a         4       6
    #  5  2019     1 bottom   b         5       6
    #  6  2019     1 bottom   c         6       6
    #  7  2019     2 top      a         7       9
    #  8  2019     2 top      b         8       9
    #  9  2019     2 top      c         9       9
    # 10  2019     2 bottom   a        10      12
    # 11  2019     2 bottom   b        11      12
    # 12  2019     2 bottom   c        12      12
    

    额外的[1] 可以防止两种错误情况:

    1. "c" 未找到:

      dat %>%
        group_by(year, month, location) %>%
        mutate(c_value = value[group == "d"]) %>%
        ungroup()
      # Error: Problem with `mutate()` input `c_value`.
      # x Input `c_value` can't be recycled to size 3.
      # i Input `c_value` is `value[group == "d"]`.
      # i Input `c_value` must be size 3 or 1, not 0.
      # i The error occured in group 1: year = 2019, month = 1, location = "bottom".
      
    2. 找到多个"c"

      dat$group[2] <- "c"
      dat %>%
        group_by(year, month, location) %>%
        mutate(c_value = value[group == "c"]) %>%
        ungroup()
      # Error: Problem with `mutate()` input `c_value`.
      # x Input `c_value` can't be recycled to size 3.
      # i Input `c_value` is `value[group == "c"]`.
      # i Input `c_value` must be size 3 or 1, not 2.
      

    两者都通过[1] 缓解,尽管第二个被静默截断。使用原始数据:

    dat %>%
      group_by(year, month, location) %>%
      mutate(c_value = value[group == "d"][1]) %>%
      ungroup()
    # # A tibble: 12 x 6
    #     year month location group value c_value
    #    <dbl> <dbl> <chr>    <chr> <dbl>   <dbl>
    #  1  2019     1 top      a         1      NA
    #  2  2019     1 top      b         2      NA
    #  3  2019     1 top      c         3      NA
    #  4  2019     1 bottom   a         4      NA
    #  5  2019     1 bottom   b         5      NA
    #  6  2019     1 bottom   c         6      NA
    #  7  2019     2 top      a         7      NA
    #  8  2019     2 top      b         8      NA
    #  9  2019     2 top      c         9      NA
    # 10  2019     2 bottom   a        10      NA
    # 11  2019     2 bottom   b        11      NA
    # 12  2019     2 bottom   c        12      NA
    
    dat$group[2] <- "c"
    dat %>%
      group_by(year, month, location) %>%
      mutate(c_value = value[group == "c"][1]) %>%
      ungroup()
    # # A tibble: 12 x 6
    #     year month location group value c_value
    #    <dbl> <dbl> <chr>    <chr> <dbl>   <dbl>
    #  1  2019     1 top      a         1       2
    #  2  2019     1 top      c         2       2
    #  3  2019     1 top      c         3       2
    #  4  2019     1 bottom   a         4       6
    #  5  2019     1 bottom   b         5       6
    #  6  2019     1 bottom   c         6       6
    #  7  2019     2 top      a         7       9
    #  8  2019     2 top      b         8       9
    #  9  2019     2 top      c         9       9
    # 10  2019     2 bottom   a        10      12
    # 11  2019     2 bottom   b        11      12
    # 12  2019     2 bottom   c        12      12
    

    您的left_join 的替代品要短一些:

    filter(dat, group == "c") %>%
      select(-group, c_value = value) %>%
      left_join(dat, ., by = c("year", "month", "location"))
    

    【讨论】:

    • [1] 是一个简洁的功能。在这种情况下,我希望只有一个值,所以我会忽略它,以便我确实看到错误。但也可用于获取组 1、2、3 等的特定索引。
    • 查看我的编辑以获得更短的连接逻辑版本,我认为这是迄今为止所有选项中最简洁的。
    【解决方案2】:
    df %>% 
      group_by(year, month, location) %>% 
      mutate(c_value = value[match("c", group)])
    
    # A tibble: 12 x 6
        year month location group value c_value
       <dbl> <dbl> <chr>    <chr> <dbl>   <dbl>
     1  2019     1 top      a         1       3
     2  2019     1 top      b         2       3
     3  2019     1 top      c         3       3
     4  2019     1 bottom   a         4       6
     5  2019     1 bottom   b         5       6
     6  2019     1 bottom   c         6       6
     7  2019     2 top      a         7       9
     8  2019     2 top      b         8       9
     9  2019     2 top      c         9       9
    10  2019     2 bottom   a        10      12
    11  2019     2 bottom   b        11      12
    12  2019     2 bottom   c        12      12
    

    使用data.table

    library(data.table)
    dt <- as.data.table(df)
    dt[, c_value := value[match("c", group)], by = c("year", "month", "location")]
    

    【讨论】:

      【解决方案3】:

      您还可以尝试使用mutate() 内的括号对group 进行逻辑测试来提取value。代码如下:

      library(dplyr)
      library(tidyr)
      #Code
      df %>% group_by(year,month,location) %>%
        mutate(value_c=value[group=='c'])
      

      输出:

      # A tibble: 12 x 6
      # Groups:   year, month, location [4]
          year month location group value value_c
         <int> <int> <chr>    <chr> <int>   <int>
       1  2019     1 top      a         1       3
       2  2019     1 top      b         2       3
       3  2019     1 top      c         3       3
       4  2019     1 bottom   a         4       6
       5  2019     1 bottom   b         5       6
       6  2019     1 bottom   c         6       6
       7  2019     2 top      a         7       9
       8  2019     2 top      b         8       9
       9  2019     2 top      c         9       9
      10  2019     2 bottom   a        10      12
      11  2019     2 bottom   b        11      12
      12  2019     2 bottom   c        12      12
      

      使用的一些数据:

      #Data
      df <- structure(list(year = c(2019L, 2019L, 2019L, 2019L, 2019L, 2019L, 
      2019L, 2019L, 2019L, 2019L, 2019L, 2019L), month = c(1L, 1L, 
      1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L), location = c("top", 
      "top", "top", "bottom", "bottom", "bottom", "top", "top", "top", 
      "bottom", "bottom", "bottom"), group = c("a", "b", "c", "a", 
      "b", "c", "a", "b", "c", "a", "b", "c"), value = 1:12), class = "data.frame", row.names = c(NA, 
      -12L))
      

      【讨论】:

        【解决方案4】:

        这是使用merge + subset 的基本 R 选项

        merge(
          subset(df, select = -value),
          subset(df, group == "c", select = -group),
          all = TRUE
        )
        

        给了

           year month location group value
        1  2019     1   bottom     a     6
        2  2019     1   bottom     b     6
        3  2019     1   bottom     c     6
        4  2019     1      top     a     3
        5  2019     1      top     b     3
        6  2019     1      top     c     3
        7  2019     2   bottom     a    12
        8  2019     2   bottom     b    12
        9  2019     2   bottom     c    12
        10 2019     2      top     a     9
        11 2019     2      top     b     9
        12 2019     2      top     c     9
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-02
          • 1970-01-01
          • 2022-06-29
          • 2020-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多