Как я могу фильтровать строки при загрузке в функции Pandas read_csv?

101

Как я могу отфильтровать, какие строки CSV должны быть загружены в память с помощью pandas? Похоже, это вариант, который стоит найти read_csv. Я что-то упускаю?

Пример: у нас есть CSV со столбцом с меткой времени, и мы хотим загрузить только те строки, метка времени которых больше заданной константы.

Бенджаминвильсон
источник

Ответы:

174

Нет возможности фильтровать строки перед загрузкой файла CSV в объект pandas.

Вы можете либо загрузить файл, а затем отфильтровать его df[df['field'] > constant], либо, если у вас очень большой файл, и вы беспокоитесь о нехватке памяти, затем используйте итератор и примените фильтр при объединении фрагментов вашего файла, например:

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

Вы можете изменить это chunksizeв соответствии с доступной памятью. Подробнее см. Здесь .

Матти Джон
источник
для chunk['filed']>constantя могу сэндвич его между 2 постоянными значениями? Например: constant1> chunk ['field']> constant2. Или я могу использовать «в диапазоне»?
weefwefwqg3
1
Попробуйте:chunk[(chunk['field'] > constant2)&(chunk['field']<constant1)]
Йоханнес Вакс
Это отсутствует .loc? chunk.loc[chunk['field'] > constant]
Винсент
1
Вы можете использовать логические маски как с, так и без них .loc. Я не думаю, что .locсуществовал еще в 2012 году, но думаю, что в наши дни использование .locболее явное.
Матти Джон
10

Я не нашел прямого способа сделать это в контексте read_csv. Однако read_csvвозвращает DataFrame, который можно отфильтровать, выбирая строки по логическому вектору df[bool_vec]:

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

Это выбор всех строк в df (при условии, что df - это любой DataFrame, такой как результат read_csvвызова, который хотя бы содержит столбец datetime timestamp), для которых значения в timestampстолбце больше, чем значение targettime. Аналогичный вопрос .

Грифон
источник
1
Я не уверен в этом, но у меня такое чувство, что это будет очень сильно сказываться на использовании памяти.
Натан
3

Если отфильтрованный диапазон является непрерывным (как это обычно бывает с фильтрами времени (меток)), то самым быстрым решением является жесткое кодирование диапазона строк. Просто комбинируйте skiprows=range(1, start_row)с nrows=end_rowпараметрами. Тогда импорт займет секунды, тогда как принятое решение займет минуты. Несколько экспериментов с исходным start_rowкодом не являются огромными затратами, учитывая экономию времени на импорт. Обратите внимание, что мы сохранили строку заголовка, используя range(1,..).

Mirekphd
источник
0

Альтернативой принятому ответу является применение read_csv () к StringIO, полученному путем фильтрации входного файла.

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

df = pd.read_csv(StringIO(text))

Это решение часто быстрее, чем принятый ответ, когда условие фильтрации сохраняет только небольшую часть строк.

М. Пейдж
источник
-3

Если вы используете Linux, вы можете использовать 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
Использование grep - плохой выбор по нескольким причинам. 1) он медленный 2) он не переносится 3) это не панды или питон (вы можете использовать регулярные выражения прямо внутри питона), поэтому я проголосовал против вашего ответа
Ахмед Масуд
Ваше решение работает не на всех платформах, в том числе оно включает Grep. Это причина отрицательного голоса.
Roman Orac
-3

Вы можете указать nrowsпараметр.

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

Этот код хорошо работает в версии 0.20.3.

user1083290
источник
1
OP спрашивает, как фильтровать, а не ограничивать количество прочитанных строк. Вот почему я отклонил ваш ответ.
Roman Orac