2015-04-23 18 views
8

Ich bin sicher, dass es etwas einfacher ist, aber ich habe einen DatenrahmenFinding einzigartige Kombinationen unabhängig von Position

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

Und ich mag einen neuen Datenrahmen, die die einzigartigen Kombinationen von Werten in den Zeilen enthalten, unabhängig davon, welcher Spalte sie sind in. So im Fall oben I

a b c 
    1 2 3 
    3 1 4 

wollen würde ich versucht habe

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

aber es sieht (1, 2, 3) als einzigartig aus (2, 3, 1), was ich nicht will.

Antwort

4

Vielleicht so etwas wie

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

Als ein alternativer Ansatz, das Paket sets bietet einen schnellen Weg für Satz Gleichheit zu prüfen:

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

Wenn Ihr data.frame ziemlich groß ist, die Geschwindigkeit kann eine Frage für Sie sein. Sie können doppelte Sätze viel schneller mit der folgenden Idee finden.

Lassen Sie uns imaginären jeden möglichen Wert in Zeilen eine Primzahl zuweisen und Produkte für jede Zeile zählen. Zum Beispiel können wir für dfprimenums = c(2,3,5,7) akzeptieren und Produkte c(30,30,70) zählen. Dann entsprechen die Duplikate in diesem Produktvektor doppelten Datensätzen in unserem Datenrahmen. Da die Multiplikation viel schneller als jede Art von Sortierung berechnet wird, können Sie effizienter werden. Der Code folgt.

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,] 

Hier initialisieren wir Vektor primenums mit Hilfe der Funktion Primes von Paket numbers, aber Sie können in anderer Weise manuell tun.

Schauen Sie sich das Beispiel an. Hier zeige ich einen Vergleich der Effizienz.

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)),] 

Jetzt, wenn data.frame fertig ist, können wir verschiedene Algorithmen testen.

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? ;) 

Es gibt eine wichtige Voraussetzung hier - wir verwenden as.matrix(df), aber was, wenn es nicht nur numerische Variablen in unserem data.frame? Eine unificated Lösung wird wie folgt sein:

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 

Und was ist, wenn wir sehr viel Spalten oder sehr unterschiedliche Variablen? In diesem Fall können wir anstelle von prod()sum(log()) verwenden (was wahrscheinlich noch schneller für große Zahlen berechnet wird). Schau dir das an.

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 

Es ist schwer df mit 3 Millionen Spalten vorstellen, aber hier ist es in Ordnung. Auf diese Weise können wir df jeder unglaublich großen Größe mit so vielen Spalten tragen, wie unser RAM aufnehmen kann.

+0

Das ist ein netter, aber nicht sicher, ob Sie ein bisschen schummeln, indem Sie 'as.matrix (df)' hier tun. –

+1

+ 1 Sehr schnell und ich mag die Idee, Primfaktorzerlegung zu verwenden, aber es gibt zwei Einschränkungen bei dieser Methode: 1) Wenn es eine große Anzahl von Spalten gibt, wird das Produkt von Primzahlen nicht funktionieren (zB 'prod (Primes (200)) 'eques' prod (Primes (201)) ') und 2) Es wird nicht funktionieren, wenn der Datenrahmen eine große Anzahl von verschiedenen Elementen enthält (weil Sie für jeden einen Primzahl generieren müssen, was mühsam sein kann und auch weil das Produkt nicht vom Computer unterscheidbar sein wird wie im vorherigen Punkt) – konvas

+0

Um ruhig zu bleiben und nicht Betrüger anstelle von 'as.matrix' zu bleiben, können wir' apply (df, 2, function (colmn) as.integer (Faktor (colmn, levels = unique (c (as.matrix (df)))))). Es wird langsamer, aber nicht sehr viel, ich werde Zeit geben und die Antwort morgen aktualisieren, denn jetzt bin ich weg vom PC. Und ich stimme zu, die Verwendung von Primzahlen hat Einschränkungen, aber vielleicht können Sie verschiedene Pakete ausprobieren, die es erlauben, mit sehr großen Zahlen zu arbeiten? – inscaven

Verwandte Themen