如何有条件地将文件转换为2D numpy数组?

时间:2018-05-02 21:12:49

标签: python numpy

我有一个格式为

的文件
2,3: true
3,5: false
4,2: true

如何将true的行转换为2D numpy数组

[[2,3],[4,2]]

我尝试了numpy.genfromtxt,但是如何应用条件并将读取行限制为前两组数字?

2 个答案:

答案 0 :(得分:3)

几百万行的整数可能不会太大而无法一次加载到NumPy阵列中(取决于计算机的可用RAM)。因此,您可以通过首先加载完整的数值数组,然后加载布尔掩码来生成所需的数组:

import numpy as np
data = np.genfromtxt('data', delimiter=',', usecols=[0,1], comments=':', dtype=int)
mask = np.genfromtxt('data', delimiter=' ', usecols=[1], dtype=str) == 'true'
result = data[mask]

产量

array([[2, 3],
       [4, 2]])

我使用了两次调用np.genfromtxt来解决数据文件有两个不同分隔符(即逗号和空格)的问题。

虽然加载整个数组可能看起来很浪费,但它比逐行解析文件要快得多(前提是你有足够的内存来执行此操作。)

事实证明 - thanks to hpaulj是为了激励我进行测试 - 简单的for-loop要快得多:

例如,使用此设置:

import numpy as np

def make_data(N=10**6):
    data = np.random.randint(10, size=(N, 2))
    mask = np.array(['true', 'false'])[np.random.randint(2, size=N)]
    with open('data', 'w') as f:
        for row, maski in zip(data, mask):
            f.write('{},{}: {}\n'.format(row[0], row[1], maski))

def using_genfromtxt():
    data = np.genfromtxt('data', delimiter=',', usecols=[0,1], comments=':', dtype=int)
    mask = np.genfromtxt('data', delimiter=' ', usecols=[1], dtype=str) == 'true'
    result = data[mask]
    return result

def using_readline():
    """
    https://stackoverflow.com/a/50144016/190597 (hpaulj)
    """
    def foo1(f):
        for line in f:
            x,y = line.split(':')
            if y.strip()=='true':
                yield x.split(',')
    with open('data', 'r') as f:
        return np.array(list(foo1(f)), dtype=int)

make_data()

我们可以使用IPython来衡量using_genfromtxtusing_readline的速度:

In [152]: %timeit using_genfromtxt()
1 loop, best of 3: 8.8 s per loop

In [171]: %timeit using_readline()
1 loop, best of 3: 861 ms per loop

所以一个简单的for-loop实际上要快10倍。

答案 1 :(得分:2)

另一种处理混合分隔符和条件的方法是通过过滤函数传递文件。

定义一个生成器,它接受一个文件或任何在行上迭代的东西,并返回一组过滤的字符串:

def foo(f):
    for line in f:
        x,y = line.split(':')
        if y.strip()=='true':
            yield x

使用文本替换文件:

In [55]: txt='''2,3: true
    ...: 3,5: false
    ...: 4,2: true
    ...: 2,3: true
    ...: 3,5: false
    ...: 4,2: true'''

生成器返回字符串,如下所示:

In [56]: list(foo(txt.splitlines()))
Out[56]: ['2,3', '4,2', '2,3', '4,2']

genfromtxt很容易将这样的Feed转换为数组:

In [57]: np.genfromtxt(foo(txt.splitlines()),delimiter=',', dtype=int)
Out[57]: 
array([[2, 3],
       [4, 2],
       [2, 3],
       [4, 2]])

genfromtxt在Python中迭代一个文件,因此使用foo不应该改变它的速度。

pandas有一个很好的csv阅读器,但编译得更快的版本并没有像Python那样多的花里胡哨。

或者我可以完全跳过genfromtxt

def foo1(f):
    for line in f:
        x,y = line.split(':')
        if y.strip()=='true':
            yield x.split(',')

In [63]: np.array(list(foo1(txt.splitlines())), dtype=int)
Out[63]: 
array([[2, 3],
       [4, 2],
       [2, 3],
       [4, 2]])

选择行后,文件格式很简单,np.array可以处理它,并直接执行字符串到整数转换。