【问题标题】:Expand ranges defined by "from" and "to" columns扩展由 \"from\" 和 \"to\" 列定义的范围
【发布时间】:2022-11-23 02:18:48
【问题描述】:

我有一个数据框,其中包含美国总统的"name",以及他们上任和离任的年份("from""to" 列)。这是一个示例:

name           from  to
Bill Clinton   1993 2001
George W. Bush 2001 2009
Barack Obama   2009 2012

...以及dput的输出:

dput(tail(presidents, 3))
structure(list(name = c("Bill Clinton", "George W. Bush", "Barack Obama"
), from = c(1993, 2001, 2009), to = c(2001, 2009, 2012)), .Names = c("name", 
"from", "to"), row.names = 42:44, class = "data.frame")

我想创建包含两列("name""year")的数据框,总统在任的每一年都有一行。因此,我需要创建一个每年从“from”到"to" 的常规序列。这是我的预期:

name           year
Bill Clinton   1993
Bill Clinton   1994
...
Bill Clinton   2000
Bill Clinton   2001
George W. Bush 2001
George W. Bush 2002
... 
George W. Bush 2008
George W. Bush 2009
Barack Obama   2009
Barack Obama   2010
Barack Obama   2011
Barack Obama   2012

我知道我可以使用 data.frame(name = "Bill Clinton", year = seq(1993, 2001)) 为单个总统扩展内容,但我不知道如何为每个总统进行迭代。

我该怎么做呢?我觉得我应该知道这一点,但我一片空白。

更新 1

好的,我已经尝试了两种解决方案,但出现错误:

foo<-structure(list(name = c("Grover Cleveland", "Benjamin Harrison", "Grover Cleveland"), from = c(1885, 1889, 1893), to = c(1889, 1893, 1897)), .Names = c("name", "from", "to"), row.names = 22:24, class = "data.frame")
ddply(foo, "name", summarise, year = seq(from, to))
Error in seq.default(from, to) : 'from' must be of length 1

