2009-05-05 10 views
8

Así que tienen dos archivos .csv, donde la primera línea en el archivo 1 es:Combinar 2 csv por columna común

MPID,Title,Description,Model,Category ID,Category Description,Subcategory ID,Subcategory Description,Manufacturer ID,Manufacturer Description,URL,Manufacturer (Brand) URL,Image URL,AR Price,Price,Ship Price,Stock,Condition 

La primera línea del archivo de 2:

Regular Price,Sale Price,Manufacturer Name,Model Number,Retailer Category,Buy URL,Product Name,Availability,Shipping Cost,Condition,MPID,Image URL,UPC,Description 

y luego el resto de cada archivo está lleno de información.

Como puede ver, ambos archivos tienen un campo común llamado MPID (archivo 1: col 1, archivo 2: col 9, donde el primer col es col 1).

Me gustaría crear un nuevo archivo que combine estos dos archivos mirando esta columna (como en: si hay un MPID que está en ambos archivos, entonces en el nuevo archivo aparecerá este MPID con su fila desde el archivo 1 y su fila desde el archivo 2). SI un MPID aparece solo en un archivo, también debe ir a este archivo combinado.

Los archivos no están ordenados de ninguna manera.

¿Cómo hago esto en una máquina Debian con un script de shell o python?

Gracias.

EDITAR: Ambos archivos no tienen comas que no sean los que separan los campos.

Respuesta

0

Parece que está tratando de hacer en un script de shell, que comúnmente se hace con el servidor SQL. ¿Es posible usar SQL para esa tarea? Por ejemplo, puede importar ambos archivos a mysql, luego crear una unión y luego exportarla a CSV.

1

Tendrá que mirar el comando join en el shell. También deberá ordenar los datos y probablemente perderá las primeras líneas. Todo el proceso se perderá si alguno de los datos contiene comas. O tendrá que procesar los datos con un proceso sensible a CSV que introduce un separador de campo diferente (tal vez control-A) que puede usar para dividir los campos de forma inequívoca.

La alternativa, usando Python, lee los dos archivos en un par de diccionarios (codificados en la (s) columna (s) común (s) y luego utiliza un bucle para cubrir todos los elementos en el más pequeño de los dos diccionarios, buscando coincidencia valores en el otro. (Este es el proceso de consulta de bucle anidado básico.)

+1

join funciona grandes; pero los archivos de entrada deben ordenarse en la clave. tampoco puede leer archivos csv arbitrarios. específicamente, una coma dentro de un campo citado cambiaría todos los campos para ese registro – Javier

+0

@Javier: estuvo de acuerdo, razón por la cual actualicé mi respuesta para decir tanto, incluso sin ver su comentario (que probablemente estaba en preparación al mismo tiempo que estaba edición). –

13
sort -t , -k index1 file1 > sorted1 
sort -t , -k index2 file2 > sorted2 
join -t , -1 index1 -2 index2 -a 1 -a 2 sorted1 sorted2 
+6

¡tenga cuidado con las comillas cotizadas! ni ordenar ni unirse obedece a ninguna cita – Javier

9

Este es el clásico problema de "unión relacional".

Tiene varios algoritmos.

  • Nested Loops. Lees de un archivo para elegir un registro "maestro". Lees todo el otro archivo ubicando todos los registros de "detalles" que coinciden con el maestro. Esta es una mala idea.

  • Sort-Merge. Clasifica cada archivo en una copia temporal basada en la clave común. A continuación, combina ambos archivos leyendo el maestro y luego leyendo todas las filas coincidentes del detalle y escribiendo los registros fusionados.

  • Búsqueda. Usted lee uno de los archivos completamente en un diccionario en memoria, indexado por el campo clave. Esto puede ser complicado para el archivo de detalles, donde tendrá varios hijos por clave. Luego, lee el otro archivo y busca los registros coincidentes en el diccionario.

De estos, sort-merge suele ser el más rápido. Esto se hace completamente usando el comando unix sort.

Implementación de búsqueda

import csv 
import collections 

index = collections.defaultdict(list) 

file1= open("someFile", "rb") 
rdr= csv.DictReader(file1) 
for row in rdr: 
    index[row['MPID']].append(row) 
file1.close() 

file2= open("anotherFile", "rb") 
rdr= csv.DictReader(file2) 
for row in rdr: 
    print row, index[row['MPID']] 
file2.close() 
+0

Desde Python 2.2, creo, DictReader no implementa __getitem__. Esto fue hecho por razones de velocidad. Entonces el código: index[rdr['MPID']].append(row) fallará con un AttributeError: la instancia de DictReader no tiene el atributo '__getitem__' – uman

+0

@uman: Los resultados de un DictReader son un objeto 'dict' de primera clase. Con todos los métodos 'dict' intactos. –

+0

@ S.Lott: quizás su código era incorrecto. Estoy MUY oxidado con Python, pero probar el ejemplo de MPID con tu código da como resultado AttributeError. Ver: http://pastebin.com/VJgSfA3u P.S. ¿Alguien sabe cómo incrustar el formato en los comentarios? – uman

0

Se puede echar un vistazo a mi proyecto de software libre CSVfix, que es un editor de flujo para la manipulación de archivos CSV. Admite combinaciones, entre otras características, y no requiere el uso de scripts.

0

Para fusionar varios archivos (incluso> 2) basados ​​en una o más columnas comunes, uno de los mejores y más eficientes enfoques en python sería usar "cervecería". Incluso podría especificar qué campos se deben considerar para la fusión y qué campos se deben guardar.

import brewery 
from brewery 
import ds 
import sys 

sources = [ 
    {"file": "grants_2008.csv", 
    "fields": ["receiver", "amount", "date"]}, 
    {"file": "grants_2009.csv", 
    "fields": ["id", "receiver", "amount", "contract_number", "date"]}, 
    {"file": "grants_2010.csv", 
    "fields": ["receiver", "subject", "requested_amount", "amount", "date"]} 
] 

Crear una lista de todos los campos y añadir el nombre de archivo para almacenar información sobre el origen de los datos a través records.Go definiciones de origen y recoger los campos:

for source in sources: 
    for field in source["fields"]: 
     if field not in all_fields: 

out = ds.CSVDataTarget("merged.csv") 
out.fields = brewery.FieldList(all_fields) 
out.initialize() 

for source in sources: 

    path = source["file"] 

# Initialize data source: skip reading of headers 
# use XLSDataSource for XLS files 
# We ignore the fields in the header, because we have set-up fields 
# previously. We need to skip the header row. 

    src = ds.CSVDataSource(path,read_header=False,skip_rows=1) 

    src.fields = ds.FieldList(source["fields"]) 

    src.initialize() 


    for record in src.records(): 

    # Add file reference into ouput - to know where the row comes from 
    record["file"] = path 

     out.append(record) 

# Close the source stream 

    src.finalize() 


cat merged.csv | brewery pipe pretty_printer 
Cuestiones relacionadas