【问题标题】:Create new dataframe based on sequential row values根据顺序行值创建新数据框
【发布时间】:2021-08-18 06:46:42
【问题描述】:

我有一个名称和年份的数据框,其中有一个虚拟变量,用于判断名称是否出现在一年内。

我正在尝试创建一个告诉我的数据框

    1. 当年出现的名字总数,以及
    1. 当年出现但前一年未出现的数量。

在下面的示例中,2017 年只有一个人(特里)出现,而前一年没有,所以总数和新人都是 1。2018 年出现了三个人,但只有两个人是新人,因为特里出现在前一年。如果有人在 2017 年和 2019 年出现,但在 2018 年没有出现,则应将其归类为 2019 年的新人。

示例

   Name x2017 x2018 x2019
1 Terry     1     1     0
2   Sam     0     0     1
3   Nic     0     1     1
4 Sarah     0     1     1

代码

data.frame(
  Name = c("Terry", "Sam", "Nic", "Sarah"), 
  x2017 = c(1, 0, 0, 0), 
  x2018 = c(1, 0, 1, 1), 
  x2019 = c(0, 1, 1, 1)
  )

我正在尝试创建的输出

  Year Total New
1 2017     1   1
2 2018     3   2
3 2019     3   1

我尝试过过滤和使用行求和,但我觉得有一个我不知道的函数可以做到这一点。

谢谢!

【问题讨论】:

    标签: r dplyr tidyverse rolling-computation accumulate


    【解决方案1】:

    mutate(new = as.numeric(values == 1 & lag(values) == 0), new = ifelse(is.na(new), values, new)) %>%part 来自 stefan (感谢他,谢谢 stefan)。 区别是parse_number

    library(tidyverse)
    df %>% 
      pivot_longer(
        cols = -Name,
        names_to = "Year", 
        values_to = "values"
      ) %>% 
      mutate(Year = parse_number(Year)) %>% 
      mutate(new = as.numeric(values == 1 & lag(values) == 0),
             new = ifelse(is.na(new), values, new)) %>% 
      group_by(Year) %>% 
      summarise(Total = sum(values), New = sum(new))
    

    输出:

       Year Total   New
    * <dbl> <dbl> <dbl>
    1  2017     1     1
    2  2018     3     2
    3  2019     3     1
    

    【讨论】:

    • 不错的解决方案。起初我怀疑为什么我想不出更简洁的解决方案。显然没有任何关于这个问题的问题哈哈。
    【解决方案2】:

    02 年更新 很抱歉,我不得不修改我的解决方案,因为我意识到只有在前一年出现了一个名称时,它才被认为是新的,因此您也可以将其用于您的示例数据和页面下方显示的数据:

    library(dplyr)
    library(purrr)
    
    
    df %>% 
      summarise(across(2:4, ~ sum(.x))) %>%
      bind_cols() %>% 
      pivot_longer(everything(), names_to = "Year", values_to = "Total", 
                   names_prefix = "x") %>%
      left_join(df %>% select(2:4) %>% pmap_dfr(~ {x <- c(...); x - lag(x, default = 0)}) %>% 
                  summarise(across(everything(), ~ sum(.x == 1))) %>% 
                  pivot_longer(everything(), names_to = "Year", values_to = "New", 
                               names_prefix = "x"), 
                by = "Year")
     
    
    # A tibble: 3 x 3
      Year  Total   New
      <chr> <dbl> <dbl>
    1 2017      1     1
    2 2018      3     2
    3 2019      3     1
    

    【讨论】:

    • 我很高兴看到您的解决方案。这让我现在有能力学习purrr。谢谢阿努希拉万。也许你对我的这个问题感兴趣stackoverflow.com/questions/67760424/…>
    • 哦,是的,我今天看到了,但有点晚了,因为我的一些预期解决方案已经发布,所以我只是投票并坐下来。顺便说一句,这是一个非常好的问题。
    【解决方案3】:

    也许这就是你要找的东西:

    1. 使用例如重整为长格式tidy::pivot_longer
    2. Name分组并利用dplyr::lag添加一个人是否是新人的指示符
    3. 按年总结
    d <- data.frame(
      Name = c("Terry", "Sam", "Nic", "Sarah"), 
      x2017 = c(1, 0, 0, 0), 
      x2018 = c(1, 0, 1, 1), 
      x2019 = c(0, 1, 1, 1)
    ) 
    
    library(dplyr)
    library(tidyr)
    
    d %>% 
      tidyr::pivot_longer(-Name, names_to = "year") %>% 
      mutate(year = gsub("^x", "", year)) %>%
      group_by(Name) %>% 
      mutate(new = as.numeric(value == 1 & lag(value) == 0),
             new = ifelse(is.na(new), value, new)) %>% 
      ungroup() %>% 
      group_by(year) %>% 
      summarise(total = sum(value), new = sum(new))
    #> # A tibble: 3 x 3
    #>   year  total   new
    #>   <chr> <dbl> <dbl>
    #> 1 2017      1     1
    #> 2 2018      3     2
    #> 3 2019      3     1
    

    【讨论】:

    • 这在很多情况下不会给你正确的结果。它检查记录是否在前一年可用,而不是在any of the previous years 上试试这个structure(list(Name = c("Terry", "Sam", "Nic", "Sarah"), x2017 = c(1, 0, 0, 1), x2018 = c(1, 0, 1, 0), x2019 = c(0, 1, 1, 1)), row.names = c(NA, -4L), class = "data.frame")
    • @AnilGoyal。 OP 将 new 定义为 “如果有人在 2017 年和 2019 年出现但不是在 2018 年出现,则应将其归类为 2019 年的新人。”。这就是为什么我只检查了前一年。
    • 好的。感谢您的澄清,我会尽快修改我的答案。 :)
    【解决方案4】:

    case-I 只需要检查前一行的记录。

    df %>%
      pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
      group_by(Year) %>%
      summarise(total = sum(value),
                new = list(Name[value == 1]), .groups = 'drop') %>%
      mutate(new = map2_int(new, lag(new), ~ sum(!(.x %in% .y))))
    
    # A tibble: 3 x 3
      Year  total   new
      <chr> <dbl> <int>
    1 2017      1     1
    2 2018      3     2
    3 2019      3     1
    

    必须查看所有先前行的记录时的情况 II。同时使用map_*accumulate。采用的策略-

    • pivot_longer 首先。使用 names_prefix 参数直接从这里删除 x
    • group_by 一年后
    • 计算n() 的总值和list 中那一年的名称
    • 使用map2_int 改变new,第一个参数仅作为该列表,第二个参数作为accumulated 和lagged 列表。
    • map2_int 因此计算该行中TRUE 的总数。
    
    library(tidyverse)
    df %>%
      pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
      group_by(Year) %>%
      summarise(total = sum(value),
             new = list(Name[value == 1]), .groups = 'drop') %>%
      mutate(new = map2_int(new, lag(accumulate(new, union, .init = first(new))[-1]), ~ sum(!(.x %in% .y))))
    
    #> # A tibble: 3 x 3
    #>   Year  total   new
    #>   <chr> <int> <int>
    #> 1 2017      1     1
    #> 2 2018      3     2
    #> 3 2019      3     1
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-23
      • 2021-08-24
      • 2016-02-12
      • 1970-01-01
      • 2021-08-02
      • 2019-04-17
      • 2021-10-03
      • 2021-11-04
      相关资源
      最近更新 更多