【问题标题】:Finding duplicate columns in a data.table在 data.table 中查找重复的列
【发布时间】:2017-06-06 07:16:51
【问题描述】:

我有一个非常大的 data.table (500 x 2000),我需要找出是否有任何列重复,即所有行的值是否相同。有没有办法在 data.table 结构中有效地做到这一点?

我已经为每对列尝试了一个简单的两个循环方法,all(col1 == col2),但它需要的时间太长。我也尝试过将其转换为data.frame并使用上述方法,但仍然需要相当长的时间。

我目前的解决方案是将data.table转换为矩阵并使用apply()函数作为:

similarity.matrix <- apply(m, 2, function(x) colSums(x == m)))/nrow(m)

但是,这种方法会强制所有元素的模式相同,我不希望这种情况发生。我还有什么其他选择?

这是 data.table 的示例构造:

m = matrix(sample(1:10, size=1000000, replace=TRUE), nrow=500, ncol=2000)
DF = as.data.frame(m)
DT = as.data.table(m)

【问题讨论】:

  • 您是否会接受结果中的误报,我的意思是,您是否手动检查可能重复的列?我可以想象每列计算一个哈希,并且相同的哈希值表示可能的重复。
  • 为什么不使用duplicated()
  • 感谢您的提示! duplicated() 可以创造奇迹。这比我笨拙的方法要快得多。

标签: r data.table


【解决方案1】:

我正在使用 mtcars 来获得可重现的结果:

library(data.table)
library(digest)

# Create  data
data <- as.data.table(mtcars)
data[, car.name := rownames(mtcars)]
data[, car.name.dup := car.name]           # create a duplicated row
data[, car.name.not.dup := car.name]       # create a second duplicated row...
data[1, car.name.not.dup := "Moon walker"] # ... but change a value so that it is no longer a duplicated column

data 现在包含:

> head(data)
    mpg cyl disp  hp drat    wt  qsec vs am gear carb          car.name      car.name.dup  car.name.not.dup
1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4         Mazda RX4         Mazda RX4       Moon walker
2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4     Mazda RX4 Wag     Mazda RX4 Wag     Mazda RX4 Wag
3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1        Datsun 710        Datsun 710        Datsun 710
4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1    Hornet 4 Drive    Hornet 4 Drive    Hornet 4 Drive
5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 Hornet Sportabout Hornet Sportabout Hornet Sportabout
6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1           Valiant           Valiant           Valiant

现在找到重复的列:

# create a vector with the checksum for each column (and keep the column names as row names)
col.checksums <- sapply(data, function(x) digest(x, "md5"), USE.NAMES = T)

# make a data table with one row per column name and hash value
dup.cols <- data.table(col.name = names(col.checksums), hash.value = col.checksums)

# self join using the hash values and filter out all column name pairs that were joined to themselves
dup.cols[dup.cols,, on = "hash.value"][col.name != i.col.name,]

结果:

       col.name                       hash.value   i.col.name
1: car.name.dup 58fed3da6bbae3976b5a0fd97840591d     car.name
2:     car.name 58fed3da6bbae3976b5a0fd97840591d car.name.dup

注意:结果仍然包含两个方向(col1 == col2 和 col2 == col1),应该去重 ;-)

【讨论】:

    【解决方案2】:

    按照@Haboryme* 的建议,您可以使用duplicated 来查找任何重复的向量。 duplicated 通常按行工作,但您可以将其与 t() 转置以查找重复项。

    DF <- DF[ , which( !duplicated( t( DF ) ) ) ]
    

    使用data.table,您可能需要添加with = FALSE(我认为这取决于您使用的data.table 的版本)。

    DT <- DT[ , which( !duplicated( t( DT ) ) ), with = FALSE ]
    

    *@Haboryme,如果你想把你的评论变成答案,请这样做,我会删除这个。

    【讨论】:

    • 干得好,结果是非重复的列索引,非常适合进一步的自动处理!
    • 请注意,duplicated 有一个数组的 MARGIN 参数,可以用来代替 t。喜欢duplicated.array(mtcars, MARGIN=2)
    • @rosscova 感谢您的回答!这对我有用。
    • @Imo,你是对的,我没有注意到,谢谢你的提示。有趣的是,我只是做了一些microbenchmark 测试,但它似乎并没有更快(在 1E5 行和 1E3 列 + 1 个重复列上,t() 为 204s,MARGIN=2 为 202s)。
    【解决方案3】:

    这是一种不同的方法,首先对每一列进行哈希处理,然后调用duplicated

    library(digest)
    dups <- duplicated(sapply(DF, digest)) 
    DF <- DF[,which(!dups)]
    

    根据您的数据,这可能是一种更快的方法。

    【讨论】:

    • 好主意,开销duplicated 操作现在每列处理一个哈希,并且在digest 中选择合适的哈希算法可以允许在哈希性能和哈希冲突密度之间进行权衡。也不需要昂贵的转置...
    • @mpjdem 感谢您提供摘要。它并没有加快我的处理速度,但我一定会记住这一点以备后用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    • 2013-02-21
    • 1970-01-01
    相关资源
    最近更新 更多