【问题标题】:Using numpy's genfromtxt to load a triangular matrix with python使用numpy的genfromtxt用python加载三角矩阵
【发布时间】:2023-03-08 05:54:02
【问题描述】:

我有一个包含上部“三角形”矩阵的文本文件,下部值被省略(下面是一个示例):

3 5 3 5 1 8 1 6 5 8

5 8 1 1 6 2 9 6 4

2 0 5 2 1 0 0 3

2 2 5 1 0 1 0

1 3 6 3 6 1

4 2 4 3 7

4 0 0 1

0 1 8

2 1

1

由于有问题的文件大小约为 10000 行,我想知道是否有一种“智能”方法可以从中生成 numpy 矩阵,例如使用genfromtxt 函数。但是,直接使用它会在以下行引发错误 Line #12431 (got 6 columns instead of 12437) 和使用 filling_values 将不起作用,因为无法指定无缺失值占位符。

现在我不得不求助于手动打开和关闭文件:

import numpy as np
def load_updiag(filename, size):
    output = np.zeros((size,size))
    line_count = 0
    for line in f:
        data = line.split()
        output[line_count,line_count:size]= data
        line_count += 1
    return output

我觉得对于大文件大小可能不是很有可扩展性。 有没有办法在此类矩阵上正确使用 genfromtxt(或 numpy 库中的任何其他优化函数)?

【问题讨论】:

    标签: python arrays numpy matrix triangular


    【解决方案1】:

    可以将文件中的原始数据读入字符串,然后使用np.fromstring得到矩阵上三角部分的一维数组:

    with open('data.txt') as data_file:
        data = data_file.read()
    
    arr = np.fromstring(data, sep=' ')
    

    或者,您可以定义一个生成器来一次读取一行文件,然后使用np.fromiter 从该生成器中读取一维数组:

    def iter_data(path):
        with open(path) as data_file:
            for line in data_file:
                yield from line.split()
    
    arr = np.fromiter(iter_data('data.txt'), int)
    

    如果您知道矩阵的大小(可以从文件的第一行确定),您可以指定np.fromitercount 关键字参数,以便该函数将预先分配正确的数量内存,这将更快。这就是这些函数的作用:

    def iter_data(fileobj):
        for line in fileobj:
            yield from line.split()
    
    def read_triangular_array(path):
        with open(path) as fileobj:
            n = len(fileobj.readline().split())
    
        count = int(n*(n+1)/2)
    
        with open(path) as fileobj:
            return np.fromiter(iter_data(fileobj), int, count=count)
    

    这“浪费”了一些工作,因为它打开文件两次以读取第一行并获取条目数。 “改进”是保存第一行并将其与迭代器链接到文件的其余部分,如以下代码所示:

    from itertools import chain
    
    def iter_data(fileobj):
        for line in fileobj:
            yield from line.split()
    
    def read_triangular_array(path):
        with open(path) as fileobj:
            first = fileobj.readline().split()
            n = len(first)
            count = int(n*(n+1)/2)
            data = chain(first, iter_data(fileobj))
            return np.fromiter(data, int, count=count)
    

    所有这些方法都会产生

    >>> arr
    array([ 3.,  5.,  3.,  5.,  1.,  8.,  1.,  6.,  5.,  8.,  5.,  8.,  1.,
            1.,  6.,  2.,  9.,  6.,  4.,  2.,  0.,  5.,  2.,  1.,  0.,  0.,
            3.,  2.,  2.,  5.,  1.,  0.,  1.,  0.,  1.,  3.,  6.,  3.,  6.,
            1.,  4.,  2.,  4.,  3.,  7.,  4.,  0.,  0.,  1.,  0.,  1.,  8.,
            2.,  1.,  1.])
    

    这种紧凑的表示可能是您所需要的,但如果您想要完整的方阵,您可以分配一个大小合适的零矩阵并使用np.triu_indices_fromarr 复制到其中,或者您可以使用scipy.spatial.distance.squareform

    >>> from scipy.spatial.distance import squareform
    >>> squareform(arr)
    array([[ 0.,  3.,  5.,  3.,  5.,  1.,  8.,  1.,  6.,  5.,  8.],
           [ 3.,  0.,  5.,  8.,  1.,  1.,  6.,  2.,  9.,  6.,  4.],
           [ 5.,  5.,  0.,  2.,  0.,  5.,  2.,  1.,  0.,  0.,  3.],
           [ 3.,  8.,  2.,  0.,  2.,  2.,  5.,  1.,  0.,  1.,  0.],
           [ 5.,  1.,  0.,  2.,  0.,  1.,  3.,  6.,  3.,  6.,  1.],
           [ 1.,  1.,  5.,  2.,  1.,  0.,  4.,  2.,  4.,  3.,  7.],
           [ 8.,  6.,  2.,  5.,  3.,  4.,  0.,  4.,  0.,  0.,  1.],
           [ 1.,  2.,  1.,  1.,  6.,  2.,  4.,  0.,  0.,  1.,  8.],
           [ 6.,  9.,  0.,  0.,  3.,  4.,  0.,  0.,  0.,  2.,  1.],
           [ 5.,  6.,  0.,  1.,  6.,  3.,  0.,  1.,  2.,  0.,  1.],
           [ 8.,  4.,  3.,  0.,  1.,  7.,  1.,  8.,  1.,  1.,  0.]])
    

    【讨论】:

    • 这很有趣。如果可能的话,我宁愿避免使用像read()readlines() 这样的函数,因为它们对于大型矩阵来说可能很难记忆。如果我找不到任何其他解决方案,我会将您的问题标记为已回答。
    • @skcidereves 当然,我添加了一个使用np.fromiter 的解决方案,并且不使用read() 读取完整文件。请注意,任何性质的所有解决方案都将采用 O(n^2) 仅仅是因为矩阵具有 O(n^2) 条目,并且它们最终必须存储在一个 numpy 数组中。但是这个使用np.fromiter 的解决方案将内存需求大致减半。
    • 谢谢!我特别感谢紧凑的表示作为奖励。
    猜你喜欢
    • 2019-09-21
    • 2011-08-27
    • 1970-01-01
    • 2010-12-28
    • 2012-02-12
    • 1970-01-01
    • 2011-08-16
    • 2017-04-16
    • 2011-06-16
    相关资源
    最近更新 更多