Использование genfromtxt numpy для загрузки треугольной матрицы с python

У меня есть текстовый файл, содержащий верхнюю "треугольную" матрицу, нижние значения опущены (вот пример ниже):

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) на таких матрицах?

1 ответ

Решение

Вы можете прочитать необработанные данные из файла в строку, а затем использовать np.fromstring чтобы получить 1-й массив верхней треугольной части матрицы:

with open('data.txt') as data_file:
    data = data_file.read()

arr = np.fromstring(data, sep=' ')

Кроме того, вы можете определить генератор для чтения одной строки вашего файла за раз, а затем использовать np.fromiter прочитать 1-й массив из этого генератора:

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)

Если вы знаете размер матрицы (которую вы можете определить по первой строке файла), вы можете указать count ключевой аргумент np.fromiter так что функция будет предварительно выделять точно нужный объем памяти, что будет быстрее. Вот что делают эти функции:

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.])

Это компактное представление может быть всем, что вам нужно, но если вам нужна полная квадратная матрица, вы можете выделить нулевую матрицу правильного размера и скопировать arr в это с помощью np.triu_indices_fromили вы можете использовать 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.]])
Другие вопросы по тегам