2012-02-02 16 views
10

So che ci deve essere una risposta facile a questo, ma in qualche modo io non riesco a trovarlo ...Attuazione della interrogazione orizzonte o di frontiera efficiente

Ho un frame di dati con 2 colonne numeriche. Vorrei rimuovere da esso, le righe, che hanno la proprietà, che esiste almeno un'altra riga nel frame di dati, con entrambi i valori di colonna più grandi di quelli in questa riga.

Quindi, se ho

Col1 Col2 
1  2 3 
2  4 7 
3  5 6 

Vorrei rimuovere la prima fila, perché il secondo compie la proprietà e mantenere solo le righe 2 e 3.

grazie mille!

+0

Non riesco a impostare la modifica perché sono solo spazi, ma il tuo tavolo potrebbe benissimo usare il formato del codice: aggiungi 4 spazi in più prima di ogni riga, e uscirà con la stessa formattazione che hai usato, e è più leggibile. – Jeff

+0

Grazie Jeff, ho cercato di capire come farlo ma ho fallito miseramente. – user1184143

risposta

21

Questo problema è chiamato "query dell'orizzonte" dagli amministratori di database (possono avere altri algoritmi) e una "frontiera efficiente" da parte degli economisti. Tracciare i dati può chiarire cosa stiamo cercando.

n <- 40 
d <- data.frame(
    x = rnorm(n), 
    y = rnorm(n) 
) 
# We want the "extreme" points in the following plot 
par(mar=c(1,1,1,1)) 
plot(d, axes=FALSE, xlab="", ylab="") 
for(i in 1:n) { 
    polygon(c(-10,d$x[i],d$x[i],-10), c(-10,-10,d$y[i],d$y[i]), 
    col=rgb(.9,.9,.9,.2)) 
} 

L'algoritmo è il seguente: ordinare i punti lungo la prima coordinata, mantenere ogni osservazione meno che non sia peggiore del precedente trattenuto uno.

d <- d[ order(d$x, decreasing=TRUE), ] 
result <- d[1,] 
for(i in seq_len(nrow(d))[-1]) { 
    if(d$y[i] > result$y[nrow(result)]) { 
    result <- rbind(result, d[i,]) # inefficient 
    } 
} 
points(result, cex=3, pch=15) 

Skyline

+0

Sì, è esattamente quello che volevo fare, ma pensavo che farlo in modo "C" non fosse appropriato, grazie mille! – user1184143

+0

Bella trama e approfondimenti. –

+0

+1 e grazie per l'ottima risposta. Apprezzo in particolare la tua connessione alla query dell'orizzonte e poi includo una trama per illustrare il motivo per cui ha quel nome! Ho aggiunto una risposta qui sotto, ispirata alla tua che sostituisce quel ciclo 'for()' con un costrutto vectorized più R-ish. –

0
d <- matrix(c(2, 3, 4, 7, 5, 6), nrow=3, byrow=TRUE) 
d2 <- sapply(d[, 1], function(x) x < d[, 1]) & 
     sapply(d[, 2], function(x) x < d[, 2]) 
d2 <- apply(d2, 2, any) 
result <- d[!d2, ] 
+0

Hmm, non lo capisco completamente, ma la mia sensazione è che non guarda ogni colonna separatamente. Ad esempio se si fa> d <- matrice (c (2, 3, 3, 7, 5, 6, 4, 8), nrow = 4, byrow = TRUE) la riga (3, 7) dovrebbe anche essere rimossa perché (4, 8) è più grande in entrambe le colonne. Grazie per aver risposto. – user1184143

+0

Giusto, ho interpretato erroneamente i tuoi requisiti. L'ho letto per indicare "se ciascuno dei numeri nella riga B è più grande di ciascuno dei numeri nella riga A, esclude la riga A". Poiché 4 e 8 non sono ciascuno più grande di 3 e 7 (cioè 4 non è più grande di 7), il criterio non è stato raggiunto e la riga non è stata esclusa. Ora capisco che ti interessa sapere se i valori nella riga B sono maggiori dei valori nelle corrispondenti colonne della riga A. Ho modificato la risposta per correggerla (credo). – jbaums

+0

(Dovrebbe ora escludere quelle righe in cui esiste un'altra riga con valori maggiori in ciascuna colonna. Credo che la soluzione di Vincent escluderà le righe in cui esiste un'altra riga con valori * o uguali * maggiori in ogni colonna, anche se potrebbe essere effettivamente ciò che inteso scrivere nella tua domanda ..?) – jbaums

2

In una sola riga:

