gpt4 book ai didi

用字符后缀而不是数字后缀将宽改成长

转载 作者:行者123 更新时间:2023-12-03 20:23:39 25 4
gpt4 key购买 nike

灵感来自 a comment来自@gsk3 关于整形数据的问题,我开始对整形数据进行一些实验,其中变量名称具有字符后缀而不是数字后缀。

例如,我将加载 dadmomw来自 UCLA ATS Stata learning webpages 之一的数据集(参见网页上的“示例 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 为长格式时,我遇到了一个问题。这是我为 reshape 数据所做的工作。
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.namesc("inc", "name")不能解决问题。
reshape希望以相当标准的方式命名列似乎非常挑剔。例如,如果我首先重命名列,我可以正确(并且轻松地) 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

我的问题是:
  • 为什么 R 在我提供的示例中交换列?
  • 我可以用基数 R reshape 得到这个结果吗?在 reshape 之前不更改变量名?
  • 是否可以考虑其他方法来代替 reshape ?
  • 最佳答案

    尽管这个问题是专门针对基础 R 的,但了解其他有助于实现相同类型结果的方法很有用。
    reshape 的一种替代方法或 merged.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

    另一种选择是使用 melt从“data.table”,像这样:
    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

    观察:
  • 基础 R 的 reshape将无法处理 reshape “mydf2”。
  • “dplyr” + “tidyr” 方法会破坏生成的“varB”、“varC”和“varD”中的结果,因为值将被强制转换为字符。
  • 正如基准所显示的,reshape给出合理的表现。

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

    关于用字符后缀而不是数字后缀将宽改成长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10468969/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com