2017-01-21 8 views
1

У меня есть довольно большой 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) 
+0

Вы принимаете ложные срабатывания в результате, я имею в виду, вы проверяете, возможно, дублированные столбцы вручную? Я мог представить себе вычисление одного хэша на столбец, и одно и то же значение хэша указывает на возможные дубликаты. –

+0

Почему бы не использовать 'duplicated()'? – Haboryme

+0

Спасибо за отзыв! duplicated() творит чудеса. Это намного быстрее, чем мой неудобный подход. – Naumz

ответ

5

По предложению @ 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, если вы собираетесь превратить ваш комментарий в ответ, пожалуйста, и я удалю это.

+0

Хорошо, результаты - это не дублированные индексы столбцов, которые идеально подходят для дальнейшей автоматической обработки! –

+0

Спасибо @R Yoda! – rosscova

+0

Обратите внимание, что 'duplicated' имеет аргумент MARGIN для массивов, который можно использовать вместо' t'. например 'duplicated.array (mtcars, MARGIN = 2)'. – lmo

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) и должны быть дедуплицированы ;-)

+0

благодарим за ответ! – Naumz

2

Вот другой подход, в котором вы сначала используете хэш каждого столбца, а затем вызываете duplicated.

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

В зависимости от ваших данных это может быть более быстрым способом.

+0

хорошая идея, операция 'duplicated' в расчете на один хэш на один столбец теперь и выбор достойного алгоритма хэширования в' digest' может позволить компромисс между хешированием и плотностью столкновений хэшей. Не требуется дорогостоящая переноска ... –

+0

@mpjdem благодарит вас за указатель на переваривание. Это не ускорило мое дело, но я обязательно буду иметь это в виду для последующего использования. – Naumz