【问题标题】:How to control new variables' names after tidyr's spread?tidyr 传播后如何控制新变量的名称?
【发布时间】:2015-10-25 14:43:22
【问题描述】:

我有一个面板结构的数据框:两年内每个单元的 2 次观察:

library(tidyr)
mydf <- data.frame(
    id = rep(1:3, rep(2,3)), 
    year = rep(c(2012, 2013), 3), 
    value = runif(6)
)
mydf
#  id year      value
#1  1 2012 0.09668064
#2  1 2013 0.62739399
#3  2 2012 0.45618433
#4  2 2013 0.60347152
#5  3 2012 0.84537624
#6  3 2013 0.33466030

我想将这些数据重塑为宽格式,这可以通过tidyr::spread 轻松完成。但是,由于year 变量的值是数字,我的新变量的名称也变成了数字,这使得它的进一步使用变得更加困难。

spread(mydf, year, value)
#  id       2012      2013
#1  1 0.09668064 0.6273940
#2  2 0.45618433 0.6034715
#3  3 0.84537624 0.3346603

我知道我可以轻松地重命名这些列。但是,如果我想用其他操作在链中重塑,那就不方便了。例如。下面这行显然没有意义。

library(dplyr)
mydf %>% spread(year, value) %>% filter(2012 > 0.5)

以下有效,但不是那么简洁:

tmp <- spread(mydf, year, value)
names(tmp) <- c("id", "y2012", "y2013")
filter(tmp, y2012 > 0.5)

知道如何更改spread 中的新变量名称吗?

【问题讨论】:

    标签: r dplyr tidyr


    【解决方案1】:

    我知道自从最初提出这个问题以来已经过去了几年,但是为了后代,我还想强调 spreadsep 参数。如果不是NULL,它将用作键名和值之间的分隔符:

    mydf %>% 
     spread(key = year, value = value, sep = "")
    #  id   year2012  year2013
    #1  1 0.15608322 0.6886531
    #2  2 0.04598124 0.0792947
    #3  3 0.16835445 0.1744542
    

    这并不完全符合问题的要求,但足以满足我的目的。见?spread

    使用 tidyr 1.0.0 更新:tidyr 1.0.0 现在引入了pivot_wider(和pivot_longer),它允许在这方面通过参数names_sep 和@987654329 进行更多控制@。所以现在的电话是:

    mydf %>% 
      pivot_wider(names_from = year, values_from = value,
                  names_prefix = "year")
    # # A tibble: 3 x 3
    #        id year2012 year2013
    #     <int>    <dbl>    <dbl>
    #   1     1    0.347    0.388
    #   2     2    0.565    0.924
    #   3     3    0.406    0.296
    

    要准确获得最初想要的内容(仅前缀“y”),您现在当然可以直接通过 names_prefix = "y" 直接获得。

    names_sep 用于您收集多个列的情况,如下所示,我在数据中添加了季度:

    # Add quarters to data
    mydf2 <- data.frame(
      id = rep(1:3, each = 8), 
      year = rep(rep(c(2012, 2013), each = 4), 3), 
      quarter  = rep(c("Q1","Q2","Q3","Q4"), 3),
      value = runif(24)
    )
    head(mydf2)
    # id year quarter     value
    # 1  1 2012      Q1 0.8651470
    # 2  1 2012      Q2 0.3944423
    # 3  1 2012      Q3 0.4580580
    # 4  1 2012      Q4 0.2902604
    # 5  1 2013      Q1 0.4751588
    # 6  1 2013      Q2 0.6851755
    
    mydf2 %>% 
      pivot_wider(names_from = c(year, quarter), values_from = value,
                  names_sep = "_", names_prefix = "y")
    # # A tibble: 3 x 9
    #      id  y2012_Q1  y2012_Q2  y2012_Q3  y2012_Q4  y2013_Q1  y2013_Q2  y2013_Q3  y2013_Q4 
    #   <int>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
    # 1     1     0.865     0.394     0.458    0.290      0.475     0.685     0.213     0.920
    # 2     2     0.566     0.614     0.509    0.0515     0.974     0.916     0.681     0.509
    # 3     3     0.968     0.615     0.670    0.748      0.723     0.996     0.247     0.449
    

    【讨论】:

    • 这正是我们想要的 :) "更改 spread 中的新变量名"
    【解决方案2】:

    您可以将backticks 用于以数字开头的列名,filter 应该可以正常工作

      mydf %>%
          spread(year, value) %>%
          filter(`2012` > 0.5)
      #  id      2012      2013
      #1  3 0.8453762 0.3346603
    

    或者另一种选择是在使用字符串“y”创建第二列“year1”后,使用unite 将两列连接到一个列。

      mydf %>%
         mutate(year1='y') %>%
         unite(yearN, year1, year) %>%
         spread(yearN, value) %>%
         filter(y_2012 > 0.5)
     #   id    y_2012    y_2013
     #1  3 0.8453762 0.3346603
    

    我们甚至可以使用paste 更改mutate 中的“年份”列

     mydf %>%
         mutate(year=paste('y', year, sep="_")) %>%
         spread(year, value) %>%
         filter(y_2012 > 0.5)
    

    【讨论】:

      【解决方案3】:

      另一种选择是使用setNames() 函数作为管道中的下一个功能:

      mydf %>%
          spread(mydf, year, value) %>%
          setNames( c("id", "y2012", "y2013") ) %>%
          filter(y2012 > 0.5)
      

      使用 setNames 的唯一问题是,当您 spread() 时,您必须确切地知道您的列将是什么。大多数时候,这不是问题,尤其是在您以半交互方式工作时。

      但是,如果您在原始数据中缺少键/值对,则它可能不会显示为列,并且您最终可能会在不知情的情况下错误地命名列。当然,如果名称的数量与列的数量不匹配,setNames() 将引发错误,因此您内置了一些错误检查。

      不过,对于我来说,使用 setNames() 的便利性往往大于风险。

      【讨论】:

        【解决方案4】:

        使用spread() 的继任者pivot_wider(),我们可以为创建的列添加前缀:

        library(tidyr)
        set.seed(1)
        mydf <- data.frame(
          id = rep(1:3, rep(2,3)), 
          year = rep(c(2012, 2013), 3), 
          value = runif(6)
        )
        
        pivot_wider(mydf, names_from = "year", values_from = "value", names_prefix = "y")
        #> # A tibble: 3 x 3
        #>      id y2012 y2013
        #>   <int> <dbl> <dbl>
        #> 1     1 0.266 0.372
        #> 2     2 0.573 0.908
        #> 3     3 0.202 0.898
        

        reprex package (v0.3.0) 于 2019-09-14 创建

        【讨论】:

          【解决方案5】:

          dplyr 中的 rename() 应该可以解决问题

          library(tidyr); library(dplyr)
          mydf %>%
            spread(year,value)%>%
            rename(y2012 = '2012',y2013 = '2013')%>%
            filter(y2012>0.5)
          

          【讨论】:

          • 从概念上讲,和@crazybilly 的回答是一样的
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-01-19
          • 1970-01-01
          • 2023-01-09
          • 1970-01-01
          • 2016-05-21
          • 2018-07-23
          • 1970-01-01
          相关资源
          最近更新 更多