【问题标题】:Efficiently Read last 'n' rows of CSV into DataFrame有效地将最后 'n' 行 CSV 读入 DataFrame
【发布时间】:2013-06-11 02:08:03
【问题描述】:

一些方法可以做到这一点:

  1. 读取整个CSV,然后使用df.tail
  2. 以某种方式反转文件(对大文件执行此操作的最佳方法是什么?),然后使用nrows 参数读取
  3. 以某种方式找到 CSV 中的行数,然后使用 skiprows 并读取所需的行数。
  4. 也许做块读取丢弃初始块(虽然不确定这会如何工作)

能以更简单的方式完成吗?如果不是,这三者中应该优先选择哪一个?为什么?

可能相关:

  1. Efficiently finding the last line in a text file
  2. Reading parts of ~13000 row CSV file with pandas read_csv and nrows

无直接关系:

  1. How to get the last n row of pandas dataframe?

【问题讨论】:

  • 你也可以seek() 然后倒退。

标签: python csv numpy pandas


【解决方案1】:

我认为 pandas 在 read_csv 中没有提供这样做的方法。

也许最简洁的(一次性)是使用collections.deque

from collections import deque
from StringIO import StringIO

with open(fname, 'r') as f:
    q = deque(f, 2)  # replace 2 with n (lines read at the end)

In [12]: q
Out[12]: deque(['7,8,9\n', '10,11,12'], maxlen=2)
         # these are the last two lines of my csv

In [13]: pd.read_csv(StringIO(''.join(q)), header=None)

另一个值得尝试的选择是 get the number of lines in a first pass 然后再次读取文件,使用 read_csv 跳过该行数(减去 n)...

【讨论】:

  • 如果您使用的是 python 3.x,请将 'from StringIO import StringIO' 替换为 'from io import StringIO'。检查stackoverflow.com/questions/11914472/stringio-in-python3/…
  • 谢谢。就我而言,这真的很快。使用形状为 (9020057, 4) 的 csv 进行测试,并检索最后的 10000 行。与df.tail(10000)df.iloc[-10000:]比较
  • 有时从文件的第一行获取标题会很有帮助。我使用这段代码来获取开头的最后 N 行标题。使用 open(product_filename, 'r') as f: q = [ f.readline() ] q.extend(deque(f,ndays)) df = pd.read_csv(StringIO(''.join(q)))
【解决方案2】:

这是一个方便的方法。非常适合我喜欢做的事情 -

import tailer
import pandas as pd
import io

with open(filename) as file:
    last_lines = tailer.tail(file, 15)

df = pd.read_csv(io.StringIO('\n'.join(last_lines)), header=None)

您需要安装tailer,才能正常工作:

pip install --user tailer

【讨论】:

  • 我尝试了几种方法来读取 csv 文件中的最后 n 行,包括在这个线程上发布的那些以及在另一个问题上发布的一些:stackoverflow.com/questions/38704949/…,@Parikshit Bhinde 的一个是最快的
【解决方案3】:

文件只是字节流。线不作为单独的实体存在;它们是将某些字节视为换行符的产物。因此,您必须从文件开头读取以按顺序识别行。

如果文件不(经常)更改并且这是您需要经常执行的操作(例如,使用不同的 n 值),您可以将换行符的字节偏移量存储在第二个文件中。您可以使用这个小得多的文件和seek 命令快速跳转到第一个文件中的给定行并从那里读取。

(有些操作系统提供的面向记录的文件,其内部结构比普通的平面文件更复杂。以上不适用于它们。)

【讨论】:

    【解决方案4】:

    由于您正在考虑反转文件,我认为可以创建新文件。

    1. 用最后 n 行创建一个新文件。 tail -n original.csv > temp.csv
    2. 将标题行添加到临时文件并生成新文件。 head -1 original.csv | cat - temp.csv > newfile.csv && rm -f temp.csv

    【讨论】:

    • 附带说明,请使用tempfile 安全地创建临时文件。如果您已经有一个名为 temp.csv 的文件,您可能会遇到麻烦。
    【解决方案5】:

    要求:

    1. 快速 - 适用于任何大小/长度的 csv
    2. 快速 - 处理时间仅基于 EOF 中的行长度和行数
    3. 不允许其他依赖项

    代码:

    import pandas as pd
    import io
    import sys
    
    def get_csv_tail(filepath, max_rows=1):
        with open(filepath, "rb") as f:
            first = f.readline().decode(sys.stdout.encoding)  # Read the first line.
            f.seek(-2, 2)                                     # Jump to the second last byte.
            count = 0
            while count < max_rows:                           # Until we've gone max_rows back
                try:
                    while f.read(1) != b"\n":                 # Until EOL is found...
                        f.seek(-2, 1)                         # ...jump back the read byte plus one more.
                except IOError:
                    f.seek(-1, 1)
                    if f.tell() == 0:
                        break
                count = count + 1
                f.seek(-2, 1)                                 # ...jump back the read byte plus one more.
            f.seek(1, 1)                                      # move forward one byte
            tail = f.read().decode(sys.stdout.encoding)       # We found our spot; read from here through to the end of the file.
            f.close()
                    
        return io.StringIO(first + tail)
    
    df = pd.read_csv(get_csv_tail('long.csv', max_rows=5))    # Get the last five rows as a df
    

    警告:这假设您的 csv 仅在 EOL 位置包含换行符,这并非适用于所有 csv 文件。

    这也会拉出标题,以便将列正确读入 pandas。如果不需要,可以在文件打开后去掉第一行,修改函数返回,只处理尾部。

    基于What is the most efficient way to get first and last line of a text file?

    【讨论】:

      【解决方案6】:

      第三个选项是我使用的:

      以某种方式找到 CSV 中的行数,然后使用 skiprows 并读取 所需的行数。

      这是我的建议:

      import pandas as pd 
      
      # User inputs
      fname = 'test_file.csv'
      tail_len = 15
      
      # The two steps in the description
      n_rows = sum(1 for row in open(fname, 'r'))
      df = pd.read_csv(fname, skiprows=range(1, n_rows - tail_len))
      

      关于“不知何故”我从here 那里得到了这个想法。

      【讨论】:

        【解决方案7】:

        您可以创建 metadata.csv 文件,并跟踪 csv 的长度。每次将行添加到 csv 时,使用最新的 row_count 更新 metadata.csv 文件。下次加载 csv 时,只需使用以下内容:

        file_size = 139405 #stored in your metadata.csv file
        n_bottom_rows = 7
        df = pd.read_csv('myfile.csv',skiprows = filesize - n_bottom_rows)
        

        【讨论】:

          猜你喜欢
          • 2018-09-10
          • 2018-07-27
          • 2019-08-16
          • 2015-07-06
          • 1970-01-01
          • 2021-05-05
          • 2016-01-28
          • 1970-01-01
          • 2016-02-16
          相关资源
          最近更新 更多