R:将多列组合为同一行中的成对列单元格
R: Combine multiple columns as pairs of column cells in same row
我想 combine/pair 数据框中的多列作为同一行中的列单元格对。例如,df1
应转换为 df2
。
df1
col1 col2 col3
1 2 3
0 0 1
df2
c1 c2
1 2
1 3
2 3
0 0
0 1
0 1
该解决方案应该可扩展 df1
秒(方式)超过三列。
我考虑过melt/reshape/dcast,但还没有找到解决办法。数据框中没有 NA。谢谢!
编辑:Reshape
刚刚产生了错误,所以我考虑了
combn(df1[1,], 2)
comb2 <- t(comb1)
并循环并追加所有行。这效率低下,考虑到 200 万行..
您的编辑与我在下面的回答非常相似,您只需要将每次迭代的结果绑定到 df1 的行上。使用 data.table 是加速 rbind 的好方法,see this answer for more。
编辑:不幸的是,当我切换到 data.table 方法时,结果是 rbindlist() 导致答案错误(正如下面的评论所指出的)。因此,虽然它可能稍微慢一些,但我认为预分配一个数据框并使用 rbind 可能是最好的选择。
EDIT2:将预分配的 df 切换为更一般的行数。
df1 = as.data.frame(matrix(c(1,2,3,4,0,0,1,1), byrow = TRUE, nrow = 2))
nrow_combn = nrow(t(combn(df1[1,], m = 2)))
nrow_df = nrow(df1) * nrow_combn
df2 = data.frame(V1 = rep(0, nrow_df), V2 = rep(0, nrow_df))
for(i in 1:nrow(df1)){
df2[(((i-1)*nrow_combn)+1):(i*(nrow_combn)), ] = data.frame(t(combn(df1[i,], m = 2)))
}
这是我会采用的方法。
创建一个使用 "data.table" 中的 rbindlist
和基础 R 中的 combn
的函数。该函数如下所示:
lengthener <- function(indf) {
temp <- rbindlist(
combn(names(indf), 2, FUN = function(x) indf[x], simplify = FALSE),
use.names = FALSE, idcol = TRUE)
setorder(temp[, .id := sequence(.N), by = .id], .id)[, .id := NULL][]
}
这是来自其他答案的示例数据,以及函数在其上的应用:
df1 = as.data.frame(matrix(c(1,2,3,4,0,0,1,1), byrow = TRUE, nrow = 2))
lengthener(df1)
# V1 V2
# 1: 1 2
# 2: 1 3
# 3: 1 4
# 4: 2 3
# 5: 2 4
# 6: 3 4
# 7: 0 0
# 8: 0 1
# 9: 0 1
# 10: 0 1
# 11: 0 1
# 12: 1 1
也在一些更大的数据上进行测试:
set.seed(1)
M <- as.data.frame(matrix(sample(100, 100*100, TRUE), 100))
system.time(out <- lengthener(M))
# user system elapsed
# 0.19 0.00 0.19
out
# V1 V2
# 1: 27 66
# 2: 27 27
# 3: 27 68
# 4: 27 66
# 5: 27 56
# ---
# 494996: 33 13
# 494997: 33 66
# 494998: 80 13
# 494999: 80 66
# 495000: 13 66
另一种方法的系统时间:
funAMK <- function(indf) {
nrow_combn = nrow(t(combn(indf[1,], m = 2)))
nrow_df = nrow(indf) * nrow_combn
df2 = data.frame(V1 = rep(0, nrow_df), V2 = rep(0, nrow_df))
for(i in 1:nrow(indf)){
df2[(((i-1)*nrow_combn)+1):(i*(nrow_combn)), ] = data.frame(t(combn(indf[i,], m = 2)))
}
df2
}
> system.time(funAMK(M))
user system elapsed
16.03 0.16 16.37
我想 combine/pair 数据框中的多列作为同一行中的列单元格对。例如,df1
应转换为 df2
。
df1
col1 col2 col3
1 2 3
0 0 1
df2
c1 c2
1 2
1 3
2 3
0 0
0 1
0 1
该解决方案应该可扩展 df1
秒(方式)超过三列。
我考虑过melt/reshape/dcast,但还没有找到解决办法。数据框中没有 NA。谢谢!
编辑:Reshape
刚刚产生了错误,所以我考虑了
combn(df1[1,], 2)
comb2 <- t(comb1)
并循环并追加所有行。这效率低下,考虑到 200 万行..
您的编辑与我在下面的回答非常相似,您只需要将每次迭代的结果绑定到 df1 的行上。使用 data.table 是加速 rbind 的好方法,see this answer for more。
编辑:不幸的是,当我切换到 data.table 方法时,结果是 rbindlist() 导致答案错误(正如下面的评论所指出的)。因此,虽然它可能稍微慢一些,但我认为预分配一个数据框并使用 rbind 可能是最好的选择。
EDIT2:将预分配的 df 切换为更一般的行数。
df1 = as.data.frame(matrix(c(1,2,3,4,0,0,1,1), byrow = TRUE, nrow = 2))
nrow_combn = nrow(t(combn(df1[1,], m = 2)))
nrow_df = nrow(df1) * nrow_combn
df2 = data.frame(V1 = rep(0, nrow_df), V2 = rep(0, nrow_df))
for(i in 1:nrow(df1)){
df2[(((i-1)*nrow_combn)+1):(i*(nrow_combn)), ] = data.frame(t(combn(df1[i,], m = 2)))
}
这是我会采用的方法。
创建一个使用 "data.table" 中的 rbindlist
和基础 R 中的 combn
的函数。该函数如下所示:
lengthener <- function(indf) {
temp <- rbindlist(
combn(names(indf), 2, FUN = function(x) indf[x], simplify = FALSE),
use.names = FALSE, idcol = TRUE)
setorder(temp[, .id := sequence(.N), by = .id], .id)[, .id := NULL][]
}
这是来自其他答案的示例数据,以及函数在其上的应用:
df1 = as.data.frame(matrix(c(1,2,3,4,0,0,1,1), byrow = TRUE, nrow = 2))
lengthener(df1)
# V1 V2
# 1: 1 2
# 2: 1 3
# 3: 1 4
# 4: 2 3
# 5: 2 4
# 6: 3 4
# 7: 0 0
# 8: 0 1
# 9: 0 1
# 10: 0 1
# 11: 0 1
# 12: 1 1
也在一些更大的数据上进行测试:
set.seed(1)
M <- as.data.frame(matrix(sample(100, 100*100, TRUE), 100))
system.time(out <- lengthener(M))
# user system elapsed
# 0.19 0.00 0.19
out
# V1 V2
# 1: 27 66
# 2: 27 27
# 3: 27 68
# 4: 27 66
# 5: 27 56
# ---
# 494996: 33 13
# 494997: 33 66
# 494998: 80 13
# 494999: 80 66
# 495000: 13 66
另一种方法的系统时间:
funAMK <- function(indf) {
nrow_combn = nrow(t(combn(indf[1,], m = 2)))
nrow_df = nrow(indf) * nrow_combn
df2 = data.frame(V1 = rep(0, nrow_df), V2 = rep(0, nrow_df))
for(i in 1:nrow(indf)){
df2[(((i-1)*nrow_combn)+1):(i*(nrow_combn)), ] = data.frame(t(combn(indf[i,], m = 2)))
}
df2
}
> system.time(funAMK(M))
user system elapsed
16.03 0.16 16.37