2010-04-19 18 views
9

yo tengo un archivo CSV con un formato que se ve así:archivos CSV de lectura en donde numpy delimitador es ""

"FieldName1", "FieldName2", "fieldname3", "FieldName4"
"13/04/2010 14: 45: 07.008", "7.59484916392", "10", "6.552373"
"13/04/2010 14: 45: 22.010", "6.55478493312", "9", " 3.5378543"
...

en cuenta que hay dos caracteres de comillas al principio y al final de cada línea en t El archivo CSV y la cadena "," se utilizan para delimitar campos dentro de cada línea. La cantidad de campos en el archivo CSV puede variar de un archivo a otro.

cuando trato de leer esto en numpy a través de:
import numpy as np
data = np.genfromtxt(csvfile, dtype=None, delimiter=',', names=True)
todos los datos se lee en el que los valores de cadena, rodeado por comillas dobles. No es razonable, pero no sirve de mucho para mí, ya que luego tengo que volver y convertir cada columna a su correcta tipo

Cuando uso delimiter='","' lugar, todo funciona como me gustaría, excepto para la primera y última campos. Como el inicio de los caracteres de línea y final de línea es un carácter de comillas dobles, no se considera un delimitador válido para los campos primero y último, por lo que se leen como, por ejemplo, "04/13/2010 14:45:07.008 y 6.552373" - tenga en cuenta los caracteres de comillas dobles iniciales y finales respectivamente. Debido a estos caracteres redundantes, numpy supone que el primer y el último campo son ambos tipos de cadena; No quiero que sea el caso

¿Hay alguna manera de instruir numpy para leer en archivos formateados de esta manera como me gustaría, sin tener que volver atrás y "arreglar" la estructura de la matriz numpy después de la lectura inicial?

Respuesta

12

El problema básico es que NumPy no entiende el concepto de pelado de las comillas (mientras que el módulo csv sí). Cuando diga delimiter='","', le está diciendo a NumPy que el delimitador de columna es literalmente una coma entrecomillada, es decir, las comillas están alrededor de la coma, no del valor, por lo que se esperan las comillas adicionales que obtenga en la primera y la última columna.

En cuanto a los documentos de función, creo que tendrá que establecer el parámetro converters para despojar a las citas para usted (el valor por defecto no lo hace):

import re 
import numpy as np 

fieldFilter = re.compile(r'^"?([^"]*)"?$') 
def filterTheField(s): 
    m = fieldFilter.match(s.strip()) 
    if m: 
     return float(m.group(1)) 
    else: 
     return 0.0 # or whatever default 

#... 

# Yes, sorry, you have to know the number of columns, since the NumPy docs 
# don't say you can specify a default converter for all columns. 
convs = dict((col, filterTheField) for col in range(numColumns)) 
data = np.genfromtxt(csvfile, dtype=None, delimiter=',', names=True, 
    converters=convs) 

o abandonar np.genfromtxt() y vamos csv.csvreader le dan el archivo de contenidos de una fila a la vez, como listas de cadenas, a continuación, que acaba de recorrer los elementos y construir la matriz:

reader = csv.csvreader(csvfile) 
result = np.array([[float(col) for col in row] for row in reader]) 
# BTW, column headings are in reader.fieldnames at this point. 

EDIT: Bueno, lo que parece que el archivo no es todos los flotadores. En ese caso, se puede establecer convs según sea necesario en el caso genfromtxt, o crear un vector de funciones de conversión en el caso csv.csvreader:

reader = csv.csvreader(csvfile) 
converters = [datetime, float, int, float] 
result = np.array([[conv(col) for col, conv in zip(row, converters)] 
    for row in reader]) 
# BTW, column headings are in reader.fieldnames at this point. 

EDIT 2: Está bien, cuente columna de variables ... Su fuente de datos sólo quiere para hacer la vida difícil Afortunadamente, solo podemos usar magic ...

reader = csv.csvreader(csvfile) 
result = np.array([[magic(col) for col in row] for row in reader]) 

... donde magic() es sólo un nombre que me dieron la parte superior de la cabeza para una función. (! Psique)

En el peor, podría ser algo como:

def magic(s): 
    if '/' in s: 
     return datetime(s) 
    elif '.' in s: 
     return float(s) 
    else: 
     return int(s) 

Tal NumPy tiene una función que toma una cadena y devuelve un único elemento con el tipo correcto. numpy.fromstring() parece estar cerca, pero podría interpretar el espacio en sus marcas de tiempo como un separador de columnas.

P.S. Una desventaja con csvreader que veo es que no descarta los comentarios; los archivos reales csv no tienen comentarios.

+1

El método 'str.replace ('"', '') 'debe funcionar notablemente más rápido que la expresión regular si el archivo de entrada es grande (muchos MB o GB) y será correcto si puede asumir el' 'El carácter no aparecerá en el medio de un campo, solo en los extremos. – gotgenes

+0

Gracias Mike y gotgenes, pero debería haber mencionado que el archivo CSV tiene una cantidad variable de columnas. Probablemente podría usar el enfoque que describió al agregar un paso inicial para leer en el primer registro del archivo para determinar el número de columnas, y luego usarlo como entrada para los pasos posteriores, pero parece bastante torpe. ¿Hay una manera mejor? – monch1962

+0

Nota minúscula: no es necesario utilizar 're.compile()' porque el uso de 're.match()' directamente almacenará en caché la expresión regular compilada de todos modos. – blokeley