2015-04-23 12 views
8

Sono sicuro che è qualcosa di semplice, ma ho un frame di datiTrovare combinazioni uniche a prescindere dalla posizione

 df <- data.frame(a = c(1, 2, 3), 
         b = c(2, 3, 1), 
         c = c(3, 1, 4)) 

E voglio una nuova cornice di dati che contiene le combinazioni uniche di valori nelle righe, a prescindere dal quale colonna cui si trovano. Quindi, nel caso di cui sopra vorrei

a b c 
    1 2 3 
    3 1 4 

ho provato

unique(df[c('a', 'b', 'c')]) 

ma vede (1, 2, 3) come unico da (2, 3, 1), che non voglio.

risposta

4

Forse qualcosa di simile che

indx <- !duplicated(t(apply(df, 1, sort))) # finds non - duplicates in sorted rows 
df[indx, ] # selects only the non - duplicates according to that index 
# a b c 
# 1 1 2 3 
# 3 3 1 4 
1

Come approccio alternativo, il pacchetto sets fornisce un modo veloce di controllo per l'uguaglianza set:

library(sets) 
df.sets <- apply(df, 1, as.set) 
#[[1]] 
#{1, 2, 3} 
#[[2]] 
#{1, 2, 3} 
#[[3]] 
#{1, 3, 4} 
df[!duplicated(df.sets),] 
# a b c 
#1 1 2 3 
#3 3 1 4 
4

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.

+0

Questo è bello, anche se non è sicuro se si sta un po 'ingannando facendo 'as.matrix (df)' qui. –

+1

+ 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

+0

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

Problemi correlati