【问题标题】:Automatically stack every nth column of a dataframe自动堆叠数据框的每第 n 列
【发布时间】:2018-04-01 03:47:01
【问题描述】:

我有一个名为 DF 的日期框架,其中包含三个相互循环重复的变量:

      A      B      C      A      B      C 
1    a1     b1     c1     a5     b5     c5
2    a2     b2     c2     a6     b6     c6
3    a3     b3     c3     a7     b7     c7
4    a4     b4     c4     a8     b8     c8

我想将第一个 A 列堆叠在第二个 A 列上(以及第三个和第四个等,如果它们存在),并对其他变量执行相同操作,然后将结果另存为新对象(例如,作为向量)。 所以我想要得到的是

V_A <- c(a1,a2,a3,a4,a5,a6,a7,a8)
V_B <- c(b1,b2,b3,b4,b5,b6,b7,b8)
V_C <- c(c1,c2,c3,c4,c5,c6,c7,c8)

虽然手动操作很容易,像这样

V_A <- DF[,seq(1, ncol(DF), 3]
V_A <- stack(DF)
V_B <- DF[,seq(2, ncol(DF), 3]
V_B <- stack(DF)
V_C <- DF[,seq(3, ncol(DF), 3]
V_C <- stack(DF)

我正在寻找的是一个自动执行此操作的代码,以便它适用于具有各种变量的数据帧,而无需每次都编写临时代码。 总而言之,代码应该: 1)选择数据框中的每第n列 2)堆叠此列 3) 将结果保存在自动创建的新对象中

我觉得一定有办法做到这一点,但到目前为止我还没有成功。提前非常感谢。

编辑 假设我处于稍微不同的情况,列重复但名称不完全相同,我仍然想做同样的事情。所以我有:

     A1      B1      C1      A2      B2      C2 
1    a11     b11     c11     a25     b25     c25
2    a12     b12     c12     a26     b26     c26
3    a13     b13     c13     a27     b27     c27
4    a14     b14     c14     a28     b28     c28

我想要:

V_A <- c(a11,a12,a13,a14,a25,a26,a27,a28)
V_B <- c(b11,b12,b13,b14,b25,b26,b27,b28)
V_C <- c(c11,c12,c13,c14,c25,c26,c27,c28)

我该怎么做?

