foo <- data.table(x = 1:5/sum(1:5),
y = (-4):0/sum((-4):0),
z1 = 2:6/sum(2:6),
z2 = 2:6/sum(2:6))
假设我有foo
数据表(如上所述):
x y z1 z2
1: 0.06666667 0.4 0.10 0.10
2: 0.13333333 0.3 0.15 0.15
3: 0.20000000 0.2 0.20 0.20
4: 0.26666667 0.1 0.25 0.25
5: 0.33333333 0.0 0.30 0.30
如何有效地计算唯一列?在这种情况下只有3。
请假设一般:
foo
始终是数据表,而不是矩阵;虽然列总是数字。foo
实际上很大,但是&gt; 20k和ncol> 100 是否可以在不制作额外数据副本的情况下执行此操作?
我目前的方法是apply
使用paste
的列来获取每列的单个值,然后对结果执行length(unique(.))
...
data.table::transpose()
,data.table::uniqueN
和其他朋友有什么魔力吗?
答案 0 :(得分:5)
另一种可能性:
length(unique(as.list(foo)))
这给出了预期的结果:
> length(unique(as.list(foo))) [1] 3
注意:必须使用length(unique())
,因为uniqueN()
会返回错误。
根据@Ryan的评论,你也可以这样做:
length(unique.default(foo))
关于速度,两种方法都具有可比性(当在更大的5M行数据集上测量时):
> fooLarge <- foo[rep(1:nrow(foo),1e6)] > microbenchmark(length(unique.default(fooLarge)), length(unique(as.list(fooLarge)))) Unit: milliseconds expr min lq mean median uq max neval cld length(unique.default(fooLarge)) 94.0433 94.56920 95.24076 95.01492 95.67131 103.15433 100 a length(unique(as.list(fooLarge))) 94.0254 94.68187 95.17648 95.02672 95.49857 99.19411 100 a
如果您只想保留唯一列,可以使用:
# option 1
cols <- !duplicated(as.list(foo))
foo[, ..cols]
# option 2 (doesn't retain the column names)
as.data.table(unique.default(foo))
给出(显示输出选项1):
x y z1 1: 0.06666667 0.4 0.10 2: 0.13333333 0.3 0.15 3: 0.20000000 0.2 0.20 4: 0.26666667 0.1 0.25 5: 0.33333333 0.0 0.30
答案 1 :(得分:1)
转置并检查非重复
ncol( foo[ , which( !duplicated( t( foo ) ) ), with = FALSE ])
3
答案 2 :(得分:1)
如果您期望大量重复项,可能会更快的另一种方法:
n_unique_cols <- function(foo) {
K <- seq_along(foo)
for (j in seq_along(foo)) {
if (j %in% K) {
foo_j <- .subset2(foo, j)
for (k in K) {
if (j < k) {
foo_k <- .subset2(foo, k)
if (foo_j[1] == foo_k[1] && identical(foo_j, foo_k)) {
K <- K[K != k]
}
rm(foo_k)
}
}
}
}
length(K)
}
时序:
library(data.table)
create_foo <- function(row, col) {
foo <- data.table(x = rnorm(row),
y = seq_len(row) - 2L)
set.seed(1)
for (k in seq_len(col %/% 2L)) {
foo[, (paste0('x', k)) := x + sample(-4:4, size = 1)]
foo[, (paste0('y', k)) := y + sample(-2:2, size = 1)]
}
foo
}
library(bench)
res <-
press(rows = c(1e5, 1e6, 1e7),
cols = c(10, 50, 100),
{
foorc <- create_foo(rows, cols)
bench::mark(n_unique_cols(foorc),
length(unique(as.list(foorc))))
})
plot(res)
对于这个数据系列,此函数的速度是其两倍,但其内存消耗增长速度快于unique(as.list(.))
。