【问题标题】:Only read certain rows in a csv file with python仅使用 python 读取 csv 文件中的某些行
【发布时间】:2019-10-09 00:54:53
【问题描述】:

我只想从 csv 文件中的某一行开始读取一定数量的行而不是迭代整个 csv 文件以达到这一点。

假设我有一个包含 100 行的 csv 文件,我只想读取第 50 到 60 行。我不想从第 1 行迭代到第 49 行以到达第 50 行开始读取。我可以通过 seek() 以某种方式实现这一点吗?

例如: 寻找第 50 行 从 50 读到 60

下次: 寻找第 27 行 读 27 到 34 等等

因此,不仅在文件中不断向前搜索,而且还向后搜索。

非常感谢

【问题讨论】:

  • 可以使用外部库吗?
  • 因为它是 csv 我假设它是字符。每行是否总是相同数量的字符?你总是有相同的行分隔符吗?
  • 要跳过读取整个文件并读取特定行,这些行必须是固定长度的。如果不计算文件中的每个换行符直到你想要的行,那么如何知道你在哪一行,这意味着从头开始读取整个文件。 CSV 是 20gb 文件的错误格式。将其转换为数据库。
  • 也许我遗漏了一些东西,但是如果不阅读之前的文件部分就不可能找到你想要的行吗?编辑:除非如上述评论所说,这些行的长度是提前已知的。这不是这里的情况。

标签: python csv


【解决方案1】:

你可以使用 chunksize

import pandas as pd

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

【讨论】:

  • 那么你仍然阅读整个文件。 100 行不是问题,但是当您读取多达 10000 行的多列文件时...
  • 通常文件的大小不是问题。这取决于您的计算机内存。编写代码时您的错误是什么?和大小文件
  • Pandas 为我指定的范围创建一个数据框。当我想从不在此数据框中的另一个范围读取数据时,我必须创建一个新的数据框。那不是最优的。 pandas 也只允许向前读取以获得下一个块,但不能向后读取。就像我第一次读到的第 70 行到第 80 行一样,然后下一次读到第 30 到 40 行。这对于熊猫来说是不可能的。
  • 由于文件是 20gb,程序就崩溃了
【解决方案2】:

一个选项是使用 Pandas。例如:

import pandas as pd
# Select file 
infile = r'path/file'
# Use skiprows to choose starting point and nrows to choose number of rows
data = pd.read_csv(infile, skiprows = 50, nrows=10)

【讨论】:

  • 我试过了。不幸的是,使用skirows,熊猫也以某种方式迭代了csv。或者至少读入整个文件以创建一个带有索引的列表。这不是我想要的,因为文件是 20gb。
  • 检查我的答案@Horst
  • Pandas 必须以某种方式跟踪它已经跳过的行数。如果你的行是固定长度的,或者如果它们被索引,你可以解决这个问题。否则,唯一的方法是计算需要读取文件的换行符的数量。
  • 我有一组始终从第 9 行开始的 csv 文件,但最后一行并不总是相同。如何设置结束行动态?在我想要的最后一行和我不想要的数据描述之间并不总是有一个空行。
【解决方案3】:

正如其他人所说,最明显的解决方案是使用 pandas read csv ! 该方法有一个称为skirows的参数:

from the doc有话说:

skiprows : 类似列表,int 或可调用,可选 文件开头要跳过的行号(0-indexed)或要跳过的行数(int)。

如果可调用,可调用函数将根据行索引进行评估,如果应该跳过该行,则返回 True,否则返回 False。一个有效的可调用参数的例子是 lambda x: x in [0, 2]。

你可以有这样的东西:

import pandas as pd
data = pd.read_csv('path/to/your/file', skiprows =lambda x: x not in range(50, 60))

由于您指定内存是您的问题,您可以使用 this tutorial 中所述的 chunksize 参数

他说:

参数本质上表示要读入的行数 数据帧在任何一个时间,以适应本地内存。 由于数据包含超过 7000 万行,我指定 每次破坏大数据的块大小为 100 万行 分成许多小块。

df_chunk = pd.read_csv(r'../input/data.csv', chunksize=1000000)

您可以尝试这样做并遍历块以仅检索您正在查找的行。

如果行号在指定列表中,该函数应返回 true

【讨论】:

  • 我试过了。不幸的是,使用skirows,熊猫也以某种方式迭代了csv。或者至少读入整个文件以创建带有索引的列表。这不是我想要的,因为文件是 20gb。
  • 如果 @Horst 只想要第 50-60 行,你的函数不应该是 lambda x: x not in range(50,60) 吗?
  • 指定块大小通常会起作用,但它不会让我来回走动,只能向前获取下一个块。但是在应用中我必须绘制数据并且用户应该能够显示下一个块以及前一个块。基本上是从头到尾,从头到尾。通过这样做,用户应该能够定义他希望一次看到的块大小。
【解决方案4】:

如果列数/行长度是可变的,则如果不“读取”(即处理)之前文件的每个字符并计算行终止符,就不可能找到您想要的行。而在 python 中处理它们的最快方法是使用迭代。

至于大文件最快的方法,不知道这样逐行迭代是否更快:

with open(file_name) as f:
    for line,_ in zip(f, range(50)):
        pass
    lines = [line for line,_ in zip(f, range(10))]

...或使用seek 一次读取一个字符,并计算换行符。但是第一个肯定更方便。

但是,如果文件被大量读取,随着时间的推移,遍历行会变慢。如果文件内容没有改变,您可以改为通过读取整个内容并提前构建行长的dict 来完成此操作:

from itertools import accumulate
with open(file_name) as f:
    cum_lens = dict(enumerate(accumulate(len(line) for line in f), 1))

这将允许您查找文件中的任何行号,而无需再次处理整个内容:

def seek_line(path, line_num, cum_lens):
    with open(path) as f:
        f.seek(cum_lens[line_num], 0)
        return f.readline()

class LineX:
    """A file reading object that can quickly obtain any line number."""
    def __init__(self, path, cum_lens):
        self.cum_lens = cum_lens
        self.path = path
    def __getitem__(self, i):
        return seek_line(self.path, i, self.cum_lens)

linex = LineX(file_name, cum_lens)
line50 = linex[50]

但此时,您最好将文件内容加载到某种数据库中。我取决于你要做什么,以及文件包含什么样的数据。

【讨论】:

    【解决方案5】:

    就这么简单:

    with open("file.csv", "r") as file:
        print(file.readlines()[50:60])
    

    【讨论】:

    • 不,它不是,因为它首先读取整个文件,在读取整个文件之后,它返回行 [50:60]。这就是我不想要的 20gb 文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 2016-05-12
    • 2013-02-12
    相关资源
    最近更新 更多