【问题讨论】:

    标签: r dataframe stack subset


    【解决方案1】:

    这里有一些替代方案。没有使用任何包。

    1) aperm 创建一个 3d 数组 a,置换维度并重塑为矩阵 m,然后将其转换为数据框。仅当所有值都属于同一类型时,此方法才有效。 (2) 和 (3) 没有这个限制。

    k <- 3
    nr <- nrow(DF)
    nc <- ncol(DF)
    unames <- unique(names(DF))
    
    a <- array(as.matrix(DF), c(nr, k, nc/k))
    m <- matrix(aperm(a, c(1, 3, 2)),, k, dimnames = list(NULL, unames))
    as.data.frame(m, stringsAsFactors = FALSE)
    

    给予:

       A  B  C
    1 a1 b1 c1
    2 a2 b2 c2
    3 a3 b3 c3
    4 a4 b4 c4
    5 a5 b5 c5
    6 a6 b6 c6
    7 a7 b7 c7
    8 a8 b8 c8
    

    如果我们处于问题编辑中给出的情况,则将 unames 替换为以下内容,其中 DF2 是 DF,并根据末尾的注释修改名称:

    unames <- unique(sub("\\d*$", "", names(DF2)))
    

    2) lapply 这概括了问题中的代码。 unames 定义如上:

    L <- lapply(split(as.list(DF), names(DF)), unlist)
    as.data.frame(L, stringsAsFactors = FALSE)
    

    给予:

       A  B  C
    1 a1 b1 c1
    2 a2 b2 c2
    3 a3 b3 c3
    4 a4 b4 c4
    5 a5 b5 c5
    6 a6 b6 c6
    7 a7 b7 c7
    8 a8 b8 c8
    

    使用问题的 EDIT 中显示的输入,可以这样做,其中DF2 在末尾的注释中可重复地给出。

    names0 <- sub("\\d*$", "", names(DF2))   # names without the trailing digits
    L <- lapply(split(as.list(DF2), names0), unlist)
    as.data.frame(L, stringsAsFactors = FALSE)
    

    3) 重塑 ncunames 来自上方。 varying 是一个包含 k 组件的列表,例如第 i 个组件包含索引向量 c(i, i+k, ...)reshape 似乎不喜欢重复的名称,因此我们将其提供为 setNames(DF, 1:nc) 作为输入。此解决方案确实具有生成索引向量timeid 的优点,它们将输出与输入数据相关联。

    varying <- split(1:nc, names(DF))
    reshape(setNames(DF, 1:nc), dir = "long", varying = varying, v.names = unames)
    

    给予:

        time  A  B  C id
    1.1    1 a1 b1 c1  1
    2.1    1 a2 b2 c2  2
    3.1    1 a3 b3 c3  3
    4.1    1 a4 b4 c4  4
    1.2    2 a5 b5 c5  1
    2.2    2 a6 b6 c6  2
    3.2    2 a7 b7 c7  3
    4.2    2 a8 b8 c8  4
    

    使用问题编辑中显示的输入,它实际上简化了。我们不再需要使用setNames(DF, 1:nc),而是可以直接使用数据框作为输入。此外,我们可以使用varying=TRUE(另见@thelatemail 的评论)而不是计算varying 的复杂参数。输入DF2如文末注释所示,names0如上(2)所示。

    reshape(DF2, dir = "long", varying = TRUE, v.names = unique(names0))
    

    注意:

    Lines <- "      A      B      C      A      B      C 
    1    a1     b1     c1     a5     b5     c5
    2    a2     b2     c2     a6     b6     c6
    3    a3     b3     c3     a7     b7     c7
    4    a4     b4     c4     a8     b8     c8"
    DF <- read.table(text = Lines, as.is = TRUE, check.names = FALSE)
    
    DF2 <- setNames(DF, c("A1", "B1", "C1", "A2", "B2", "C2")) # test input
    

    更新:一些简化。还在最后的注释中添加了DF2,并在每个替代方案中讨论如何修改代码来处理它。 (一种通用的方法可能只是将 DF2 减少到 DF,正如我在下面的 cmets 中讨论的那样。)

    【讨论】:

    • 这是reshape 真正闪耀的场合之一。尽管在创建变量时不需要rep,但我不认为。 split(seq_len(nc), seq_len(k))
    • 很好的简化。实际上,我可以在names(DF) 上进行拆分,这就是我现在所做的。
    • 我不需要做setNames(DF, 1:nc) 部分。没有它似乎也能正常工作。
    • 我最初也是这么认为的,但事实上如果你检查输出然后没有它是错误的。
    • 问题编辑中的情况可以通过首先使用names(DF) &lt;- sub("\\d*$", "", names(DF))从名称中删除任何尾随数字来简化为原始问题中给出的情况。
    【解决方案2】:

    stackunstack

    NEWDF=stack(DF)
    NEWDF$ind=as.character(NEWDF$ind)
    NEWDF$ind=rep(NEWDF$ind[1:(dim(NEWDF)[1]/2)],2)
    unstack(NEWDF)
       A  B  C
    1 a1 b1 c1
    2 a2 b2 c2
    3 a3 b3 c3
    4 a4 b4 c4
    5 a5 b5 c5
    6 a6 b6 c6
    7 a7 b7 c7
    8 a8 b8 c8
    

    【讨论】:

      【解决方案3】:

      我们可以按列拆分然后行绑定 (using G. Grothendieck's example data, DF and DF2):

      library(data.table) #rbindlist for binding columns with different names.
      
      k <- 3
      nr <- nrow(DF)
      nc <- ncol(DF)
      
      rbindlist(split.default(DF, rep(1:(nc/k), each = k)))
      #     A  B  C
      # 1: a1 b1 c1
      # 2: a2 b2 c2
      # 3: a3 b3 c3
      # 4: a4 b4 c4
      # 5: a5 b5 c5
      # 6: a6 b6 c6
      # 7: a7 b7 c7
      # 8: a8 b8 c8
      
      rbindlist(split.default(DF2, rep(1:(nc/k), each = k)), use.names = FALSE)
      #    A1 B1 C1
      # 1: a1 b1 c1
      # 2: a2 b2 c2
      # 3: a3 b3 c3
      # 4: a4 b4 c4
      # 5: a5 b5 c5
      # 6: a6 b6 c6
      # 7: a7 b7 c7
      # 8: a8 b8 c8
      

      【讨论】:

        【解决方案4】:

        这会将第 n 列堆叠到单独的 data.frames 中,并存储在列表中:

        library(tidyr)
        cols <- ncol(dat)
        set_width <- 3
        result <- dat %>%
          gather(key, value) %>%
          split(., rep(rep(1:set_width, each = nrow(dat)), ncol(dat)/set_width))
        

        数据:

        dat <- read.table(text = "      A      B      C      A      B      C 
        1    a1     b1     c1     a5     b5     c5
                          2    a2     b2     c2     a6     b6     c6
                          3    a3     b3     c3     a7     b7     c7
                          4    a4     b4     c4     a8     b8     c8", check.names = TRUE)
        

        (如果您的变量名称不唯一,即它们完全重复,请将 janitor::clean_names() 插入管道中)。

        【讨论】:

          【解决方案5】:

          我们可以使用来自dplyr 包的bind_rows

          df1 <- df[,1:3]
          df2 <- df[,4:6]
          colnames(df2) <- colnames(df1) # for differing colnames
          bind_rows(df1, df2)
          

          输出:

            A     B     C    
            <chr> <chr> <chr>
          1 a1    b1    c1   
          2 a2    b2    c2   
          3 a3    b3    c3   
          4 a4    b4    c4   
          5 a5    b5    c5   
          6 a6    b6    c6   
          7 a7    b7    c7   
          8 a8    b8    c8  
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-17
            • 2023-01-11
            • 1970-01-01
            • 1970-01-01
            • 2020-09-06
            相关资源
            最近更新 更多