2013-01-10 10 views
5

Ho una domanda sulla rimozione di duplicati in Python. Ho letto un sacco di post ma non sono ancora riuscito a risolverlo. Ho il seguente file CSV:Python Duplicate Removal

EDIT

ingresso:

ID, Source, 1.A, 1.B, 1.C, 1.D 
1, ESPN, 5,7,,,M 
1, NY Times,,10,12,W 
1, ESPN, 10,,Q,,M 

uscita dovrebbe essere:

ID, Source, 1.A, 1.B, 1.C, 1.D, duplicate_flag 
1, ESPN, 5,7,,,M, duplicate 
1, NY Times,,10,12,W, duplicate 
1, ESPN, 10,,Q,,M, duplicate 
1, NY Times, 5 (or 10 doesn't matter which one),7, 10, 12, W, not_duplicate 

In parole, se l'ID è la lo stesso, prendere i valori dalla riga con la fonte "NY Times", se la r ow con "NY Times" ha un valore vuoto e la riga duplicata dall'origine "ESPN" ha un valore per quella cella, prende il valore dalla riga con la fonte "ESPN". Per l'output, contrassegna le due righe originali come duplicati e crea una terza riga.

Per chiarire un po 'più lontano, da quando ho bisogno di eseguire questo script su molti file csv diversi con diverse intestazioni di colonna, non riesco a fare qualcosa di simile:

def main(): 
     with open(input_csv, "rb") as infile: 
      input_fields = ("ID", "Source", "1.A", "1.B", "1.C", "1.D") 
      reader = csv.DictReader(infile, fieldnames = input_fields) 
      with open(output_csv, "wb") as outfile: 
       output_fields = ("ID", "Source", "1.A", "1.B", "1.C", "1.D", "d_flag") 
       writer = csv.DictWriter(outfile, fieldnames = output_fields) 
       writer.writerow(dict((h,h) for h in output_fields)) 
       next(reader) 
       first_row = next(reader) 
       for next_row in reader: 
        #stuff 

Perché voglio il programma di girare su le prime due colonne indipendentemente dalle altre colonne presenti nella tabella. In altre parole, "ID" e "Sorgente" saranno in ogni file di input, ma il resto delle colonne cambierà a seconda del file.

Apprezzerei molto qualsiasi aiuto che puoi fornire! Cordiali saluti, "Fonte" può essere solo: NY Times, ESPN, o Wall Street Journal e l'ordine di priorità per i duplicati è: prendere NY Times se disponibile, altrimenti prendere ESPN, altrimenti prendere Wall Street Journal. Questo vale per ogni file di input.

risposta

2

Il codice seguente legge tutti i record in un grande dizionario le cui chiavi sono i loro identificatori ei cui valori sono dizionari che mappano i nomi di origine a intere righe di dati. Quindi itera attraverso il dizionario e ti dà l'output richiesto.

import csv 

header = None 
idfld = None 
sourcefld = None 

record_table = {} 

with open('input.csv', 'rb') as csvfile: 
    reader = csv.reader(csvfile) 
    for row in reader: 
     row = [x.strip() for x in row] 

     if header is None: 
      header = row 
      for i, fld in enumerate(header): 
       if fld == 'ID': 
        idfld = i 
       elif fld == 'Source': 
        sourcefld = i 
      continue 

     key = row[idfld] 
     sourcename = row[sourcefld] 

     if key not in record_table: 
      record_table[key] = {sourcename: row, "all_rows": [row]} 
     else: 
      if sourcename in record_table[key]: 
       cur_row = record_table[key][sourcename] 
       for i, fld in enumerate(row): 
        if cur_row[i] == '': 
         record_table[key][sourcename][i] = fld 
      else: 
       record_table[key][sourcename] = row 
      record_table[key]["all_rows"].append(row) 

print ', '.join(header) + ', duplicate_flag' 

for recordid in record_table: 
    rowdict = record_table[recordid] 

    final_row = [''] * len(header) 

    rowcount = len(rowdict) 

    for sourcetype in ['NY Times', 'ESPN', 'Wall Street Journal']: 
     if sourcetype in rowdict: 
      row = rowdict[sourcetype] 
      for i, fld in enumerate(row): 
       if final_row[i] != '': 
        continue 
       if fld != '': 
        final_row[i] = fld 

    if rowcount > 1: 
     for row in rowdict["all_rows"]: 
      print ', '.join(row) + ', duplicate' 

    print ', '.join(final_row) + ', not_duplicate' 
+0

Grazie per l'ottima risposta. Funziona molto bene sui dati. Scusate per il ritardo nel rispondere, volevo lavorare attraverso il codice me stesso per essere sicuro di aver veramente capito perché funziona. Mi sono imbattuto in un problema: se una determinata riga ha gli stessi valori "ID" e "Sorgente", lo script corrente prenderà solo l'ultima riga che si verifica con quella particolare combinazione "ID" e "Sorgente". C'è un modo per modificare il codice in modo tale che tutte le righe originali vengano copiate nell'output (con il tag "duplicato" se applicabile) E la riga "not_duplicate" viene compilata per evitare il problema menzionato in questo commento? – user7186

+0

Aggiungerò un esempio di punto dati al post sopra come una modifica in modo da poterlo vedere più facilmente. Grazie mille ancora! Questo è veramente utile e ho imparato una buona quantità a riscriverlo. – user7186

+0

Nel nuovo esempio sopra, ci sono due righe con "ID" = 1 e "SOURCE" = ESPN. Se non c'è alcun valore per una data colonna nella riga "NY Times" e c'è un valore in entrambe le colonne delle righe "ESPN", non importa quale riga ESPN prendiamo da, solo che siamo coerenti tra File. Tuttavia, se la riga "NY Times" non ha un valore per una colonna particolare e una riga "ESPN" (ESPN A) non ha un valore per quella colonna, prendere dall'altra riga "ESPN" (ESPN B) . E, se ESPN A ha un valore ma ESPN B no, e la riga "NY Times" no, prendi da ESPN A. – user7186