Python: проблема с реализацией эффективного способа чтения конкретной строки в CSV - PullRequest
0 голосов
/ 02 марта 2019

В моих проектах ML я столкнулся с csv-файлами размером 10 Гб +, поэтому я пытаюсь реализовать эффективный способ получения определенных строк из моих csv-файлов.

Это привело меня к обнаружению itertools(который может предположительно эффективно пропускать строки csv.reader, тогда как зацикливание на нем вместо этого будет загружать каждую строку, которую он переместил в память), и после ответа this я попробовал следующее:

import collections
import itertools

with open(csv_name, newline='') as f:

    ## Efficiently find total number of lines in csv
    lines = sum(1 for line in f)

    ## Proceed only if my csv has more than just its header
    if lines < 2:
        return None   
    else:

        ## Read csv file
        reader = csv.reader(f, delimiter=',')

        ## Skip to last line
        consume(reader, lines)

        ## Output last row
        last_row = list(itertools.islice(reader, None, None))

с consume(), являющимся

def consume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(itertools.islice(iterator, n, n), None)

Однако я получаю только пустые списки от last_row, что означает, что что-то пошло не так.

Короткий CSV, который я проверяю этокод на:

Author,Date,Text,Length,Favorites,Retweets
Random_account,2019-03-02 19:14:51,twenty-two,10,0,0

Где я иду не так?

1 Ответ

0 голосов
/ 03 марта 2019

Что не так, вы перебираете файл, чтобы получить его длину, исчерпывая файловый итератор,

lines = sum(1 for line in f)

Вам нужно либо заново открыть файл, либо использовать f.seek(0).

Так что либо:

def get_last_line(csv_name):

    with open(csv_name, newline='') as f:
        ## Efficiently find total number of lines in csv
        lines = sum(1 for line in f) # the iterator is now exhausted

    if len(lines) < 2:
        return

    with open(csv_name, newline='') as f: # open file again
        # Keep going with your function
        ...

В качестве альтернативы

def get_last_line(csv_name):

    with open(csv_name, newline='') as f:
        ## Efficiently find total number of lines in csv
        lines = sum(1 for line in f) # the iterator is now exhausted

        if len(lines) < 2:
            return

        # we can "cheat" the iterator protocol and
        # and move the iterator back to the beginning
        f.seek(0) 
        ... # continue with the function

Однако, если вы хотите последнюю строку, вы можете просто сделать:

for line in f:
   pass
print(line)

Возможно, использование collections.deque будет быстрее (они используют его в рецепте):

collections.deque(f, maxlen=1)

Вот два разных способа решения проблемы, позвольте мне просто создать файл очень быстро:

Juans-MacBook-Pro:tempdata juan$ history > history.txt
Juans-MacBook-Pro:tempdata juan$ history >> history.txt
Juans-MacBook-Pro:tempdata juan$ history >> history.txt
Juans-MacBook-Pro:tempdata juan$ history >> history.txt
Juans-MacBook-Pro:tempdata juan$ cat history.txt | wc -l
    2000

ОК, в реплее IPython:

In [1]: def get_last_line_fl(filename):
   ...:     with open(filename) as f:
   ...:         prev = None
   ...:         for line in f:
   ...:             prev = line
   ...:         if prev is None:
   ...:             return None
   ...:         else:
   ...:             return line
   ...:

In [2]: import collections
   ...: def get_last_line_dq(filename):
   ...:     with open(filename) as f:
   ...:         last_two = collections.deque(f, maxlen=2)
   ...:         if len(last_two) < 2:
   ...:             return
   ...:         else:
   ...:             return last_two[-1]
   ...:

In [3]: %timeit get_last_line_fl('history.txt')
1000 loops, best of 3: 337 µs per loop

In [4]: %timeit get_last_line_dq('history.txt')
1000 loops, best of 3: 339 µs per loop

In [5]: get_last_line_fl('history.txt')
Out[5]: '  588  history >> history.txt\n'

In [6]: get_last_line_dq('history.txt')
Out[6]: '  588  history >> history.txt\n'
...