ms6*_*609 19 r matrix duplicates
我有一个具有许多行和列的矩阵
x <- matrix(c(1, 1, 3, 3, 55, 55, 1, 3, 3, 1,
1, 1, 3, 3, 55, 55, 1, 3, 9, 1), ncol = 2)
Run Code Online (Sandbox Code Playgroud)
在每组重复行(即每组相同的行)中,我希望标识第一行索引并将其分配给该组中的所有出现情况。例如,1两列(第 1、2、7、10 行)中都有多个重复行。在每一行上,我想要第一行索引,即 1。
x
# [,1] [,2]
# [1,] 1 1 # first row of 1-1. Assign its row index, 1, to all 1-1 rows
# [2,] 1 1
# [3,] 3 3 # first row of 3-3. Assign its row index, 3, to all 3-3 rows
# [4,] 3 3
# [5,] 55 55 # first row of 55-55. Assign its row index, 5, to all 55-55 rows
# [6,] 55 55
# [7,] 1 1
# [8,] 3 3
# [9,] 3 9 # first (and only) row of 3-9; row index 9
# [10,] 1 1
Run Code Online (Sandbox Code Playgroud)
期望的结果:
1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
我想出的最好的方法是基于duplicatedandfor循环的复杂方法,既不高效也不优雅。我还知道data.frames 的可能解决方案;那些涉及将行连接成字符串的操作也非常消耗资源。
1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
有没有使用baseR 的优雅解决方案?
Hen*_*rik 15
对于大小相等但形状不同的整数矩阵(5e+06-by-2、5e+05-by-20、5000-by-2000),包含从 1 到 10 的整数,base测试的最快答案是grouping/ ,在@alexis_laz 的评论match中建议。最快的非答案是/ ,尽管/在所有情况下都具有可比性,甚至优于5000×2000 情况下的答案。basedata.table::frankmatchgroupingmatchdata.table
请注意,对于双精度矩阵或范围更大的整数矩阵,结果可能会有所不同,具体取决于 可用的线程数data.table。[去做?]
@MikaelJagan 的asplit/match(<list>, <list>) 答案似乎是“使用基本 R 的优雅解决方案”。但是,?match警告:
列表匹配可能非常慢,最好避免,除非是简单的情况。
鉴于 OP 有“一个包含许多行和列的矩阵”,我们想要将asplit/match(<list>, <list>)答案的性能与其他base答案的性能进行比较:
paste/match(<chr>, <chr>) 答案;interaction/match(<int>, <int>) 答案;grouping/match(<int>, <int>) 答案。我们将这些与一些非base答案一起进行了基准测试,我们将其用作参考点(认识到OPbase仅要求):
Rcpp 回答;data.table回答:
which = TRUE和mult = "first"to [.data.table;frank(ties.method = "average")/ match(<dbl>, <dbl>),frank(ties.method = "dense")/ match(<int>, <int>)。library(microbenchmark)
library(data.table)
getDTthreads() # 4
f_asplit <- function(x) {
l <- asplit(x, 1L)
match(l, l) }
f_paste <- function(x) {
s <- do.call(paste, as.data.frame(x))
match(s, s) }
f_interaction <- function(x) {
z <- as.integer(interaction(as.data.frame(x)))
match(z, z) }
f_grouping <- function(x) {
g <- do.call(grouping, as.data.frame(x))
o <- order(g, method = "radix")
e <- attr(g, "ends")
z <- rep.int(seq_along(e), c(e[1L], e[-1L] - e[-length(e)]))[o]
match(z, z) }
f_join <- function(x) {
d <- as.data.table(x)
d[d, on = names(d), mult = "first", which = TRUE] }
f_frank_average <- function(x) {
d <- as.data.table(x)
r <- frank(d, ties.method = "average")
match(r, r) }
f_frank_dense <- function(x) {
d <- as.data.table(x)
r <- frank(d, ties.method = "dense")
match(r, r) }
Rcpp::sourceCpp('<copy source code from @MikaelJagan\'s answer here>')
Run Code Online (Sandbox Code Playgroud)
5e+06我们首先使用×2 整数矩阵评估性能:
set.seed(1L)
x <- matrix(sample(10L, size = 1e+07L, replace = TRUE), ncol = 2L)
microbenchmark(
f_asplit(x),
f_paste(x),
f_interaction(x),
f_grouping(x),
f_join(x),
f_frank_average(x),
f_frank_dense(x),
f_rcpp(x),
times = 10L,
check = "identical",
setup = gc(FALSE)
)
Run Code Online (Sandbox Code Playgroud)
Unit: milliseconds
expr min lq mean median uq max neval
f_asplit(x) 17369.93905 18861.91195 19070.21298 19013.0180 19207.29194 22420.71085 10
f_paste(x) 502.63884 507.35077 509.01823 509.2443 511.72301 515.10083 10
f_interaction(x) 234.19311 236.52494 241.80098 238.7392 242.32923 259.75644 10
f_grouping(x) 182.25226 182.89358 187.09642 184.6124 187.10444 208.15532 10
f_join(x) 119.43460 120.86829 123.16607 122.9332 125.07169 128.44722 10
f_frank_average(x) 104.40150 107.53607 111.00268 108.5597 116.80375 121.83675 10
f_frank_dense(x) 86.60926 88.29555 91.42976 90.4716 92.32413 99.30659 10
f_rcpp(x) 459.02304 464.79855 472.43669 468.2492 470.25508 523.06734 10
Run Code Online (Sandbox Code Playgroud)
f_asplit比替代方案慢两个数量级base。f_grouping是最快的base答案,但f_frank_dense速度快了大约 2 倍(总体上最快)。
上述结果并不能推广到所有整数矩阵输入。例如,f_interaction扩展性非常差:如果 的每一列都有唯一的元素ncol(x),则可能的交互数量。u^ncol(x)xu
因此,我们执行了第二次基准测试,这次考虑的是行数较少 ( 5e+05) 和列数较多 (20) 的矩阵。
set.seed(1L)
x <- matrix(sample(10L, size = 1e+07L, replace = TRUE), ncol = 20L)
Run Code Online (Sandbox Code Playgroud)
初始测试f_interaction导致内存分配错误,因此被排除在基准测试之外。
system.time(f_interaction(x))
Run Code Online (Sandbox Code Playgroud)
Error: cannot allocate vector of size 7.5 Gb
Timing stopped at: 173.2 6.05 200.4
Run Code Online (Sandbox Code Playgroud)
microbenchmark(
f_asplit(x),
f_paste(x),
## f_interaction(x),
f_grouping(x),
f_join(x),
f_frank_average(x),
f_frank_dense(x),
f_rcpp(x),
times = 10L,
check = "identical",
setup = gc(FALSE)
)
Run Code Online (Sandbox Code Playgroud)
Unit: milliseconds
expr min lq mean median uq max neval
f_asplit(x) 5416.08762 5681.23523 5731.89246 5732.31779 5905.44517 5913.77141 10
f_paste(x) 592.92990 604.15083 629.31101 623.78679 637.81814 724.83871 10
f_grouping(x) 63.89522 64.14134 65.42723 65.11530 66.00557 68.06045 10
f_join(x) 340.73722 342.18096 353.35774 352.08861 359.88480 382.13480 10
f_frank_average(x) 69.90496 70.81840 72.29819 72.04409 73.11977 77.44347 10
f_frank_dense(x) 52.58033 53.33760 54.42029 54.01672 55.63532 56.99664 10
f_rcpp(x) 184096.21999 184816.36584 185774.76817 186218.58335 186696.31674 186781.24972 10
Run Code Online (Sandbox Code Playgroud)
f_grouping仍然是最快的base答案。值得注意的是,它现在比 快了f_paste整整一个数量级,并且仅比f_frank_dense.
我们执行了最终基准测试,排除了上一轮 (f_asplit和f_rcpp) 中最慢的答案,现在考虑 5000×2000 整数矩阵:
set.seed(1L)
x <- matrix(sample(10L, size = 1e+07L, replace = TRUE), ncol = 2000L)
Run Code Online (Sandbox Code Playgroud)
microbenchmark(
## f_asplit(x),
f_paste(x),
## f_interaction(x),
f_grouping(x),
f_join(x),
f_frank_average(x),
f_frank_dense(x),
## f_rcpp(x),
times = 10L,
check = "identical",
setup = gc(FALSE)
)
Run Code Online (Sandbox Code Playgroud)
Unit: milliseconds
expr min lq mean median uq max neval
f_paste(x) 1067.47994 1075.45148 1083.17391 1080.72997 1089.74027 1102.45249 10
f_grouping(x) 19.24007 19.50026 19.86404 19.79002 20.25302 20.60127 10
f_join(x) 616.66706 621.29854 630.61460 628.16315 636.39097 650.16180 10
f_frank_average(x) 59.82007 61.41706 62.68610 62.99318 64.56520 64.88463 10
f_frank_dense(x) 58.03648 60.59857 63.50526 61.99278 66.03694 71.30638 10
Run Code Online (Sandbox Code Playgroud)
现在f_grouping总体来说是最快的,比之前快了f_frank_dense大约 3 倍。
Ony*_*mbu 12
如果您有大矩阵,那么以下解决方案可能就足够了:
l <- do.call(paste, data.frame(x))
match(l, l)
[1] 1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
Mik*_*gan 11
这个怎么样?
l <- asplit(x, 1L)
match(l, l)
Run Code Online (Sandbox Code Playgroud)
[1] 1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
在这里,我们使用asplit来获取行列表l并x获取match每行第一次出现的索引。
ave如果您正在使用 Base R,我们可以使用
> ave(1:nrow(x), x[, 1], x[, 2], FUN = function(v) v[1])
[1] 1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
如果你有多个列,你可以尝试
> z <- as.integer(interaction(as.data.frame(m2)))
> ave(seq_along(z), z, FUN = function(x) x[1])
[1] 1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
或者
> z <- as.integer(interaction(as.data.frame(x)))
> match(z, z)
[1] 1 1 3 3 5 5 1 3 9 1
Run Code Online (Sandbox Code Playgroud)
set.seed(1)
v <- sample(1:10, 1e7, replace = TRUE)
m2 <- matrix(v, ncol = 2)
microbenchmark(
ave1 = {
ave(1:nrow(m2), m2[, 1], m2[, 2], FUN = function(v) v[1])
},
ave2 = {
z <- as.integer(interaction(as.data.frame(m2)))
ave(seq_along(z), z, FUN = function(x) x[1])
},
match = {
z <- as.integer(interaction(as.data.frame(m2)))
match(z, z)
},
times = 10L
)
Run Code Online (Sandbox Code Playgroud)
我们将会看到
Unit: milliseconds
expr min lq mean median uq max neval
ave1 648.0755 655.9521 715.8848 701.1927 747.4759 885.9838 10
ave2 785.4868 883.2935 913.3867 899.1789 929.6571 1050.9020 10
match 417.1598 447.3718 507.0462 495.8791 551.9436 625.0841 10
Run Code Online (Sandbox Code Playgroud)