d <- matrix(c(2, 3, 4, 7, 5, 6), nrow=3, byrow=TRUE) 
d[!apply(d,1,max)<max(apply(d,1,min)),] 

    [,1] [,2] 
[1,] 4 7 
[2,] 5 6 

Edit: Alla luce della vostra precisione in risposta jbaums', ecco come fare per verificare la presenza di entrambe le colonne separatamente.

d <- matrix(c(2, 3, 3, 7, 5, 6, 4, 8), nrow=4, byrow=TRUE) 
d[apply(d,1,min)>min(apply(d,1,max)) ,] 

    [,1] [,2] 
[1,] 5 6 
[2,] 4 8 
+0

Tuttavia, devo trattare ogni colonna in modo indipendente, ad esempio se ho d <- matrice (c (1, 8, 7, 2), nrow = 2, byrow = TRUE), entrambe le righe devono essere conservate perché la prima riga ha il valore più alto nella seconda colonna e la seconda riga ha il valore più alto nella prima colonna, quindi non ci sono altre righe con un valore superiore in ** entrambe ** colonne. – user1184143

5

Ecco una soluzione sqldf dove DF è la cornice del trattamento dei dati:

library(sqldf) 
sqldf("select * from DF a 
where not exists (
    select * from DF b 
    where b.Col1 >= a.Col1 and b.Col2 > a.Col2 
     or b.Col1 > a.Col1 and b.Col2 >= a.Col2 
)" 
) 
9

Edit (2015/03/02): Per una soluzione più efficiente, si prega di consultare Patrick Roocks' rPref, un pacchetto per "Database Preferences e Skyline Computation", (anch'esso collegato nella sua risposta di seguito). Per dimostrare che trova qui la stessa soluzione del mio codice, ho aggiunto un esempio che lo usa alla mia risposta originale qui.


riffing off di risposta illuminante di Vincent Zoonekynd, ecco un algoritmo che è pienamente vettorializzare, e probabilmente più efficiente:

set.seed(100) 
d <- data.frame(x = rnorm(100), y = rnorm(100)) 

D <- d[order(d$x, d$y, decreasing=TRUE), ] 
res <- D[which(!duplicated(cummax(D$y))), ] 
#    x   y 
# 64 2.5819589 0.7946803 
# 20 2.3102968 1.6151907 
# 95 -0.5302965 1.8952759 
# 80 -2.0744048 2.1686003 


# And then, if you would prefer the rows to be in 
# their original order, just do: 
d[sort(as.numeric(rownames(res))), ] 
#   x   y 
# 20 2.3102968 1.6151907 
# 64 2.5819589 0.7946803 
# 80 -2.0744048 2.1686003 
# 95 -0.5302965 1.8952759 

Oppure, utilizzando il RPREF pacchetto:

library(rPref) 
psel(d, high(x) | high(y)) 
#    x   y 
# 20 2.3102968 1.6151907 
# 64 2.5819589 0.7946803 
# 80 -2.0744048 2.1686003 
# 95 -0.5302965 1.8952759 
+0

Molto elegante! Grazie. – user1184143

4

Questa domanda è piuttosto vecchia, ma nel frattempo c'è una nuova soluzione. Spero che sia giusto fare un po 'di auto-promozione qui: ho sviluppato un pacchetto rPref che esegue un efficiente calcolo Skyline grazie agli algoritmi C++.Con il pacchetto RPREF installato query dalla questione può essere fatta tramite (supponendo che df è il nome del set di dati):

library(rPref) 
psel(df, high(Col1) | high(Col2)) 

Questo rimuove solo le tuple, dove un altro tupla migliori in entrambe le dimensioni.

Se si richiede che l'altra tupla sia strettamente migliore in una sola dimensione (e migliore o uguale nell'altra dimensione), utilizzare invece high(Col1) * high(Col2).

+0

Hah! Appena cliccato sul tuo profilo, ho visto il link a 'rPref', e sono venuto subito qui per prendere nota che ora c'è una soluzione migliore. Aggiungerò una nota alla mia risposta che punta la gente qui, perché altrimenti la tua risposta è praticamente persa tra le erbacce! –

+0

Questo pacchetto riguarda le frontiere efficienti che hanno più di due variabili? Diciamo che volevamo includere n-variabili –

+0

Sì, lo fa. Non c'è limite principale nel numero di variabili/dimensioni, 'alto (x1) | alto (x2) | ... | alto (xn) 'è anche possibile, ma i costi di calcolo (e quindi il tempo di esecuzione) cresceranno con il numero di dimensioni. –

Problemi correlati