Использование 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.]])