【问题标题】:reshape wide to long with character suffixes instead of numeric suffixes用字符后缀而不是数字后缀将宽改成长
【发布时间】:2016-07-08 01:53:15
【问题描述】:

受@gsk3 的a comment 的关于重塑数据的问题的启发,我开始对变量名称具有字符后缀而不是数字后缀的数据进行重塑。

例如,我将从UCLA ATS Stata learning webpages 之一加载dadmomw 数据集(请参阅网页上的“示例4”)。

数据集如下所示:

library(foreign)
dadmom <- read.dta("https://stats.idre.ucla.edu/stat/stata/modules/dadmomw.dat")
dadmom
#   famid named  incd namem  incm
# 1     1  Bill 30000  Bess 15000
# 2     2   Art 22000   Amy 18000
# 3     3  Paul 25000   Pat 50000

当试图从这种宽格式重塑为长格式时,我遇到了一个问题。以下是我为重塑数据所做的工作。

reshape(dadmom, direction="long", idvar=1, varying=2:5, 
        sep="", v.names=c("name", "inc"), timevar="dadmom",
        times=c("d", "m"))
#     famid dadmom  name  inc
# 1.d     1      d 30000 Bill
# 2.d     2      d 22000  Art
# 3.d     3      d 25000 Paul
# 1.m     1      m 15000 Bess
# 2.m     2      m 18000  Amy
# 3.m     3      m 50000  Pat

注意“name”和“inc”的交换列名;将v.names 更改为c("inc", "name") 并不能解决问题。

reshape 似乎对希望以相当标准的方式命名列非常挑剔。例如,如果我首先重命名列,我可以正确(并且轻松地)重塑数据:

dadmom2 <- dadmom # Just so we can continue experimenting with the original data
# Change the names of the last four variables to include a "."
names(dadmom2)[2:5] <- gsub("(d$|m$)", "\\.\\1", names(dadmom2)[2:5])
reshape(dadmom2, direction="long", idvar=1, varying=2:5, 
        timevar="dadmom")
#     famid dadmom name   inc
# 1.d     1      d Bill 30000
# 2.d     2      d  Art 22000
# 3.d     3      d Paul 25000
# 1.m     1      m Bess 15000
# 2.m     2      m  Amy 18000
# 3.m     3      m  Pat 50000

我的问题是:

  1. 为什么 R 会交换我提供的示例中的列?
  2. 我能否在不更改变量名的情况下使用基数 R reshape 获得此结果?
  3. 是否可以考虑使用其他方法来代替reshape

【问题讨论】:

  • Stata 标签的唯一明显原因是示例数据集采用专有的 Stata 格式。这似乎是偶然的,所以我删除了它。如果我错过了重要的内容,请随时重新介绍它。

标签: r reshape


【解决方案1】:

这有效(指定以改变哪些列与谁一起使用):

reshape(dadmom, direction="long",  varying=list(c(2, 4), c(3, 5)), 
        sep="", v.names=c("name", "inc"), timevar="dadmom",
        times=c("d", "m"))

所以你实际上已经在这里嵌套了重复的措施;妈妈和爸爸的名字和公司。因为你有不止一个系列的重复测量,你必须提供一个 list 来改变,告诉 reshape 哪个组堆叠在另一个组上。

所以解决这个问题的两种方法是像我一样提供一个列表,或者像你一样按照 R 野兽喜欢它们的方式重命名列。

请参阅我最近在reshape 上的博客了解更多信息(特别是第二个链接涉及此内容):

reshape (part I)

reshape (part II)

【讨论】:

  • @DWin,为了清晰起见,我认为帮助页面也可以重写。只有在阅读了 Tyler 的解决方案并重新访问该页面后,我才发现这行 这通常是变量名称向量的列表,但它可以是名称矩阵或单个名称向量。 这是我在使用时经常打开帮助页面的功能之一。
  • @mrdwab 即使你指出了重塑帮助页面的解释,我的小面条仍然很难理解。这是一个如此强大的功能,很难描述它所做的一切。我试图用一种非常容易理解的语言来捕捉它是如何工作的。我希望它可以帮助其他人。
  • @TylerRinker,您可能对我发布的其他一些方法感兴趣,作为该答案的替代方法。当然,复选标记仍然是你的,因为它完全解决了我的问题,但我认为现在新工具出现了需要更新。
  • 随意移动它以指导未来的搜索者。 +1 更新工作。
