У меня относительно большой набор данных (1 750 000 строк, 5 столбцов), который содержит записи с уникальными значениями идентификаторов (первый столбец), описанные по четырем критериям (4 других столбца).Вот небольшой пример:
# example
library(data.table)
dt <- data.table(id=c("a1","b3","c7","d5","e3","f4","g2","h1","i9","j6"),
s1=c("a","b","c","l","l","v","v","v",NA,NA),
s2=c("d","d","e","k","k","o","o","o",NA,NA),
s3=c("f","g","f","n","n","s","r","u","w","z"),
s4=c("h","i","j","m","m","t","t","t",NA,NA))
, который выглядит следующим образом:
id s1 s2 s3 s4
1: a1 a d f h
2: b3 b d g i
3: c7 c e f j
4: d5 l k n m
5: e3 l k n m
6: f4 v o s t
7: g2 v o r t
8: h1 v o u t
9: i9 <NA> <NA> w <NA>
10: j6 <NA> <NA> z <NA>
Моя конечная цель - найти все записи с одним и тем же символом в любом описании.столбцы (без учета NA) и сгруппировать их под новым идентификатором, чтобы можно было легко идентифицировать дублированные записи.Эти идентификаторы создаются путем объединения идентификаторов каждой строки.
Все пошло не так, потому что я могу найти эти записи с дублированными описаниями напрямую и косвенно.Поэтому в настоящее время я выполняю эту операцию в два этапа.
ШАГ 1 - Построение дублированных идентификаторов на основе прямых дубликатов
# grouping ids with duplicated info in any of the columns
#sorry, I could not find search for duplicates using multiple columns simultaneously...
dt[!is.na(dt$s1),ids1:= paste(id,collapse="|"), by = list(s1)]
dt[!is.na(dt$s1),ids2:= paste(id,collapse="|"), by = list(s2)]
dt[!is.na(dt$s1),ids3:= paste(id,collapse="|"), by = list(s3)]
dt[!is.na(dt$s1),ids4:= paste(id,collapse="|"), by = list(s4)]
# getting a unique duplicated ID for each row
dt$new.id <- apply(dt[,.(ids1,ids2,ids3,ids4)], 1, paste, collapse="|")
dt$new.id <- apply(dt[,"new.id",drop=FALSE], 1, function(x) paste(unique(strsplit(x,"\\|")[[1]]),collapse="|"))
Эта операция приводит к следующему, с определением уникального дублированного идентификатора.as "new.id":
id s1 s2 s3 s4 ids1 ids2 ids3 ids4 new.id
1: a1 a d f h a1 a1|b3 a1|c7 a1 a1|b3|c7
2: b3 b d g i b3 a1|b3 b3 b3 b3|a1
3: c7 c e f j c7 c7 a1|c7 c7 c7|a1
4: d5 l k n m d5|e3 d5|e3 d5|e3 d5|e3 d5|e3
5: e3 l k n m d5|e3 d5|e3 d5|e3 d5|e3 d5|e3
6: f4 v o s t f4|g2|h1 f4|g2|h1 f4 f4|g2|h1 f4|g2|h1
7: g2 v o r t f4|g2|h1 f4|g2|h1 g2 f4|g2|h1 f4|g2|h1
8: h1 v o u t f4|g2|h1 f4|g2|h1 h1 f4|g2|h1 f4|g2|h1
9: i9 <NA> <NA> w <NA> <NA> <NA> <NA> <NA> NA
10: j6 <NA> <NA> z <NA> <NA> <NA> <NA> <NA> NA
Обратите внимание, что записи "b3" и "c7" дублируются косвенно через "a1" (все остальные примеры являются прямыми дубликатами, которые должны оставаться такими же).Вот почему нам нужен следующий шаг.
ШАГ 2 - Обновление дублированных идентификаторов на основе косвенных дубликатов
#filtering the relevant columns for the indirect search
dt = dt[,.(id,new.id)]
#creating the patterns to be used by grepl() for the look-up for each row
dt[,patt:= .(paste(paste("^",id,"\\||",sep=""),paste("\\|",id,"\\||",sep=""),paste("\\|",id,"$",sep=""),collapse = "" ,sep="")), by = list(id)]
#Transforming the ID vector into factor and setting it as a 'key' to the data.table (speed up the processing)
dt$new.id = as.factor(dt$new.id)
setkeyv(dt, c("new.id"))
#Performing the loop using sapply
library(stringr)
for(i in 1:nrow(dt)) {
pat = dt$patt[i] # retrieving the research pattern
tmp = dt[new.id %like% pat] # searching the pattern using grepl()
if(dim(tmp)[1]>1) {
x = which.max(str_count(tmp$new.id, "\\|"))
dt$new.id[i] = as.character(tmp$new.id[x])
}
}
#filtering the final columns
dt = dt[,.(id,new.id)]
Финальная таблица выглядит следующим образом:
id new.id
1: a1 a1|b3|c7
2: b3 a1|b3|c7
3: c7 a1|b3|c7
4: d5 d5|e3
5: e3 d5|e3
6: f4 f4|g2|h1
7: g2 f4|g2|h1
8: h1 f4|g2|h1
9: i9 NA
10: j6 NA
Обратите внимание, что теперь первые три записи («a1», «b3», «c7») сгруппированы по более широкому дублированному идентификатору, который содержит как прямые, так и косвенные записи.
Все работает нормально, но мойкод ужасно медленныйПотребовалось 2 полных дня, чтобы запустить половину набора данных (~ 800,0000).Я мог бы распараллелить цикл на разные ядра, но все равно это заняло бы часы.И я почти уверен, что мог бы использовать функции data.table лучше, возможно, используя set в цикле.Я потратил часы сегодня, пытаясь реализовать те же самые коды, используя data.table, но я новичок в его синтаксисе, и мне действительно трудно здесь.Любые предложения о том, как я мог бы оптимизировать этот код?
Примечание. Самая медленная часть кода - это цикл, а внутри цикла самый неэффективный шаг - это grepl () шаблонов внутри data.table.Кажется, что установка «ключа» для data.table может ускорить процесс, но я не изменил время, необходимое для выполнения grepl () в моем случае.