【问题标题】:Reshaping wide to long with multiple row用多行从宽变长
【发布时间】:2019-02-21 13:18:22
【问题描述】:

这是我的数据框的一部分:

name      value_1  value_2  value_3
 AK          x       X
 AK          y       Y
 AK          m       M         B
 HU          z       Z
 HU          a       A
 HU          f       F         C
 KO          b       B
 KO          c       C
 KO          d       D         B

我需要:

num   AK    HU   KO
 1    x      z   b
 1    X      Z   B
 2    y      a   c
 2    Y      A   C
 3    m      f   d
 3    M      F   D
 3    B      C   B 

用文字;我对人 AK、HU 和 KO(以及许多其他人)有价值观。所有评级都相同 - 因此每个唯一 ID 的行数相同,值的数量也将相同。 在这部分数据框 1 和 2 有两个值,3 每个人有 3 个值。 它实际上是强制选择任务,因此 value_1:2 中的值实际上只是 1 和 0,但我已将它们替换为 a-Z 以显示排序。

我试过了:

library(reshape2)
long <- melt(df, id.vars = c("name"))

然而,这并不能解决问题,因为所有这些都被添加到彼此的下方,而我需要根据它们的 ID(名称)将它们重新整形为 long,但是不同的 name-ID 将需要放置在彼此相邻的单独列。

感谢您的帮助。

