2016-06-10 50 views
7

Ho due frame di dati, "dati" e "colonne sonore", e desidera unire loro sulla colonna "id":Selezionare solo la prima riga quando si uniscono i frame di dati con più partite

data = data.frame(id = c(1,2,3,4,5), 
        state = c("KS","MN","AL","FL","CA")) 
scores = data.frame(id = c(1,1,1,2,2,3,3,3), 
        score = c(66,75,78,86,85,76,75,90)) 
merge(data, scores, by = "id")     
semi_join(data, scores, by = "id")     

Nel i "punteggi" dati, ci sono "ID" con più osservazioni, in cui ogni corrispondenza ottiene una riga dopo il join. Vedi ?merge:

Se c'è più di una corrispondenza, tutte le corrispondenze possibili contribuiscono con una riga ciascuna.

Tuttavia, voglio tenere solo la riga corrispondente al primo partita dalla tabella scores.

Un semi join sarebbe stato bello, ma non sono in grado di selezionare il punteggio dalla tabella giusta.

Qualche suggerimento?

risposta

10

Uso data.table insieme mult = "first" e nomatch = 0L:

require(data.table) 
setDT(scores); setDT(data) # convert to data.tables by reference 

scores[data, mult = "first", on = "id", nomatch=0L] 
# id score state 
# 1: 1 66 KS 
# 2: 2 86 MN 
# 3: 3 76 AL 

Per ogni giro data 'id colonna s, le righe corrispondenti nella scores' id colonna si trovano, e la prima uno solo è mantenuto (perché mult = "first"). Se non ci sono corrispondenze, vengono rimosse (a causa di nomatch = 0L).

+0

Che ne dici di usare un 'score' key.table.table:' setDT (data); punteggi <- data.table (punteggi, chiave = "id"); unique (scores) [data, nomatch = 0L] ' – mtoto

+3

Funzionerebbe bene, ma è solo molto inefficiente. 'data.table()' produce una copia profonda. L'argomento chiave dovrebbe riordinare l'intero dato.tabella (inefficiente a meno che non si possa fare caso per il riutilizzo e probabilmente indesiderabile). 'unique()' genera dati intermedi non necessari. E 'on =' consente anche una sintassi pulita per solo guardarlo e capire cosa è la colonna di join (anche se può essere utilizzato anche su dati key.tables). Raccomando di leggere la vignetta [indici secondari] (https://github.com/Rdatatable/data.table/wiki/Getting-started). – Arun

4

Qui è un metodo di base R utilizzando aggregate e head:

merge(data, aggregate(score ~ id, data=scores, head, 1), by="id") 

La funzione aggregate rompe il colonne dataframe da id, allora head viene applicato per ottenere la prima osservazione da ciascun id. Poiché aggregate restituisce un data.frame, questo viene direttamente unito ai dati data.frame.


Probabilmente più efficiente è quello sottoinsieme punteggi data.frame usando duplicated che ottenere lo stesso risultato come aggregate, ma ridurrà il carico computazionale.

merge(data, scores[!duplicated(scores$id),], by="id") 
+0

Questa soluzione è buona, ma è inefficiente. Vorrei usare dplyr :: distinct per sostituire l'aggregato. –

+0

Grazie @ huanfa-chen. Il tuo suggerimento mi ha dato l'idea di usare 'duplicato' al posto di 'aggregato + testa' che sarà sicuramente più efficiente. – lmo

2

Ecco un altro metodo che utilizza dplyr :: distinct. È utile se vuoi mantenere tutte le righe da "dati" anche se non c'è corrispondenza.

data = data.frame(id=c(1,2,3,4,5), 
        state=c("KS","MN","AL","FL","CA")) 
scores = data.frame(id=c(1,1,1,2,2,3,3,3), 
        score=c(66,75,78,86,85,76,75,90)) 
data %>% dplyr::left_join(dplyr::distinct(scores, id, .keep_all = T)) 
# Joining, by = "id" 
# id state score 
# 1 1 KS 66 
# 2 2 MN 86 
# 3 3 AL 76 
# 4 4 FL NA 
# 5 5 CA NA 

Inoltre, se si desidera sostituire il AN nel nuovo data.frame, provare la funzione tidyr :: replace_na(). Esempio:

data %>% dplyr::left_join(dplyr::distinct(scores, id, .keep_all = T)) %>% tidyr::replace_na(replace = list("score"=0L)) 
# Joining, by = "id" 
# id state score 
# 1 1 KS 66 
# 2 2 MN 86 
# 3 3 AL 76 
# 4 4 FL  0 
# 5 5 CA  0