【问题讨论】:

    标签: r dataframe sequence


    【解决方案1】:

    这是一个data.table 解决方案。它有一个很好的(如果是次要的)功能,可以让总统按他们提供的顺序排列:

    library(data.table)
    dt <- data.table(presidents)
    dt[, list(year = seq(from, to)), by = name]
    #               name year
    #  1:   Bill Clinton 1993
    #  2:   Bill Clinton 1994
    #  ...
    #  ...
    # 21:   Barack Obama 2011
    # 22:   Barack Obama 2012
    

    编辑:要处理任期不连续的总统,请改用:

    dt[, list(year = seq(from, to)), by = c("name", "from")]
    

    【讨论】:

      【解决方案2】:

      您可以使用 plyr 包:

      library(plyr)
      ddply(presidents, "name", summarise, year = seq(from, to))
      #              name year
      # 1    Barack Obama 2009
      # 2    Barack Obama 2010
      # 3    Barack Obama 2011
      # 4    Barack Obama 2012
      # 5    Bill Clinton 1993
      # 6    Bill Clinton 1994
      # [...]
      

      如果数据按年份排序很重要,您可以使用 arrange 函数:

      df <- ddply(presidents, "name", summarise, year = seq(from, to))
      arrange(df, df$year)
      #              name year
      # 1    Bill Clinton 1993
      # 2    Bill Clinton 1994
      # 3    Bill Clinton 1995
      # [...]
      # 21   Barack Obama 2011
      # 22   Barack Obama 2012
      

      编辑 1:继@edgester 的“更新 1”之后,更合适的方法是使用 adply 来计算任期不连续的总统:

      adply(foo, 1, summarise, year = seq(from, to))[c("name", "year")]
      

      【讨论】:

      • 您的解决方案适用于大多数数据。请看我的更新。
      • adply 解决方案是唯一没有出现错误“seq.default(from, to) 中的错误:‘from’的长度必须为 1”的解决方案。感谢您提供可行的解决方案。您能解释一下为什么其他解决方案出现“长度必须为 1”的错误吗?
      • @JoshOBrien 和我的都在处理您的示例数据,因此如果不查看您的完整数据就很难说。也许您可以将数据缩减为一个子集,以重现您看到的错误?那么我们或许可以提供帮助。
      【解决方案3】:

      使用unnestmap2的备用tidyverse方法。

      library(tidyverse)
      
      presidents %>%
        unnest(year = map2(from, to, seq)) %>%
        select(-from, -to)
      
      #              name  year
      # 1    Bill Clinton  1993
      # 2    Bill Clinton  1994
      ...
      # 21   Barack Obama  2011
      # 22   Barack Obama  2012
      

      编辑:来自 tidyr v1.0.0 的新变量不能再作为 unnest() 的一部分创建。

      presidents %>%
        mutate(year = map2(from, to, seq)) %>%
        unnest(year) %>%
        select(-from, -to)
      

      【讨论】:

      • 很棒的答案。如此简单,却如此有效。
      • 为了避免select,可以使用.keep = "unused"presidents %&gt;% mutate(year = map2(from, to, seq), .keep = "unused") %&gt;% unnest(year)
      【解决方案4】:

      这是一个 dplyr 解决方案:

      library(dplyr)
      
      # the data
      presidents <- 
      structure(list(name = c("Bill Clinton", "George W. Bush", "Barack Obama"
      ), from = c(1993, 2001, 2009), to = c(2001, 2009, 2012)), .Names = c("name", 
      "from", "to"), row.names = 42:44, class = "data.frame")
      
      # the expansion of the table
      presidents %>%
          rowwise() %>%
          do(data.frame(name = .$name, year = seq(.$from, .$to, by = 1)))
      
      # the output
      Source: local data frame [22 x 2]
      Groups: <by row>
      
                   name  year
                  (chr) (dbl)
      1    Bill Clinton  1993
      2    Bill Clinton  1994
      3    Bill Clinton  1995
      4    Bill Clinton  1996
      5    Bill Clinton  1997
      6    Bill Clinton  1998
      7    Bill Clinton  1999
      8    Bill Clinton  2000
      9    Bill Clinton  2001
      10 George W. Bush  2001
      ..            ...   ...
      

      电话:https://stackoverflow.com/a/24804470/1036500

      【讨论】:

        【解决方案5】:

        两个base解决方案。

        使用sequence

        len = d$to - d$from + 1
        data.frame(name = d$name[rep(1:nrow(d), len)], year = sequence(len, d$from)).
        

        使用mapply

        l <- mapply(`:`, d$from, d$to) 
        data.frame(name = d$name[rep(1:nrow(d), lengths(l))], year = unlist(l))
        
        #              name year
        # 1    Bill Clinton 1993
        # 2    Bill Clinton 1994
        # ...snip
        # 8    Bill Clinton 2000
        # 9    Bill Clinton 2001
        # 10 George W. Bush 2001
        # 11 George W. Bush 2002
        # ...snip
        # 17 George W. Bush 2008
        # 18 George W. Bush 2009
        # 19   Barack Obama 2009
        # 20   Barack Obama 2010
        # 21   Barack Obama 2011
        # 22   Barack Obama 2012
        

        【讨论】:

          【解决方案6】:

          这是一个快速的 base-R 解决方案,其中 Df 是您的 data.frame

          do.call(rbind, apply(Df, 1, function(x) {
            data.frame(name=x[1], year=seq(x[2], x[3]))}))
          

          它给出了一些关于行名的警告,但似乎返回了正确的data.frame

          【讨论】:

          • +1——非常好,虽然我希望它没有抛出那些警告并产生具有如此难看的行名称的结果。
          • @JoshO'Brien,实际上我不介意行名——它为数据增加了一个层次:我们可以快速识别,比方说,比尔克林顿是美国第 42 任总统。这在plyrdata.table 解决方案中都丢失了。
          【解决方案7】:

          使用tidyverse 的另一个选项可能是将gather 数据转换为长格式,group_by name 并在fromto 日期之间创建一个序列。

          library(tidyverse)
          
          presidents %>%
            gather(key, date, -name) %>%
            group_by(name) %>%
            complete(date = seq(date[1], date[2]))%>%
            select(-key) 
          
          # A tibble: 22 x 2
          # Groups:   name [3]
          #   name          date
          #   <chr>        <dbl>
          # 1 Barack Obama  2009
          # 2 Barack Obama  2010
          # 3 Barack Obama  2011
          # 4 Barack Obama  2012
          # 5 Bill Clinton  1993
          # 6 Bill Clinton  1994
          # 7 Bill Clinton  1995
          # 8 Bill Clinton  1996
          # 9 Bill Clinton  1997
          #10 Bill Clinton  1998
          # … with 12 more rows
          

          【讨论】:

            【解决方案8】:

            使用by创建一个bydata.frames列表L,每个总统一个data.frame,然后rbind他们在一起。没有使用包。

            L <- by(presidents, presidents$name, with, data.frame(name, year = from:to))
            do.call("rbind", setNames(L, NULL))
            

            如果您不介意行名,那么最后一行可以简化为:

            do.call("rbind", L)
            

            【讨论】:

              【解决方案9】:

              另一个使用dplyrtidyr的解决方案:

              library(magrittr) # for pipes
              df <- data.frame(tata = c('toto1', 'toto2'), from = c(2000, 2004), to = c(2001, 2009))
              
              #    tata from   to
              # 1 toto1 2000 2001
              # 2 toto2 2004 2009
              
              df %>% 
                dplyr::as.tbl() %>%
                dplyr::rowwise() %>%
                dplyr::mutate(combined = list(seq(from, to))) %>%
                dplyr::select(-from, -to) %>%
                tidyr::unnest(combined)
              
              #   tata  combined
              #   <fct>    <int>
              # 1 toto1     2000
              # 2 toto1     2001
              # 3 toto2     2004
              # 4 toto2     2005
              # 5 toto2     2006
              # 6 toto2     2007
              # 7 toto2     2008
              # 8 toto2     2009
              

              【讨论】:

                【解决方案10】:

                tidyverse 解决方案的补充可以是:

                df %>%
                 uncount(to - from + 1) %>%
                 group_by(name) %>%
                 transmute(year = seq(first(from), first(to)))
                
                   name            year
                   <chr>          <dbl>
                 1 Bill Clinton    1993
                 2 Bill Clinton    1994
                 3 Bill Clinton    1995
                 4 Bill Clinton    1996
                 5 Bill Clinton    1997
                 6 Bill Clinton    1998
                 7 Bill Clinton    1999
                 8 Bill Clinton    2000
                 9 Bill Clinton    2001
                10 George W. Bush  2001
                

                【讨论】:

                  猜你喜欢
                  • 2012-07-14
                  • 2015-05-22
                  • 1970-01-01
                  • 2022-01-23
                  • 2016-07-07
                  • 2018-01-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多