Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R 使用字符后缀而不是数字后缀将宽改为长_R_Reshape - Fatal编程技术网

R 使用字符后缀而不是数字后缀将宽改为长

R 使用字符后缀而不是数字后缀将宽改为长,r,reshape,R,Reshape,受@gsk3关于重塑数据的问题的启发,我开始做一些重塑数据的实验,其中变量名有字符后缀而不是数字后缀 例如,我将从其中一个加载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

受@gsk3关于重塑数据的问题的启发,我开始做一些重塑数据的实验,其中变量名有字符后缀而不是数字后缀

例如,我将从其中一个加载
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
注意“name”和“inc”的交换列名;将
v.names
更改为
c(“inc”,“name”)
并不能解决问题

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

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
dadmom2这起作用(用于指定随谁而变化的列):

实际上,这里有嵌套的重复度量;妈妈和爸爸的名字和公司。由于您有多个重复测量序列,因此您必须提供一个
列表
,以告知
重塑
哪一组堆叠在另一组上

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

有关这方面的更多信息,请参见我最近在base
Reforme
上的博客(特别是第二个链接):


合并。我的“splitstackshape”中的堆栈通过使用
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”后去掉剩下的内容而创建的。

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

重塑
合并。堆栈
的一种替代方法是使用“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
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方法实际上比较慢(在我的许多测试中,甚至比base R的
    restrape
    还要慢),可能是因为必须先使数据长,然后宽,然后执行类型转换。然而,一些用户喜欢它的循序渐进的方法
  • Hadleyverse方法可能会产生一些意想不到的后果,因为需要在广泛使用数据之前很久就进行数据处理。这将强制所有度量值列强制为相同的类型(通常为“字符”),前提是它们的开始类型不同
  • 两者都没有
    merged.stack的便利性。只需查看获得结果所需的代码;-)
    
merged.stack
,但是,可能会从简化的更新中受益,这是一种类似于


这些方法与基本R的重塑相比如何

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

测试用例 下面是一些示例数据。“多年筹资框架”是平衡的。“多年筹资框架2”不平衡

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
意见:

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

注意:由于发布我的最后一个答案和发布方法的时间不同,我想我会将此作为一个新的答案来分享。

@DWin,为了清晰起见,我认为帮助页面也可以重新编写。只有在阅读了Tyler的解决方案并重新浏览页面后,我才明白这是一个规范的变量名向量列表,但它可以是一个名称矩阵,也可以是一个名称向量。这是我使用帮助页面时经常打开的功能之一。@mrdwab即使在你指出重塑帮助页面的解释之后,我的小面条仍然很难理解它。这是一个非常强大的函数,很难描述它的所有功能。我试图用一种非常容易理解的语言来描述它是如何工作的。我希望它能帮助其他人。@Tylerinker,你可能会对我发布的其他一些方法感兴趣,作为这个答案的替代方法。当然,复选标记仍然是你的,因为它完全解决了我的问题,但我认为现在新工具已经出现,需要更新。请随意移动它来指导未来的搜索者+1用于更新工作。使用Stata标记的唯一明显原因是示例数据集采用专有Stata格式。这似乎是偶然的,所以我把它去掉了。如果我错过了一些重要的东西,请随时重新介绍。很好,很详细。你不能将新的
melt
功能合并到
splitstackshape
中,以获得它提供的语法优势吗?或者你已经有了?“我记得很久以前讨论过这个问题。”阿伦,在我的待办事项清单上。看,太棒了!期待。
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
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"))
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