Se il data.frame è abbastanza grande, la la velocità potrebbe essere una questione per te. Puoi trovare set duplicati molto più velocemente con la seguente idea.
Immaginiamo di assegnare ogni possibile valore in righe un numero primo e contare i prodotti per ogni riga. Ad esempio, per il dato df
possiamo accettare primenums = c(2,3,5,7)
e contare i prodotti c(30,30,70)
. Quindi i duplicati in questo vettore di prodotti corrispondono a set duplicati nel nostro data.frame. Poiché la moltiplicazione viene calcolata molto più velocemente di qualsiasi tipo di ordinamento, puoi ottenere efficienza. Il codice è il seguente.
require("numbers")
primenums <- Primes(100)[1:4]
dfmult <- apply(as.matrix(df), 1, function(z) prod(primenums[z]))
my_indx <- !duplicated(dfmult)
df[my_indx,]
Qui inizializziamo vettore primenums
con l'aiuto della funzione di Primes
dalla confezione numbers
, ma si può fare manualmente in altro modo.
Dai un'occhiata all'esempio. Qui mostro il confronto dell'efficienza.
require("numbers")
# generate all unique combinations 10 out of 20
allcomb <- t(combn(20,10))
# make sample of 1 million rows
set.seed(789)
df <- allcomb[sample(nrow(allcomb), 1e6, T),]
# lets sort matrix to show we have duplicates
df <- df[do.call(order, lapply(1:ncol(df), function(i) df[, i])), ]
head(df, 10)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,] 1 2 3 4 5 6 7 8 9 10
# [2,] 1 2 3 4 5 6 7 8 9 10
# [3,] 1 2 3 4 5 6 7 8 9 10
# [4,] 1 2 3 4 5 6 7 8 9 10
# [5,] 1 2 3 4 5 6 7 8 9 11
# [6,] 1 2 3 4 5 6 7 8 9 11
# [7,] 1 2 3 4 5 6 7 8 9 11
# [8,] 1 2 3 4 5 6 7 8 9 11
# [9,] 1 2 3 4 5 6 7 8 9 11
# [10,] 1 2 3 4 5 6 7 8 9 11
# to be fair need to permutate numbers in rows before searching for identical sets
df <- t(apply(df, 1, function(z) z[sample(10,10)]))
df <- as.data.frame(df)
names(df) <- letters[1:10]
# how does it look like now?
head(df, 10)
# a b c d e f g h i j
# 1 2 3 7 9 10 1 4 8 5 6
# 2 4 2 6 3 8 10 9 1 5 7
# 3 4 2 6 8 5 1 10 7 3 9
# 4 6 8 5 4 2 1 10 9 7 3
# 5 11 2 7 6 8 1 9 4 5 3
# 6 9 6 3 11 4 2 8 7 5 1
# 7 5 2 3 11 1 8 6 9 7 4
# 8 3 9 7 1 2 5 4 8 11 6
# 9 6 2 8 3 4 1 11 5 9 7
# 10 4 6 3 9 7 2 1 5 11 8
# now lets shuffle rows to make df more plausible
df <- df[sample(nrow(df), nrow(df)),]
Ora quando data.frame è pronto, possiamo testare diversi algoritmi.
system.time(indx <- !duplicated(t(apply(df, 1, sort))))
# user system elapsed
# 119.75 0.06 120.03
# doesn't impress, frankly speaking
library(sets)
system.time(indx <- !duplicated(apply(df, 1, as.set)))
# user system elapsed
# 91.60 0.00 91.89
# better, but we want faster! =)
# now lets check out the method with prime numbers
primenums <- Primes(100)[1:20]
# [1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71
system.time({
dfmult <- apply(as.matrix(df), 1, function(z) prod(primenums[z]))
my_indx <- !duplicated(dfmult) })
# user system elapsed
# 6.44 0.16 6.61
# not bad, isn't it? but lets compare results
identical(indx, my_indx)
# [1] TRUE
# So, if there is no difference, why wait more? ;)
C'è un presupposto importante qui - usiamo as.matrix(df)
, ma cosa succede se non ci sono solo le variabili numeriche nel nostro data.frame
? Una soluzione più unificati sarà il seguente:
system.time({
dfmult <- apply(
apply(df, 2, function(colmn) as.integer(factor(colmn,
levels = unique(c(as.matrix(df)))))),
1, function(z) prod(primenums[z]))
my_indx <- !duplicated(dfmult) })
# user system elapsed
# 27.48 0.34 27.84
# is distinctly slower but still much faster then previous methods
E che dire se abbiamo moltissimo colonne o molto molto diverse variabili? In questo caso invece di prod()
possiamo usare sum(log())
(che viene calcolato probabilmente ancora più veloce per grandi numeri). Guarda questo.
pr <- Primes(5e7)
length(pr)
# [1] 3001134
system.time(N <- sum(log(pr)))
# user system elapsed
# 0.12 0.00 0.13
N
# [1] 49993718
E 'difficile immaginare df
con 3 milioni di colonne, ma qui è ok. In questo modo possiamo trasportare df
di dimensioni incredibilmente grandi con il numero di colonne che la nostra RAM può contenere.
Questo è bello, anche se non è sicuro se si sta un po 'ingannando facendo 'as.matrix (df)' qui. –
+ 1 Molto veloce e mi piace l'idea di utilizzare la fattorizzazione primaria, ma ci sono due limitazioni a questo metodo: 1) Se ci sono un gran numero di colonne che prendono il prodotto di numeri primi non funzionerà (ad esempio 'prod (Primes (200)) 'equals' prod (Primes (201)) ') e 2) Non funzionerà se il frame di dati contiene un gran numero di elementi distinti (perché è necessario generare un primo per ciascuno, che può essere ingombrante e anche perché il prodotto non sarà distinguibile dal computer come nel punto precedente) – konvas
Per mantenere la calma e non rimanere imbroglioni invece di 'as.matrix' possiamo fare' apply (df, 2, function (colmn) as.integer (factor (colmn, levels = unique (c (as.matrix (df)))))) '. Sarà più lento ma non molto, darò i tempi e la risposta di aggiornamento domani, perché ora sono lontano dal PC. E sono d'accordo, l'uso di numeri primi ha delle limitazioni, ma forse puoi provare diversi pacchetti r che permettono di lavorare con numeri molto grandi? – inscaven