【问题标题】:Expand ranges defined by "from" and "to" columns扩展由“from”和“to”列定义的范围
【发布时间】:2012-07-14 17:20:14
【问题描述】:

我有一个数据框,其中包含美国总统的"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


    【解决方案1】:

    您可以使用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' must be of length 1”的解决方案。感谢您提供有效的解决方案。您能解释一下为什么我在其他解决方案中收到“长度必须为 1”的错误吗?
    • @JoshOBrien 和我的都在处理您的示例数据,因此如果不查看您的完整数据就很难说。也许您可以将数据缩减为重现您看到的错误的子集?那么我们也许可以提供帮助。
    【解决方案2】:

    这是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")]
    

    【讨论】:

      【解决方案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)
      

      【讨论】:

      • 很棒的答案。如此简单,却又如此有效。
      【解决方案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
      ..            ...   ...
      

      h/t:https://stackoverflow.com/a/24804470/1036500

      【讨论】:

        【解决方案5】:

        另一个base解决方案:

        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】:

          这是一个快速的基础-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 创建一个by 列表L 的data.frames,每个总统一个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
              

              【讨论】:

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