NumPy读取文件,带有过滤线

Rom*_*lov 8 python numpy input large-files bigdata

我有一大堆用CSV文件编写的数字,只需加载该数组的一部分.从概念上讲,我想调用np.genfromtxt()然后对结果数组进行行切片,但是

  1. 该文件太大,可能无法容纳在RAM中
  2. 相关行的数量可能很小,因此无需解析每一行.

MATLAB具有textscan()可以获取文件描述符并只读取文件块的功能.在NumPy中有类似的东西吗?

现在,我定义了以下函数,它只读取满足给定条件的行:

def genfromtxt_cond(fname, cond=(lambda str: True)):
  res = []
  with open(fname) as file:
    for line in file:
      if cond(line):
        res.append([float(s) for s in line.split()])

  return np.array(res, dtype=np.float64)
Run Code Online (Sandbox Code Playgroud)

此解决方案有几个问题:

  • not general:仅支持float类型,同时genfromtxt检测类型,这些类型可能因列而异; 还缺少值,转换器,跳过等;
  • 效率不高:当条件困难时,每行可能被解析两次,使用的数据结构和读取缓冲也可能不是最理想的;
  • 需要编写代码.

是否存在实现过滤的标准函数,或MATLAB的一些对应函数textscan

The*_*eke 16

我可以想到两种方法可以提供您要求的一些功能:

  1. 要以块/ /或n行/等的步幅读取文件:
    您可以传递generatornumpy.genfromtxt以及numpy.loadtxt.这样,您可以高效地从文本文件中加载大型数据集,同时保留两个函数的所有方便的解析功能.

  2. 仅从符合可以表示为正则表达式的条件的行读取数据:
    您可以使用numpy.fromregex并使用a regular expression精确定义应加载输入文件中给定行的哪些标记.与模式不匹配的行将被忽略.

为了说明这两种方法,我将使用我的研究背景中的一个例子.
我经常需要加载具有以下结构的文件:

6
 generated by VMD
  CM         5.420501        3.880814        6.988216
  HM1        5.645992        2.839786        7.044024
  HM2        5.707437        4.336298        7.926170
  HM3        4.279596        4.059821        7.029471
  OD1        3.587806        6.069084        8.018103
  OD2        4.504519        4.977242        9.709150
6
 generated by VMD
  CM         5.421396        3.878586        6.989128
  HM1        5.639769        2.841884        7.045364
  HM2        5.707584        4.343513        7.928119
  HM3        4.277448        4.057222        7.022429
  OD1        3.588119        6.069086        8.017814
Run Code Online (Sandbox Code Playgroud)

这些文件可能很大(GB),我只对数值数据感兴趣.所有数据块都具有相同的大小 - 6在本例中 - 它们总是由两行分隔.所以stride块是8.

使用第一种方法:

首先,我将定义一个过滤掉不需要的行的生成器:

def filter_lines(f, stride):
    for i, line in enumerate(f):
        if i%stride and (i-1)%stride:
            yield line
Run Code Online (Sandbox Code Playgroud)

然后我打开文件,创建一个filter_lines-generator(这里我需要知道stride),并将该生成器传递给genfromtxt:

with open(fname) as f:
    data = np.genfromtxt(filter_lines(f, 8),
                         dtype='f',
                         usecols=(1, 2, 3))
Run Code Online (Sandbox Code Playgroud)

这很轻松.请注意,我可以用来usecols摆脱数据的第一列.以同样的方式,您可以使用所有其他功能genfromtxt- 检测类型,从列到列的不同类型,缺失值,转换器等.

在这个例子data.shape(204000, 3),原始文件由272000行组成.

这里generator用于过滤均匀跨步的线,但是同样可以想象它基于(简单)标准过滤掉不均匀的线块.

使用第二种方法:

这是regexp我要使用的:

regexp = r'\s+\w+' + r'\s+([-.0-9]+)' * 3 + r'\s*\n'
Run Code Online (Sandbox Code Playgroud)

组 - 即内部()- 定义要从给定线提取的标记.接下来,fromregex完成工作并忽略与模式不匹配的行:

data = np.fromregex(fname, regexp, dtype='f')
Run Code Online (Sandbox Code Playgroud)

结果与第一种方法完全相同.