【问题讨论】:

    标签: r transpose


    【解决方案1】:

    使用下面注释中显示的输入,运行问题中显示的melt,然后将value_1、value_2 和value_3 转换为仅1、2 和3,并添加subseq 列以区分具有相同键的行。删除空的 value 行并使用 dcast 删除 subseq 列转换回宽格式:

    library(reshape2)
    
    long <- melt(DF, id.var = "name") # from question
    long2 <- transform(long, num = gsub("\\D", "", variable),
              subseq =  ave(1:nrow(m), name, variable, FUN = seq_along),
              variable = NULL)
    long3 <- subset(long2, value != "")
    wide <- dcast(subseq + num ~ name, data = long3, value.var = "value")[-1]
    

    给予:

    > wide
      num AK HU KO
    1   1  x  z  b
    2   2  X  Z  B
    3   1  y  a  c
    4   2  Y  A  C
    5   1  m  f  d
    6   2  M  F  D
    7   3  B  C  B
    

    对照预期检查:

    identical(wide, expected)
    ## [1] TRUE
    

    注意

    可重现形式的输入是:

    Lines <- "
    name      value_1  value_2  value_3
     AK          x       X
     AK          y       Y
     AK          m       M         B
     HU          z       Z
     HU          a       A
     HU          f       F         C
     KO          b       B
     KO          c       C
     KO          d       D         B"
    DF <- read.table(text = Lines, header = TRUE, as.is = TRUE, fill = TRUE, strip.white = TRUE)
    

    我们假设num 是一个因素,而其他数据是字符——我们无法判断其意图是什么,因为输入在问题中没有以可重现的形式给出。

    expected <- structure(list(num = structure(c(1L, 2L, 1L, 2L, 1L, 2L, 3L), .Label = 
    c("1", "2", "3"), class = "factor"), AK = c("x", 
    "X", "y", "Y", "m", "M", "B"), HU = c("z", "Z", "a", "A", "f", 
    "F", "C"), KO = c("b", "B", "c", "C", "d", "D", "B")), row.names = c(NA, 
    -7L), class = "data.frame")
    

    更新 2

    此变体使用 0/1 数据和整数 num:

    set.seed(123)
    # test data
    DF2 <- data.frame(name = DF$name, 
               value_1 = rbinom(9, 1, .5), 
               value_2 = rbinom(9, 1, .5),
               value_3 = ifelse(DF[, 4] == "", NA, rbinom(9, 1, .5)))
    
    long <- melt(DF2, id.var = "name")
    long2 <- subset(long, !is.na(value))
    long3 <- transform(long2, num = as.integer(gsub("\\D", "", variable)),
               subseq =  ave(1:nrow(long2), name, variable, FUN = seq_along),
               variable = NULL)
    wide <- dcast(subseq + num ~ name, data = long3, value.var = "value")[-1]
    

    给予:

    > wide
      num AK HU KO
    1   1  0  1  1
    2   2  0  1  1
    3   3  1  1  1
    4   1  1  1  1
    5   2  1  1  0
    6   1  0  0  1
    7   2  0  0  0
    

    【讨论】:

    • 谢谢,尽管您的输出与我上面所说的不同。我需要每个名称 ID 下的列按行列出,而不是按列列出。
    • 已修复并在末尾添加了一个检查,以确保它给出与预期问题相同的答案。
    • 我知道我的信息不够,所以我在上面编辑了它。实际上这些值只有 0 和 1,但是为了显示我需要的重新排序,我使用了字母。
    【解决方案2】:

    这将提供所需的输出:

     library(tidyverse)
    df0=df%>%mutate(Value12=map2(value_1,value_2,c))%>%
      mutate(Value=map2(Value12,value_3,c))%>%
      select(name,Value)%>%
      unnest()%>%
      drop_na()%>%
      group_by(name)%>%
       mutate(i = row_number())%>%
      spread(name,Value)%>%
      select(-i)
    
    
    df0$num=cumsum(c(toupper(df0$AK)==toupper(lag(df0$AK)),F)%>%na.omit())
    > df0
    # A tibble: 7 x 4
      AK    HU    KO      num
      <chr> <chr> <chr> <int>
    1 x     z     b         1
    2 X     Z     B         1
    3 y     a     c         2
    4 Y     A     C         2
    5 m     f     d         3
    6 M     F     D         3
    7 B     C     B         3
    

    但是您需要澄清填充列 num 的逻辑是什么。我不得不假设 AK 列的相同字母(不区分大小写)将具有相同的数字。

    【讨论】:

    • 澄清一下;每个拥有姓名 ID 的人都必须对相同数量的整体进行评分;所以在这里他们每个人都必须对 3 个组进行评分,并且每个组都有一定数量的元素(在示例中为 2 个或 3 个元素)。重塑它们将摆脱分组但保留顺序。这说明了什么?
    【解决方案3】:

    我必须包含一些额外的小步骤才能完成此操作,因为您希望按特定顺序输出,但应该这样做:

    long<-melt(df, id.vars=c("name"), value.var=c("value_1","value_2","value_3"))
    long$variable<-c("11","21","31","11","21","31","11","21","31",
                     "12","22","32","12","22","32","12","22","32",
                     "13","23","33","13","23","33","13","23","33")
    short<-dcast(long, variable ~ name)
    final<-short[short$AK!="",]
    final$variable<-round(as.numeric(final$variable)/10,0)
    colnames(final)[1]<-"num"
    

    希望这会有所帮助!

    【讨论】:

    • 非常感谢!不幸的是,我没有得到正确的行顺序。而不是彼此下方的第一行的值,加上第二行的值,我得到了一些不同的东西。你知道出了什么问题吗?
    • 好吧,我误读了你的第一个问题。但我编辑了我的答案,给出了你想要的输出。
    【解决方案4】:

    或者,这里有一个使用 包中的 melt() 和 dcast() 的解决方案。 rowid(name) 用于为每个name 单独创建行号。

    它再现了预期的结果,包括列num。

    library(data.table)
    long <- melt(setDT(df)[, num := rowid(name)], id.vars = c("num", "name"))[value != ""]
    dcast(long, num + rowid(name) ~ name, value.var = "value")[, name := NULL][]
    
       num AK HU KO
    1:   1  x  z  b
    2:   1  X  Z  B
    3:   2  y  a  c
    4:   2  Y  A  C
    5:   3  m  f  d
    6:   3  M  F  D
    7:   3  B  C  B
    

    数据

    library(data.table)
    df <- fread("
    name      value_1  value_2  value_3
     AK          x       X
     AK          y       Y
     AK          m       M         B
     HU          z       Z
     HU          a       A
     HU          f       F         C
     KO          b       B
     KO          c       C
     KO          d       D         B", fill = TRUE)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-25
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多