2011-08-19 10 views
14

Ho due frame di dati (x & y) dove gli ID sono student_name, father_name e mother_name. A causa di errori tipografici ("n" invece di "m", spazi bianchi casuali, ecc.), Ho circa il 60% dei valori che non si allineano, anche se posso guardare i dati e vedere che dovrebbero. C'è un modo per ridurre il livello di non corrispondenza in qualche modo in modo che il montaggio manuale sia almeno possibile? I dataframes hanno circa 700K di osservazioni.Fusione attraverso la corrispondenza fuzzy di variabili in R

R sarebbe meglio. Conosco un po 'di python e alcuni strumenti unix di base. Post scriptum Ho letto su agrep(), ma non capisco come possa funzionare sui dataset attuali, specialmente quando la partita ha più di una variabile.


aggiornamento (i dati per taglie scritto):

Here sono due telai ad esempio dati, sites_a e sites_b. Potrebbero essere abbinati alle colonne numeriche lat e lon e alla colonna sitename. Sarebbe utile sapere come ciò potrebbe essere fatto a) solo lat + lon, b) sitename o c) entrambi.

è possibile trovare il file test_sites.R che viene pubblicato come un elenco.

Idealmente la risposta finirebbe con

merge(sites_a, sites_b, by = **magic**) 
+0

Potete fornire un piccolo sottoinsieme di dati (o fornirci dati falsi)? –

+2

@ RomanLuštrik Anche se questa non era originariamente la mia domanda, ho avuto un problema simile, ho creato alcuni dati di esempio e offerto una taglia. –

+0

@David hai provato a unire (sites_a, sites_b, by = c ("lon", "lat")) '? Nel tuo caso, se vuoi unirmi per nome, dovrai investire più energia per far coincidere i nomi in entrambi i data.frames (buona fortuna, eh). –

risposta

6

La funzione agrep (parte della base R), che fa approssimativo stringa corrispondente usando l'Levenshtein edit distance è probabilmente da provare. Senza sapere come sono i tuoi dati, non posso davvero suggerire una soluzione funzionante. Ma questo è un suggerimento ... Registra le partite in una lista separata (se ci sono più partite ugualmente buone, anche queste sono registrate). Diciamo che il vostro data.frame si chiama df:

l <- vector('list',nrow(df)) 
matches <- list(mother = l,father = l) 
for(i in 1:nrow(df)){ 
    father_id <- with(df,which(student_name[i] == father_name)) 
    if(length(father_id) == 1){ 
    matches[['father']][[i]] <- father_id 
    } else { 
    old_father_id <- NULL 
    ## try to find the total                                 
    for(m in 10:1){ ## m is the maximum distance                            
     father_id <- with(df,agrep(student_name[i],father_name,max.dist = m)) 
     if(length(father_id) == 1 || m == 1){ 
     ## if we find a unique match or if we are in our last round, then stop                    
     matches[['father']][[i]] <- father_id 
     break 
     } else if(length(father_id) == 0 && length(old_father_id) > 0) { 
     ## if we can't do better than multiple matches, then record them anyway                    
     matches[['father']][[i]] <- old_father_id 
     break 
     } else if(length(father_id) == 0 && length(old_father_id) == 0) { 
     ## if the nearest match is more than 10 different from the current pattern, then stop                 
     break 
     } 
    } 
    } 
} 

Il codice per la mother_name sarebbe fondamentalmente la stessa. Potresti anche metterli insieme in un ciclo, ma questo esempio è solo a scopo illustrativo.

+0

Grazie, nullglob. Potresti spiegare la sintassi dei dataframes (x, y) e delle variabili (student_name, ecc.). – user702432

+0

Scusa @ user702432, non ho letto abbastanza attentamente la tua domanda. Tu e già trovato 'agrep'. Ho aggiunto un suggerimento su come utilizzarlo in combinazione con i tuoi dati.frame – nullglob

5

questo richiede un elenco di nomi di colonna comuni, corrispondenze in base agrep di tutte quelle colonne combinate, e poi se all.x o all.y uguale VERO che aggiunge i record non corrispondenti compilando colonne mancante con NA. A differenza di merge, i nomi delle colonne corrispondenti devono essere uguali in ogni frame di dati. La sfida sembrerebbe impostare correttamente le opzioni agrep per evitare corrispondenze spurie.

agrepMerge <- function(df1, df2, by, all.x = FALSE, all.y = FALSE, 
    ignore.case = FALSE, value = FALSE, max.distance = 0.1, useBytes = FALSE) { 

    df1$index <- apply(df1[,by, drop = FALSE], 1, paste, sep = "", collapse = "") 
    df2$index <- apply(df2[,by, drop = FALSE], 1, paste, sep = "", collapse = "") 

    matches <- lapply(seq_along(df1$index), function(i, ...) { 
     agrep(df1$index[i], df2$index, ignore.case = ignore.case, value = value, 
      max.distance = max.distance, useBytes = useBytes) 
    }) 

    df1_match <- rep(1:nrow(df1), sapply(matches, length)) 
    df2_match <- unlist(matches) 

    df1_hits <- df1[df1_match,] 
    df2_hits <- df2[df2_match,] 

    df1_miss <- df1[setdiff(seq_along(df1$index), df1_match),] 
    df2_miss <- df2[setdiff(seq_along(df2$index), df2_match),] 

    remove_cols <- colnames(df2_hits) %in% colnames(df1_hits) 

    df_out <- cbind(df1_hits, df2_hits[,!remove_cols]) 

    if(all.x) { 
     missing_cols <- setdiff(colnames(df_out), colnames(df1_miss)) 
     df1_miss[missing_cols] <- NA 
     df_out <- rbind(df_out, df1_miss) 
    } 
    if(all.x) { 
     missing_cols <- setdiff(colnames(df_out), colnames(df2_miss)) 
     df2_miss[missing_cols] <- NA 
     df_out <- rbind(df_out, df2_miss) 
    } 
    df_out[,setdiff(colnames(df_out), "index")] 
} 
+0

Inseriscilo in un Gist qui: https://gist.github.com/enricoferrero/0e41549d437aeda4d5f2f95116316c00 – enricoferrero