Загрузить разреженный массив из файла npy

#python #scipy #sparse-array

#python #сципи #разреженный массив

Вопрос:

Я пытаюсь загрузить разреженный массив, который я ранее сохранил. Сохранить разреженный массив было достаточно просто. Однако пытаться прочитать это — сплошная боль. scipy.load возвращает массив 0d вокруг моего разреженного массива.

 import scipy as sp
A = sp.load("my_array"); A
array(<325729x325729 sparse matrix of type '<type 'numpy.int8'>'
with 1497134 stored elements in Compressed Sparse Row format>, dtype=object)
  

Чтобы получить разреженную матрицу, я должен сгладить массив 0d или использовать sp.asarray(A). Это кажется действительно трудным способом что-то делать. Достаточно ли Scipy умен, чтобы понять, что он загрузил разреженный массив? Есть ли лучший способ загрузить разреженный массив?

Ответ №1:

Функции mmwrite/mmread выполняются в scipy.io может сохранять / загружать разреженные матрицы в формате Matrix Market.

 scipy.io.mmwrite('/tmp/my_array',x)
scipy.io.mmread('/tmp/my_array').tolil()    
  

mmwrite и mmread может быть, это все, что вам нужно. Он хорошо протестирован и использует хорошо известный формат.

Однако следующее может быть немного быстрее:

Мы можем сохранить координаты строк и столбцов и данные в виде одномерных массивов в формате npz.

 import random
import scipy.sparse as sparse
import scipy.io
import numpy as np

def save_sparse_matrix(filename,x):
    x_coo=x.tocoo()
    row=x_coo.row
    col=x_coo.col
    data=x_coo.data
    shape=x_coo.shape
    np.savez(filename,row=row,col=col,data=data,shape=shape)

def load_sparse_matrix(filename):
    y=np.load(filename)
    z=sparse.coo_matrix((y['data'],(y['row'],y['col'])),shape=y['shape'])
    return z

N=20000
x = sparse.lil_matrix( (N,N) )
for i in xrange(N):
    x[random.randint(0,N-1),random.randint(0,N-1)]=random.randint(1,100)

save_sparse_matrix('/tmp/my_array',x)
load_sparse_matrix('/tmp/my_array.npz').tolil()
  

Вот некоторый код, который предлагает сохранить разреженную матрицу в файле npz
может быть быстрее, чем использование mmwrite / mmread:

 def using_np_savez():    
    save_sparse_matrix('/tmp/my_array',x)
    return load_sparse_matrix('/tmp/my_array.npz').tolil()

def using_mm():
    scipy.io.mmwrite('/tmp/my_array',x)
    return scipy.io.mmread('/tmp/my_array').tolil()    

if __name__=='__main__':
    for func in (using_np_savez,using_mm):
        y=func()
        print(repr(y))
        assert(x.shape==y.shape)
        assert(x.dtype==y.dtype)
        assert(x.__class__==y.__class__)    
        assert(np.allclose(x.todense(),y.todense()))
  

выдает

 % python -mtimeit -s'import test' 'test.using_mm()'
10 loops, best of 3: 380 msec per loop

% python -mtimeit -s'import test' 'test.using_np_savez()'
10 loops, best of 3: 116 msec per loop
  

Комментарии:

1. 1, scipy.io является правильным решением. Я бы добавил, что если вы хотите пойти по пути оптимизации, вы могли бы рассмотреть numpy.load(mmap_mode='r'/'c') . Сопоставление файлов с диска обеспечивает мгновенную загрузку и может сэкономить память, поскольку один и тот же массив, сопоставленный с памятью, может использоваться совместно несколькими процессами.

2. scipy.io.savemat, вероятно, лучший

3. Использование np_savez вместо mm сократило время загрузки большой разреженной матрицы с 8 минут 47 до 3 секунд! Спасибо! Я также пробовал savez_compressed, но размер тот же, а время загрузки намного больше.

Ответ №2:

Можно извлечь объект, скрытый в массиве 0d, используя () в качестве индекса:

 A = sp.load("my_array")[()]
  

Это выглядит странно, но, похоже, все равно работает, и это очень короткое обходное решение.

Комментарии:

1. Я почти уверен, что вы также можете использовать .item (), но не цитируйте меня по этому поводу 🙂

Ответ №3:

Несмотря на все «за» mmwrite ответ, я удивлен, что никто не попытался ответить на сам вопрос. Но поскольку он был повторно активирован, я попробую.

Это воспроизводит случай OP:

 In [90]: x=sparse.csr_matrix(np.arange(10).reshape(2,5))
In [91]: np.save('save_sparse.npy',x)
In [92]: X=np.load('save_sparse.npy')
In [95]: X
Out[95]: 
array(<2x5 sparse matrix of type '<type 'numpy.int32'>'
    with 9 stored elements in Compressed Sparse Row format>, dtype=object)
In [96]: X[()].A
Out[96]: 
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

In [93]: X[()].A
Out[93]: 
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
In [94]: x
Out[94]: 
<2x5 sparse matrix of type '<type 'numpy.int32'>'
    with 9 stored elements in Compressed Sparse Row format
  

[()] То, что дал нам пользователь4713166, не является «трудным способом» извлечения разреженного массива.

np.save и np.load предназначены для работы с ndarrays. Но разреженная матрица не является таким массивом и не является подклассом (как np.matrix есть). Похоже, что np.save обертывает объект, не являющийся массивом, в object dtype array и сохраняет его вместе с обработанной формой объекта.

Когда я пытаюсь сохранить объект другого типа, который нельзя мариновать, я получаю сообщение об ошибке на:

 403  # We contain Python objects so we cannot write out the data directly.
404  # Instead, we will pickle it out with version 2 of the pickle protocol.
  

—> 405 pickle.дамп (массив, fp, протокол=2)

Итак, в ответ на Is Scipy smart enough to understand that it has loaded a sparse array? , нет. np.load не знает о разреженных массивах. Но np.save достаточно умен, чтобы уклоняться, когда дается что-то, что не является массивом, и np.load делает то, что может, с тем, что если найдет в файле.

Что касается альтернативных методов сохранения и загрузки разреженных массивов, io.savemat был упомянут метод, совместимый с MATLAB. Это был бы мой первый выбор. Но этот пример также показывает, что вы можете использовать обычный Python pickling . Это может быть лучше, если вам нужно сохранить определенный разреженный формат. И np.save неплохо, если вы можете смириться с [()] этапом извлечения. 🙂


https://github.com/scipy/scipy/blob/master/scipy/io/matlab/mio5.py
write_sparse — разреженные массивы сохраняются в csc формате. Вместе с заголовками он сохраняет A.indices.astype('i4')) , A.indptr.astype('i4')) A.data.real и необязательно A.data.imag .


В быстрых тестах я обнаружил, что он np.save/load обрабатывает все разреженные форматы, кроме dok , где load жалуется на отсутствие shape . В противном случае я не нахожу никакого специального кода травления в разреженных файлах.