高效地读取CSV文件中最后的'n'行数据并转换为DataFrame

29
有几种方法可以实现这个目标:
  1. 读取整个 CSV,然后使用 df.tail
  2. 以某种方式反转文件(对于大文件来说最好的方法是什么?),然后使用 nrows 参数进行读取
  3. 以某种方式查找 CSV 中的行数,然后使用 skiprows 并读取所需的行数。
  4. 可能要进行块读取,丢弃初始块(虽然不确定这将如何工作)
有更简单的方法吗?如果没有,应该选择其中哪一种方法?为什么?
可能相关:
  1. 高效地查找文本文件中的最后一行
  2. 使用 pandas 的 read_csv 和 nrows 读取 ~13000 行 CSV 文件的部分内容
不直接相关:
  1. 如何获取 pandas 数据帧的最后 n 行?

1
你也可以使用 seek() 然后向后移动。 - Burhan Khalid
7个回答

35

我认为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)

另一个值得尝试的选项是在第一次读取文件时获取行数,然后再次读取文件,使用read_csv跳过该行数(减去n)...


1
如果您正在使用Python 3.x,请将“from StringIO import StringIO”替换为“from io import StringIO”。请参阅https://dev59.com/7mct5IYBdhLWcg3wqfL9#18284900。 - xiaxio
谢谢。在我的情况下,这真的很快。使用形状为(9020057, 4)的csv进行测试,并检索最后的10000行。将其与df.tail(10000)df.iloc[-10000:]进行比较。 - Rakesh
1
有时候从文件的第一行获取标题是很有帮助的。我使用以下代码来获取最后N行,并将标题放在开头。 with open(product_filename, 'r') as f: q = [ f.readline() ] q.extend(deque(f,ndays)) df = pd.read_csv(StringIO(''.join(q))) - rocketman

13

这是一个方便的方法。对于我想要做的事情非常有效 -

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

1
我尝试了几种读取CSV文件中最后n行的方法,包括在这个主题中发布的方法以及这个问题https://dev59.com/npnga4cB1Zd3GeqPYnP6上的一些方法,其中@Parikshit Bhinde的方法是最快的。 - xiaxio

7
文件只是一系列二进制数的流。行并不存在于单独的实体中;它们是将某些字节视为换行符而产生的结果。因此,您必须从文件开头开始读取以按顺序确定行。
如果文件不会(经常)更改并且这是您需要经常执行的操作(例如,使用不同的`n`值),则可以在第二个文件中存储换行符的字节偏移量。您可以使用此更小的文件和“`seek`”命令快速跳转到第一个文件中的特定行并从那里读取。
(某些操作系统提供记录导向文件,其内部结构比普通的平面文件更复杂。上述内容不适用于它们。)

4
第三个选项是我使用的:

以某种方式找到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))

"somehow"这个想法是我从这里得到的。


可以尝试使用skiprows=range(0, ...)来跳过第一行! - Mustafah

3

鉴于您正在考虑反转文件,我假设创建新文件是可以接受的。

  1. 创建一个包含原始文件最后n行的新文件。 tail -n original.csv > temp.csv
  2. 向temp文件添加标题行并生成新文件。 head -1 original.csv | cat - temp.csv > newfile.csv && rm -f temp.csv

顺便提一下,使用tempfile来安全地创建临时文件。如果您已经有一个名为temp.csv的文件,那么可能会遇到麻烦。 - Antoine

2

需求:

  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文件仅在行尾位置包含换行符,但并非所有csv文件都是如此。

此外,该代码还会提取标题行,以便正确读入pandas中的列。如果您不需要标题行,则可以删除文件打开后的第一行,并修改函数返回值以仅处理尾部。

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


0
您可以创建一个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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接