获取CSV文件的最后10000行

7
在pandas中,我可以使用 pandas.io.parser.read_csv("file.csv", nrows=10000) 来获取csv文件的前10000行。
但是因为我的csv文件非常大,而且最后几行比前面的几行更重要,所以我想读取最后的10000行。然而,即使我知道文件的长度,要跳过一个1000000行的csv文件的前990000行,使用 pandas.io.parser.read_csv("file.csv", nrows=10000, skiprows=990000) 也会跳过包含文件头的第一行。(header=0 是在应用了 skiprows 后进行计算的,所以它也无法帮助。)
如何获取具有第0行标题的csv文件的最后10000行,最好不知道文件的行数?

你使用的是Linux或OSX系统吗?如果是,那么使用tail -n 10000 file > file2可能是最简单的方法... - Martin Tournoij
借鉴@Carpetsmoker的想法,如果你坚持使用Python,你可以在subprocess.call()中调用它 :P - Patrick the Cat
@Carpetsmoker 但他还需要一个头部。它应该是 head -n 1 file > file2; tail -n 10000 file >> file2 - Anton Protopopov
@AntonProtopopov 你觉得更短的写法怎样: head -1 file > file2; tail -10000 file >> file2。我少打了四个键。 - Patrick the Cat
4个回答

7
你可以先使用以下方法计算文件大小:
size = sum(1 for l in open('file.csv'))

接下来使用rangeskiprows

df = pd.read_csv('file.csv', skiprows=range(1, size - 10000))

编辑

正如 @ivan_pozdeev 提到的那样,使用这种解决方案需要两次读取文件。我尝试使用 pandas 一次性读取整个文件,然后使用 tail 方法,但是该方法比建议的方法慢。

示例数据帧:

pd.DataFrame(np.random.randn(1000000,3), columns=list('abc')).to_csv('file.csv')

时间控制

def f1():
    size = sum(1 for l in open('file.csv'))
    return pd.read_csv('file.csv', skiprows=range(1, size - 10000))

def f2():
    return pd.read_csv('file.csv').tail(10000)

In [10]: %timeit f1()
1 loop, best of 3: 1.8 s per loop

In [11]: %timeit f2()
1 loop, best of 3: 1.94 s per loop

2
使用 tail,首先需要将 所有 数据读入到 DataFrame 中,然后再进行切片。这不仅速度较慢,还可能会导致内存耗尽。 - ivan_pozdeev
如果有人想知道,我认为更大的问题可能是临时列表,它的大小与“大文件”相同,但是pandas无论如何都会将整数爆炸成列表。 - luk32
这取决于文件的大小。如果您有足够的内存来读入文件而不进行交换,那么速度会更快。但一旦交换开始,一切都会变得很慢。对于可扩展的解决方案,您需要找到一种即使处理非常大的文件也能正常工作的方法。但是,如果您预计大多数文件都足够小以适合内存,则可能希望在可能的情况下使用更快的算法,然后在输入过大时退回可扩展的算法。 - tripleee
@tripleee tail的性能会比RAM限制慢得多。tail解决方案完全解析了数据集,而跳过行只需要正确地跟踪分隔符即可。这是个严重的优势。如果实际上要跳过的行数相对较少,使初始传递基本上只是一个开销,则tail才有机会。 - luk32
@luk32 我相信你是正确的;我在思考一般情况,并不熟悉Pandas的内部机制。 - tripleee
显示剩余3条评论

4
使用@Anton Protopopov的样本文件。将文件的一部分和头分别读入远比读取整个文件便宜得多。
直接读取最后的行即可。
In [22]: df = read_csv("file.csv", nrows=10000, skiprows=990001, header=None, index_col=0)

In [23]: df
Out[23]: 
               1         2         3
0                                   
990000 -0.902507 -0.274718  1.155361
990001 -0.591442 -0.318853 -0.089092
990002 -1.461444 -0.070372  0.946964
990003  0.608169 -0.076891  0.431654
990004  1.149982  0.661430  0.456155
...          ...       ...       ...
999995  0.057719  0.370591  0.081722
999996  0.157751 -1.204664  1.150288
999997 -2.174867 -0.578116  0.647010
999998 -0.668920  1.059817 -2.091019
999999 -0.263830 -1.195737 -0.571498

[10000 rows x 3 columns]

非常快速地完成此任务

In [24]: %timeit read_csv("file.csv", nrows=10000, skiprows=990001, header=None, index_col=0)
1 loop, best of 3: 262 ms per loop

预先确定文件长度非常便宜

In [25]: %timeit sum(1 for l in open('file.csv'))
10 loops, best of 3: 104 ms per loop

在头部读取

In [26]: df.columns = read_csv('file.csv', header=0, nrows=1, index_col=0).columns

In [27]: df
Out[27]: 
               a         b         c
0                                   
990000 -0.902507 -0.274718  1.155361
990001 -0.591442 -0.318853 -0.089092
990002 -1.461444 -0.070372  0.946964
990003  0.608169 -0.076891  0.431654
990004  1.149982  0.661430  0.456155
...          ...       ...       ...
999995  0.057719  0.370591  0.081722
999996  0.157751 -1.204664  1.150288
999997 -2.174867 -0.578116  0.647010
999998 -0.668920  1.059817 -2.091019
999999 -0.263830 -1.195737 -0.571498

[10000 rows x 3 columns]

2

只取最后N行的唯一方法是,根据Anton Protopopov的说法,首先遍历整个文件并计算行数。

但在接下来的步骤中,即获取这些行时,可以进行优化(就像tail所做的那样):

  • 当您遍历时,请将行的偏移量保存在长度为N的循环缓冲区中。然后,在结束时,缓冲区中最旧的项目将是所需的偏移量。然后,只需要像使用Python Pandas处理大于10GB的数据集那样在文件对象上执行f.seek()操作。

一个更快的方法是不要求准确的行数:从我看到的情况来看,您只需要任意大量的行。因此,您可以:

  • 得到您需要查找的偏移量的大致估计(例如,计算/估算一行的平均长度)
  • 跳转到该位置,然后跳到下一个(或上一个)换行符

    如果您的数据中有嵌入式换行符,则需要格外小心:在这种情况下,没有绝对可靠的方法来检测哪些引号是开放的,哪些是闭合的。您必须做出关于什么可以在引号内/外以及甚至要查找多远的引号才能找到嵌入式换行符的假设!


0

你可以尝试使用pandas中的tail,它会返回最后n行

df.tail(10000)

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