Como posso filtrar linhas ao carregar na função read_csv do Pandas?

94

Como posso filtrar quais linhas de um CSV devem ser carregadas na memória usando o pandas? Esta parece ser uma opção que devemos encontrar read_csv. Estou esquecendo de algo?

Exemplo: temos um CSV com uma coluna de carimbo de data / hora e gostaríamos de carregar apenas as linhas com um carimbo de data / hora maior que uma determinada constante.

benjaminwilson
fonte

Respostas:

163

Não há uma opção de filtrar as linhas antes que o arquivo CSV seja carregado em um objeto pandas.

Você pode carregar o arquivo e filtrar usando df[df['field'] > constant]ou, se tiver um arquivo muito grande e estiver preocupado com a falta de memória, use um iterador e aplique o filtro à medida que concatena pedaços de seu arquivo, por exemplo:

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])

Você pode variar o chunksizepara se adequar à sua memória disponível. Veja aqui para mais detalhes.

Matti John
fonte
pois chunk['filed']>constantposso imprensá-lo entre 2 valores constantes? Ex .: constant1> chunk ['field']> constant2. Ou posso usar 'dentro do alcance'?
weefwefwqg3
Experimente:chunk[(chunk['field'] > constant2)&(chunk['field']<constant1)]
Johannes Wachs
Está faltando um .loc? chunk.loc[chunk['field'] > constant]
Vincent
1
Você pode usar máscaras booleanas com ou sem .loc. Acho que não .locexistia em 2012, mas acho que hoje em dia o uso .locé um pouco mais explícito.
Matti John
8

Não encontrei uma maneira direta de fazer isso dentro do contexto de read_csv. No entanto, read_csvretorna um DataFrame, que pode ser filtrado selecionando linhas por vetor booleano df[bool_vec]:

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

Isso é selecionar todas as linhas em df (assumindo que df seja qualquer DataFrame, como o resultado de uma read_csvchamada, que contém pelo menos uma coluna datetime timestamp) para os quais os valores na timestampcoluna são maiores que o valor de targettime. Pergunta semelhante .

Griffin
fonte
1
Não tenho certeza sobre isso, mas tenho a sensação de que isso seria extremamente pesado no uso de memória.
Nathan de
2

Se o intervalo filtrado for contíguo (como geralmente é com os filtros de tempo (carimbo)), a solução mais rápida é codificar o intervalo de linhas. Simplesmente combine skiprows=range(1, start_row)com nrows=end_rowparâmetros. Em seguida, a importação leva segundos, enquanto a solução aceita levaria minutos. Alguns experimentos com o inicial start_rownão representam um custo enorme, dada a economia no tempo de importação. Observe que mantivemos a linha do cabeçalho usando range(1,..).

mirekphd
fonte
-3

Se você estiver no Linux, você pode 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
fonte
1
Usar grep é uma escolha muito ruim por vários motivos. 1) é lento 2) não é portátil 3) não é pandas ou python (você pode usar expressões regulares dentro de python), é por isso que votei contra sua resposta
Ahmed Masud
Sua solução não funciona em todas as plataformas e também inclui Grep. Este é o motivo do downvote.
Roman Orac
-3

Você pode especificar o nrowsparâmetro.

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

Este código funciona bem na versão 0.20.3.

user1083290
fonte
1
OP está perguntando como filtrar e não limitar o número de linhas lidas. É por isso que eu votei contra sua resposta.
Roman Orac