¿Cómo puedo filtrar líneas en carga en la función read_csv de Pandas?

95

¿Cómo puedo filtrar qué líneas de un CSV se cargarán en la memoria usando pandas? Esta parece una opción en la que uno debería encontrar read_csv. ¿Me estoy perdiendo de algo?

Ejemplo: tenemos un CSV con una columna de marca de tiempo y nos gustaría cargar solo las líneas que tienen una marca de tiempo mayor que una constante dada.

benjaminwilson
fuente

Respuestas:

165

No hay una opción para filtrar las filas antes de que el archivo CSV se cargue en un objeto pandas.

Puede cargar el archivo y luego filtrar usando df[df['field'] > constant], o si tiene un archivo muy grande y le preocupa que se agote la memoria, use un iterador y aplique el filtro a medida que concatena partes de su archivo, por ejemplo:

import pandas as pd
iter_csv = pd.read_csv('file.csv', iterator=True, chunksize=1000)
df = pd.concat([chunk[chunk['field'] > constant] for chunk in iter_csv])

Puede variar el chunksizepara adaptarse a su memoria disponible. Consulte aquí para obtener más detalles.

Matti John
fuente
para chunk['filed']>constantpuedo Sandwich lugar entre los 2 valores constantes? Por ejemplo: constante1> fragmento ['campo']> constante2. ¿O puedo usar 'dentro del rango'?
weefwefwqg3
1
Prueba:chunk[(chunk['field'] > constant2)&(chunk['field']<constant1)]
Johannes Wachs
¿Le falta un .loc? chunk.loc[chunk['field'] > constant]
Vincent
1
Puede utilizar máscaras booleanas con o sin .loc. No creo que .locexistiera en 2012, pero creo que en estos días el uso .loces un poco más explícito.
Matti John
8

No encontré una forma sencilla de hacerlo dentro del contexto de read_csv. Sin embargo, read_csvdevuelve un DataFrame, que se puede filtrar seleccionando filas por vector booleano df[bool_vec]:

filtered = df[(df['timestamp'] > targettime)]

Esto es seleccionar todas las filas en df (asumiendo que df es cualquier DataFrame, como el resultado de una read_csvllamada, que al menos contiene una columna de fecha y hora timestamp) para los cuales los valores en la timestampcolumna son mayores que el valor de targettime. Pregunta similar .

Grifo
fuente
1
No estoy seguro de esto, pero tengo la sensación de que sería extremadamente pesado para el uso de la memoria.
Nathan
2

Si el rango filtrado es contiguo (como suele ser con los filtros de tiempo (sello)), entonces la solución más rápida es codificar el rango de filas. Simplemente combine skiprows=range(1, start_row)con los nrows=end_rowparámetros. Luego, la importación toma segundos, mientras que la solución aceptada tomaría minutos. Unos pocos experimentos con el inicial start_rowno suponen un gran coste dado el ahorro en tiempos de importación. Observe que mantuvimos la fila de encabezado usando range(1,..).

mirekphd
fuente
-3

Si está en Linux, puede usar grep.

# to import either on Python2 or Python3
import pandas as pd
from time import time # not needed just for timing
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO


def zgrep_data(f, string):
    '''grep multiple items f is filepath, string is what you are filtering for'''

    grep = 'grep' # change to zgrep for gzipped files
    print('{} for {} from {}'.format(grep,string,f))
    start_time = time()
    if string == '':
        out = subprocess.check_output([grep, string, f])
        grep_data = StringIO(out)
        data = pd.read_csv(grep_data, sep=',', header=0)

    else:
        # read only the first row to get the columns. May need to change depending on 
        # how the data is stored
        columns = pd.read_csv(f, sep=',', nrows=1, header=None).values.tolist()[0]    

        out = subprocess.check_output([grep, string, f])
        grep_data = StringIO(out)

        data = pd.read_csv(grep_data, sep=',', names=columns, header=None)

    print('{} finished for {} - {} seconds'.format(grep,f,time()-start_time))
    return data
Christopher Bell
fuente
1
Usar grep es una mala elección por varias razones. 1) es lento 2) no es portátil 3) no es pandas o python (puede usar expresiones regulares dentro de python), por eso voté negativamente en su respuesta
Ahmed Masud
Su solución no funciona en todas las plataformas y también incluye Grep. Esta es la razón del voto negativo.
Roman Orac
-3

Puede especificar el nrowsparámetro.

import pandas as pd df = pd.read_csv('file.csv', nrows=100)

Este código funciona bien en la versión 0.20.3.

usuario1083290
fuente
1
OP pregunta cómo filtrar y no limitar el número de líneas leídas. Es por eso que voté en contra de tu respuesta.
Roman Orac