Comment puis-je filtrer les lignes lors du chargement dans la fonction Pandas read_csv?


95

Comment puis-je filtrer les lignes d'un CSV à charger en mémoire à l'aide de pandas? Cela semble être une option dans laquelle on devrait trouver read_csv. Est-ce que je manque quelque chose?

Exemple: nous avons un CSV avec une colonne d'horodatage et nous aimerions charger uniquement les lignes qui ont un horodatage supérieur à une constante donnée.

Réponses:


165

Il n'y a pas d'option pour filtrer les lignes avant que le fichier CSV ne soit chargé dans un objet pandas.

Vous pouvez soit charger le fichier puis filtrer en utilisant df[df['field'] > constant], ou si vous avez un fichier très volumineux et que vous craignez que la mémoire soit épuisée, utilisez un itérateur et appliquez le filtre lorsque vous concaténez des morceaux de votre fichier, par exemple:

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

Vous pouvez faire varier le chunksizeen fonction de votre mémoire disponible. Voir ici pour plus de détails.


car chunk['filed']>constantpuis-je le prendre en sandwich entre 2 valeurs constantes? Par exemple: constante1> chunk ['champ']> constante2. Ou puis-je utiliser «à portée»?
weefwefwqg3

1
Essayez:chunk[(chunk['field'] > constant2)&(chunk['field']<constant1)]
Johannes Wachs

Est-ce qu'il manque un .loc? chunk.loc[chunk['field'] > constant]
Vincent le

1
Vous pouvez utiliser des masques booléens avec ou sans .loc. Je ne pense pas qu'il .locexistait en 2012, mais je suppose que ces jours-ci, l'utilisation .locest un peu plus explicite.
Matti John le

8

Je n'ai pas trouvé de moyen simple de le faire dans le contexte de read_csv. Cependant, read_csvrenvoie un DataFrame, qui peut être filtré en sélectionnant des lignes par vecteur booléen df[bool_vec]:

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

Il s'agit de sélectionner toutes les lignes dans df (en supposant que df est un DataFrame, tel que le résultat d'un read_csvappel, qui contient au moins une colonne datetime timestamp) pour lequel les valeurs de la timestampcolonne sont supérieures à la valeur de targettime. Question similaire .


1
Je ne suis pas sûr de cela, mais j'ai le sentiment que cela serait extrêmement lourd sur l'utilisation de la mémoire.
Nathan

2

Si la plage filtrée est contiguë (comme c'est généralement le cas avec les filtres d'horodatage), la solution la plus rapide consiste à coder en dur la plage de lignes. Combinez simplement skiprows=range(1, start_row)avec des nrows=end_rowparamètres. Ensuite, l'importation prend quelques secondes alors que la solution acceptée prendrait quelques minutes. Quelques expériences avec l'initiale start_rowne sont pas un coût énorme étant donné les économies sur les temps d'importation. Notez que nous avons conservé la ligne d'en-tête en utilisant range(1,..).


-3

Vous pouvez spécifier le nrowsparamètre.

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

Ce code fonctionne bien dans la version 0.20.3.


1
OP demande comment filtrer et non limiter le nombre de lignes lues. C'est pourquoi j'ai décliné votre réponse.
Roman Orac

-4

Si vous êtes sous Linux, vous pouvez utiliser 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

1
L'utilisation de grep est un très mauvais choix pour plusieurs raisons. 1) c'est lent 2) ce n'est pas portable 3) ce n'est pas des pandas ou python (vous pouvez utiliser des expressions régulières directement dans python) c'est pourquoi j'ai décliné votre réponse
Ahmed Masud

Votre solution ne fonctionne pas sur toutes les plates-formes et inclut également Grep. C'est la raison du vote défavorable.
Roman Orac le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.