【问题标题】:Python: trouble implementing efficient way to read specific line in csvPython:难以实现读取 csv 中特定行的有效方法
【发布时间】:2019-03-02 19:21:26
【问题描述】:

在我的 ML 项目中,我开始遇到 10 Gb+ 大小的 csv 文件,因此我正在尝试实施一种有效的方法来从我的 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

我哪里错了?

【问题讨论】:

  • 熊猫不能满足你的需求吗? pandas.pydata.org/pandas-docs/stable/reference/api/…
  • 不,因为 pandas 需要大约几分钟才能为我加载整个 csv,而我通常只需要它们的特定行(在我上面的示例中,最后一行)。
  • 你需要一个换行偏移索引。因此,您可以在不读取文件的情况下查找给定行。
  • 循环遍历它不会将每一行都加载到内存中。它一次加载一行,因此最多只需要最大行的内存开销。

标签: python loops csv file-io iterator


【解决方案1】:

出了什么问题是您正在迭代文件以使其长度耗尽文件迭代器,

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 repl 中:

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'

【讨论】:

  • 那么,我将如何重新打开文件,或以某种方式使用f.seek(0)(不确定如何使用)?我发现能够找到我的 csv 的长度很重要,并且不想停止这样做。另外,我会在哪里使用collections.deque(f, maxlen=1)
  • 虽然我很欣赏你在回答中所做的工作,但在我看来,我只给出了打开的 csv 的最后一行,而不是任意一行。例如,我如何使用您的两个函数返回(最好由 csv.reader 解析)第 22 行?
  • @Coolio2654 对任意行使用line = next(itertools.islice(f, n, n+1), None) 之类的东西n。注意处理标题。我建议您不要使用 csv 解析它,因为如果性能是您的问题,解析将需要大量工作,而您将丢弃这些工作。只需解析最后一行,你可以做parsed = next(csv.reader(io.StringIO(line)))来利用模块
猜你喜欢
  • 1970-01-01
  • 2019-11-12
  • 2015-09-06
  • 1970-01-01
  • 2017-01-13
  • 2014-12-15
  • 2017-08-19
  • 1970-01-01
  • 2019-05-12
相关资源
最近更新 更多