1

上の「三角」行列を含むテキスト ファイルがあり、下の値は省略されています (以下に例を示します)。

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 のライブラリの他の最適化された関数)を適切に使用する方法はありますか?

4

1 に答える 1

2

ファイルから生データを文字列に読み取ってからnp.fromstring、行列の上三角部分の 1 次元配列を取得するために使用できます。

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

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

または、一度にファイルの 1 行を読み取るようにジェネレーターを定義して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)

これは、ファイルを 2 回開いて最初の行を読み取り、エントリの数を取得するため、少し作業が「無駄」になります。「改善」は、次のコードのように、最初の行を保存し、ファイルの残りの部分で反復子を使用してチェーンすることです。

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.]])
于 2015-11-12T16:13:06.240 に答える