R - 自动堆叠数据框的每第n列并将其保存为新对象

时间:2017-10-20 01:34:20

标签: r dataframe stack subset

我有一个名为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)将结果保存在自动创建的新对象中

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

EDIT 假设我的情况略有不同,其中列重复但名称不完全相同,我仍然想做同样的事情。所以我有:

     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)

我该怎么做?

5 个答案:

答案 0 :(得分:5)

以下是一些替代方案。没有包使用。

1)精子创建一个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

根据问题的编辑中显示的输入,可以这样完成,其中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

Upate:一些简化。最后在Note中添加了DF2,并在每个备选方案中讨论如何修改代码来处理它。 (正如我在下面的评论中讨论的那样,一般方法可能只是将DF2减少到DF。)

答案 1 :(得分:1)

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

答案 2 :(得分:1)

我们可以按列拆分然后行绑定(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

答案 3 :(得分:0)

这使您可以将第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()插入管道中。

答案 4 :(得分:0)

我们可以使用 bind_rows 包中的 dplyr

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