【解决方案2】:

我的“splitstackshape”中的merged.stack 使用sep = "var.stubs" 构造来处理这个问题:

library(splitstackshape)
merged.stack(dadmom, var.stubs = c("inc", "name"), sep = "var.stubs")
#    famid .time_1   inc name
# 1:     1       d 30000 Bill
# 2:     1       m 15000 Bess
# 3:     2       d 22000  Art
# 4:     2       m 18000  Amy
# 5:     3       d 25000 Paul
# 6:     3       m 50000  Pat

请注意,由于在堆叠的变量中没有真正的分隔符,我们可以从名称中去掉var.stubs 来创建“时间”变量。使用sep = "var.stubs" 相当于使用sep = "inc|name"

之所以有效,是因为“.time_1”是通过从列名中删除“var.stubs”后删除剩余的内容来创建的。

【讨论】:

    【解决方案3】:

    虽然这个问题是专门针对基础 R 的,但了解其他有助于实现相同类型结果的方法很有用。

    reshapemerged.stack 的一种替代方法是使用“dplyr”和“tidry”的组合,如下所示:

    dadmom %>%
      gather(variable, value, -famid) %>%               ## Make the entire dataset long
      separate(variable, into = c("var", "time"),       ## Split "variable" column into two...
               sep = "(?<=name|inc)", perl = TRUE) %>%  ## ... using regex to split the values
      spread(var, value, convert = TRUE)                ## Make result wide, converting type
    #   famid time   inc name
    # 1     1    d 30000 Bill
    # 2     1    m 15000 Bess
    # 3     2    d 22000  Art
    # 4     2    m 18000  Amy
    # 5     3    d 25000 Paul
    # 6     3    m 50000  Pat
    

    另一种选择是使用“data.table”中的melt,如下所示:

    library(data.table)
    melt(as.data.table(dadmom),             ## melt here requres a data.table 
         measure = patterns("name", "inc"), ## identify columns by patterns
         value.name = c("name", "inc"))[    ## specify the resulting variable names
           ## melt creates a numeric "variable" value. Replace with factored labels
           , variable := factor(variable, labels = c("d", "m"))][]
    #    famid variable name   inc
    # 1:     1        d Bill 30000
    # 2:     2        d  Art 22000
    # 3:     3        d Paul 25000
    # 4:     1        m Bess 15000
    # 5:     2        m  Amy 18000
    # 6:     3        m  Pat 50000
    

    这些方法与merged.stack 相比如何?

    • 这两个包都得到了更好的支持。他们比我更广泛地更新和测试他们的代码。
    • melt 非常快。
    • Hadleyverse 方法实际上更慢(在我的许多测试中,甚至比基本 R 的reshape 更慢)可能是因为必须使数据变长,然后变宽,然后执行类型转换。不过,一些用户喜欢它的循序渐进的方法。
    • Hadleyverse 方法可能会产生一些意想不到的后果,因为需要在使数据变宽之前使数据变长。这会强制将所有度量列强制转换为相同类型(通常是“字符”),如果它们的类型不同的话。
    • 两者都没有merged.stack 的便利。只需查看获取结果所需的代码;-)

    不过,merged.stack 可能会受益于简化的更新,类似于this function

    ReshapeLong_ <- function(indt, stubs, sep = NULL) {
      if (!is.data.table(indt)) indt <- as.data.table(indt)
      mv <- lapply(stubs, function(y) grep(sprintf("^%s", y), names(indt)))
      levs <- unique(gsub(paste(stubs, collapse="|"), "", names(indt)[unlist(mv)]))
      if (!is.null(sep)) levs <- gsub(sprintf("^%s", sep), "", levs, fixed = TRUE)
      melt(indt, measure = mv, value.name = stubs)[
        , variable := factor(variable, labels = levs)][]
    }
    

    然后可以用作:

    ReshapeLong_(dadmom, stubs = c("name", "inc"))
    

    这些方法与基本 R 的 reshape 相比如何?

    • 主要区别在于reshape 无法处理不平衡的面板数据集。例如,在下面的测试中,请参阅“mydf2”而不是“mydf”。

    测试用例

    这是一些示例数据。 “mydf”是平衡的。 “mydf2”不平衡。

    set.seed(1)
    x <- 10000
    mydf <- mydf2 <- data.frame(
      id_1 = 1:x, id_2 = c("A", "B"), varAa = sample(letters, x, TRUE), 
      varAb = sample(letters, x, TRUE), varAc = sample(letters, x, TRUE),
      varBa = sample(10, x, TRUE), varBb = sample(10, x, TRUE), 
      varBc = sample(10, x, TRUE), varCa = rnorm(x), varCb = rnorm(x), 
      varCc = rnorm(x), varDa = rnorm(x), varDb = rnorm(x), varDc = rnorm(x))
    
    mydf2 <- mydf2[-c(9, 14)] ## Make data unbalanced
    

    这里有一些功能需要测试:

    f1 <- function(mydf) {
      mydf %>%
        gather(variable, value, starts_with("var")) %>%
        separate(variable, into = c("var", "time"),
                 sep = "(?<=varA|varB|varC|varD)", perl = TRUE) %>%
        spread(var, value, convert = TRUE) 
    }
    
    f2 <- function(mydf) {
      melt(as.data.table(mydf),
           measure = patterns(paste0("var", c("A", "B", "C", "D"))),
           value.name = paste0("var", c("A", "B", "C", "D")))[
             , variable := factor(variable, labels = c("a", "b", "c"))][]
    }
    
    f3 <- function(mydf) {
      merged.stack(mydf, var.stubs = paste0("var", c("A", "B", "C", "D")), sep = "var.stubs")
    }
    
    ## Won't run with "mydf2". Should run with "mydf"
    f4 <- function(mydf) {
      reshape(mydf, direction = "long", 
              varying = lapply(c("varA", "varB", "varC", "varD"), 
                               function(x) grep(x, names(mydf))), 
              sep = "", v.names = paste0("var", c("A", "B", "C", "D")), 
              timevar="time", times = c("a", "b", "c"))
    }
    

    测试性能:

    library(microbenchmark)
    microbenchmark(f1(mydf), f2(mydf), f3(mydf), f4(mydf))
    # Unit: milliseconds
    #      expr        min         lq       mean     median         uq       max neval
    #  f1(mydf) 463.006547 492.073086 528.533319 514.189548 538.910756 867.93356   100
    #  f2(mydf)   3.737321   4.108376   6.674066   4.332391   4.761681  47.71142   100
    #  f3(mydf)  60.211254  64.766770  86.812077  87.040087  92.841747 262.89409   100
    #  f4(mydf)  40.596455  43.753431  61.006337  48.963145  69.983623 230.48449   100
    

    观察:

    • Base R 的 reshape 将无法处理重塑“mydf2”。
    • “dplyr”+“tidyr”方法会破坏生成的“varB”、“varC”和“varD”中的结果,因为值将被强制转换为字符。
    • 正如基准测试所示,reshape 提供了合理的性能。

    注意:由于发布我上一个答案之间的时间差异和方法的差异,我想我会分享这个作为一个新的答案。

    【讨论】:

    • 很好,很详细。您不能将新的melt 功能合并到splitstackshape 中,以获得它提供的语法糖吗?或者你已经有了?我记得前段时间讨论过这个问题。
    • @Arun,在我的待办事项清单上。见this gist
    • 太棒了!期待。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 2011-04-03
    相关资源
    最近更新 更多