Jak mogę filtrować linie podczas ładowania w funkcji read_csv Pandas?

100

Jak mogę filtrować, które wiersze pliku CSV mają zostać załadowane do pamięci za pomocą pand? Wydaje się, że jest to opcja, którą należy znaleźć read_csv. Czy coś mi brakuje?

Przykład: mamy plik CSV z kolumną znacznika czasu i chcielibyśmy załadować tylko wiersze, które mają znacznik czasu większy niż dana stała.

benjaminwilson
źródło

Odpowiedzi:

173

Nie ma opcji filtrowania wierszy przed załadowaniem pliku CSV do obiektu pandy.

Możesz załadować plik, a następnie przefiltrować za pomocą df[df['field'] > constant], lub jeśli masz bardzo duży plik i martwisz się, że skończy się pamięć, użyj iteratora i zastosuj filtr podczas łączenia fragmentów pliku, np .:

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

Możesz zmieniać, chunksizeaby dopasować do dostępnej pamięci. Więcej informacji znajdziesz tutaj .

Matti John
źródło
dla chunk['filed']>constantMogę kanapka go między 2 wartości stałych? Np .: stała1> chunk ['pole']> stała2. Czy mogę użyć „w zasięgu”?
weefwefwqg3
1
Spróbuj:chunk[(chunk['field'] > constant2)&(chunk['field']<constant1)]
Johannes Wachs
Czy to brakuje .loc? chunk.loc[chunk['field'] > constant]
Vincent
1
Możesz używać masek logicznych z lub bez .loc. Wydaje mi się, że nie .locistniało w 2012 roku, ale wydaje mi się, że obecnie używanie .locjest nieco bardziej jednoznaczne.
Matti John
10

Nie znalazłem prostego sposobu na zrobienie tego w kontekście read_csv. Jednak read_csvzwraca DataFrame, którą można filtrować, wybierając wiersze według wektora boolowskiego df[bool_vec]:

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

Oznacza to wybranie wszystkich wierszy w df (przy założeniu, że df jest dowolną ramką DataFrame, taką jak wynik read_csvwywołania, która przynajmniej zawiera kolumnę z datą i godziną timestamp), dla których wartości w timestampkolumnie są większe niż wartość czasu docelowego. Podobne pytanie .

Gryf
źródło
1
Nie jestem tego pewien, ale mam wrażenie, że byłoby to niezwykle obciążające zużycie pamięci.
Nathan
3

Jeśli filtrowany zakres jest ciągły (jak to zwykle bywa w przypadku filtrów czasu (stempla)), najszybszym rozwiązaniem jest zakodowanie zakresu wierszy na stałe. Po prostu połącz skiprows=range(1, start_row)z nrows=end_rowparametrami. Następnie import zajmie kilka sekund, podczas gdy zaakceptowane rozwiązanie zajmie minuty. Kilka eksperymentów z początkiem start_rownie jest ogromnym kosztem, biorąc pod uwagę oszczędność czasu importu. Zauważ, że zachowaliśmy wiersz nagłówka za pomocą range(1,..).

mirekphd
źródło
0

Alternatywą dla zaakceptowanej odpowiedzi jest zastosowanie read_csv () do StringIO, uzyskanego przez filtrowanie pliku wejściowego.

with open(<file>) as f:
    text = "\n".join([line for line in f if <condition>])

df = pd.read_csv(StringIO(text))

To rozwiązanie jest często szybsze niż zaakceptowana odpowiedź, gdy warunek filtrowania zachowuje tylko niewielką część linii

M. Page
źródło
-3

Jeśli używasz Linuksa, możesz użyć 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
źródło
1
Używanie grep jest bardzo złym wyborem z kilku powodów. 1) jest powolny 2) nie jest przenośny 3) to nie jest pandy ani python (możesz używać wyrażeń regularnych bezpośrednio w pythonie), dlatego obniżyłem twoją odpowiedź
Ahmed Masud
Twoje rozwiązanie nie działa na wszystkich platformach, a także zawiera Grep. To jest powód negatywnego głosu.
Roman Orac
-3

Możesz określić nrowsparametr.

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

Ten kod działa dobrze w wersji 0.20.3.

user1083290
źródło
1
OP pyta, jak filtrować, a nie ograniczać liczbę czytanych wierszy. Dlatego zlekceważyłem twoją odpowiedź.
Roman Orac