Чтение определенных кусков панд / не чтение всех кусков в пандах - PullRequest
3 голосов
/ 23 мая 2019

Я пытаюсь использовать в соответствии с этим вопрос и ответ , читая большой CSV-файл кусками и обрабатывая его.Поскольку я не знаком с Python, у меня возникла проблема с оптимизацией, и я ищу лучшее решение здесь.

Что делает мой код:

Я читаю в счетчике строк моего csv с

with open(file) as f:
    row_count = sum(1 for line in f)

, после чего я "нарезаю" свои данные на 30 блоков равного размера и обрабатываюэто соответствует связанному ответу с циклом for и pd.read_csv(file, chunksize).Поскольку построение 30 графиков в одном довольно неясно, я делаю это каждые 5 шагов по модулю (который может изменяться).Для этого я использую внешний счетчик.

chunksize = row_count // 30
counter = 0
for chunk in pd.read_csv(file, chunksize=chunksize):
    df = chunk
    print(counter)
    if ((counter % 5) == 0 | (counter == 0):
        plt.plot(df["Variable"])
    counter = counter +1
plt.show()

Теперь на мой вопрос:

Кажется, что этот цикл считывает размер куска перед обработкой цикла, что является разумным.Я вижу это, поскольку шаги print(counter) также довольно медленные.Так как я прочитал несколько миллионов строк в CSV, каждый шаг занимает некоторое время.Есть ли способ пропустить ненужные куски в цикле for, прежде чем читать его?Я пробовал что-то вроде:

wanted_plts <- [1,5,10,15,20,25,30]
for i in wanted_plts:
   for chunk[i] in pd.read_csv(file, chunksize=chunksize):
   .
   .

Я думаю, у меня есть проблемы с пониманием, как я могу манипулировать этим синтаксисом диапазона цикла for.Должен быть элегантный способ исправить это.

Кроме того: я нашел .get_chunk(x) от панд, но это, кажется, создает только один кусок размером x.

Еще одна попытка, которую я пытаюсьдля подстановки объекта считывателя pd.read_csv, например pd.read_csv()[0,1,2], но, похоже, это тоже невозможно.


Поправка: я знаю, что вывод большого количества данных в matplotlib очень медленный.Я предварительно обработал его ранее, но для того, чтобы сделать этот код читабельным, я удалил все ненужные части.

Ответы [ 3 ]

1 голос
/ 01 июня 2019

Насколько я знаю, pandas не поддерживает пропуски фрагментов файла.По крайней мере, я ничего не нашел об этом в документации.

В общем, пропустить строки из файла (не читая их вообще) сложно, если вы заранее не знаете, сколько строк вы хотите пропустить и сколько символову вас есть в каждой из этих строк.В этом случае вы можете попробовать поиграть с IO и seek , чтобы переместить позицию потока в нужное место на следующей итерации.
Но это не похоже на ваш случай.

Я думаю, что лучшее, что вы можете сделать для повышения эффективности, - это прочитать строки с использованием стандартного ввода-вывода и преобразовать в кадр данных только те строки, которые вам нужны / которые вы хотите построить.

Рассмотрим, например,следующий пользовательский итератор.
При создании он сохраняет заголовок (первая строка).Каждую итерацию он читает часть строк из файла и затем пропускает следующие n*chunksize строки.Он возвращает строку заголовка, за которой следуют строки чтения, обернутые в объект io.StringIO (поэтому это поток и может быть передан непосредственно в pandas.read_csv).

import io
from itertools import islice

class DfReaderChunks:
    def __init__(self, filename, chunksize, n):
        self.fo = open(filename)
        self.chs = chunksize
        self.skiplines = self.chs * n
        self.header = next(self.fo)

    def getchunk(self):
        ll = list(islice(self.fo, self.chs))
        if len(ll) == 0:
            raise StopIteration
        dd = list(islice(self.fo, self.skiplines))
        return self.header + ''.join(ll)

    def __iter__(self):
        return self

    def __next__(self):
        return io.StringIO(self.getchunk())

    def close(self):
        self.fo.close()

    def __del__(self):
        self.fo.close()

Используя этот класс, вы можете читатьиз вашего файла:

reader = DfReaderChunks(file, chunksize, 4)
for dfst in reader:
    df = pd.read_csv(dfst)
    print(df) #here I print to stdout, you can plot
reader.close()

, что "эквивалентно" вашей настройке:

for chunk in pd.read_csv(file, chunksize=chunksize):
    df = chunk
    if (counter % 5 == 0):
        print(df) #again I print, you can plot
    counter += 1

Я проверил время, используемое обоими вышеупомянутыми фрагментами, используя кадр данных 39 Мб (100000 строкили случайные числа).

На моем компьютере первое занимает 0,458 секунды, а второе 0,821 секунды.

Единственный недостаток заключается в том, что первый фрагмент теряет отслеживание индекса строки (это новыйфрейм данных каждый раз, поэтому индекс всегда начинается с 0), но напечатанные фрагменты одинаковы.

1 голос
/ 03 июня 2019

Вы тратите много ресурсов при разборе CSV на DataFrame без его использования.Чтобы избежать этого, вы можете создать индекс строки во время первого прохода:

fp = open(file_name)
row_count = 0
pos = {0: 0}
line = fp.readline()
while line:
    row_count += 1
    pos[row_count] = fp.tell()
    line = fp.readline()

Пока не утилизируйте дескриптор файла!* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 100 * * 100 * * *} * * * * * * * * * * * * * * * * * * * * * * * * * * 100 * 100 * * * И, наконец, предупреждение: при чтении CSV вы потеряете имена столбцов.Поэтому сделайте корректировку:

 obj = pd.read_csv(fp, chunksize=chunksize, names=[!!<column names you have>!!])

PS file - зарезервированное слово, избегайте его использования для предотвращения нежелательных побочных эффектов.Вместо этого вы можете использовать file_ или file_name.

1 голос
/ 01 июня 2019

Я играл с вашей настройкой, пытаясь найти способ пропустить фрагменты, используя другую библиотеку рендеринга, такую ​​как pyqtgraph или используя подпрограммы matplotlib.pyplot вместо plot(), но все безрезультатно.

Так что единственный справедливый совет, который я могу вам дать, - это ограничить область действия read_csv только теми данными, которые вас интересуют , передав параметр usecols.

Вместо:

for chunk in pd.read_csv(file, chunksize=chunksize):
    plt.plot(chunk['Variable'])

Использование:

for chunk in pd.read_csv(file, usecols=['Variable'], chunksize=chunksize):
    plt.plot(chunk)

И, если вы еще этого не сделали, определенно ограничьте число итераций, выбрав самый большой chunksize, который вы, возможно, можете (так что в вашем случае самый низкий делитель row_count).

Я не дал количественную оценку их соответствующего веса, но вы увеличите накладные расходы как csv_read(), так и plot(), хотя бы немного, из-за того, что ваши текущие блоки уже достаточно велики.

С моими данными испытаний четырехкратное увеличение chunksize сокращает время обработки в два раза:

chunksize=1000 => выполнено за 12,7 с
chunksize=2000 => выполнено за 9.06 с
chunksize=3000 => выполнено за 7,68 с
chunksize=4000 => выполнено за 6,94 с

И указание usecols во время чтения также сокращает время обработки в два раза:

chunksize=1000 + usecols=['Variable'] => выполнено за 8,33 с
chunksize=2000 + usecols=['Variable'] => выполнено за 5,27
chunksize=3000 + usecols=['Variable'] => выполнено за 4,39 с
chunksize=4000 + usecols=['Variable'] => выполнено за 3,54 с

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...