Импортировать большой файл .tiff в виде разреженной матрицы

У меня есть большой файл .tiff (4,4 ГБ, значения 79530 x 54980) с 1 полосой. Поскольку действительны только 16% значений, я подумал, что лучше импортировать файл как разреженную матрицу для экономии оперативной памяти. Когда я впервые открываю его как np.array, а затем преобразовываю в разреженную матрицу с помощью csr_matrix(), мое ядро ​​уже падает. См. код ниже.

from osgeo import gdal
import numpy as np
from scipy.sparse import csr_matrix

ds = gdal.Open("file.tif")
band =  ds.GetRasterBand(1)
array = np.array(band.ReadAsArray())
csr_matrix(array)

Есть ли лучший способ работы с этим файлом? В конце концов, мне приходится производить расчеты на основе значений в растре. (К сожалению, из соображений конфиденциальности я не могу прикрепить соответствующий файл.)

Честно говоря, разреженность в 16%, вероятно, того не стоит — матрица CSR должна хранить два значения для каждого ненулевого значения. Вы экономите примерно 65 % памяти всего массива, но теряете большую часть удобства, связанного с плотным массивом, непрерывным в памяти. Если все значения 0-255, вы можете преобразовать плотный массив в np.uint8 (что составляет ~ 16% от размера стандартного np.int64)

CJR 18.03.2022 14:51

Насколько я знаю, в Scipy нет разреженных матриц, что приводит к значительно меньшему объему используемой памяти. Вы можете найти список здесь: docs.scipy.org/doc/scipy/reference/sparse.html . Единственные решения, которые я вижу, это: работа в сопоставленная память или работа по частям. Первый проще, но медленнее, чем второй.

Jérôme Richard 19.03.2022 18:38
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
73
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Предполагая, что вы знаете размер своей матрицы, вы можете создать пустую разреженную матрицу, а затем установить только допустимые значения одно за другим.

from osgeo import gdal
import numpy as np
from scipy.sparse import csr_matrix

ds = gdal.Open("file.tif")
band =  ds.GetRasterBand(1)
matrix_size = (1000, 1000) # set you size
matrix = csr_matrix(matrix_size)

# for each valid value
matrix[i, j] = your_value

Изменить 1

Если вы не знаете размер своей матрицы, вы сможете проверить это следующим образом:

from osgeo import gdal

ds = gdal.Open("file.tif")
width  = ds.GetRasterXSize()
height = ds.GetRasterYSize()
matrix_size = (width, height)

Редактировать 2

Измерил метрики, предложенные в комментариях (заполнил полностью). Вот как я измерял использование памяти.

размер 500x500

матрицапустой размерполный размервремя заполнения
csr_matrix28562992477,67 с
doc_matrix726358075783,15 с
lil_matrix884088400,54 с

размер 1000x1000

матрицапустой размерполный размервремя заполнения
csr_matrix485649927164,94 с
doc_matrix72615057885812,81 с
lil_matrix16840168402,19 с

Вероятно, лучшим решением было бы использовать lil_matrix.

Спасибо, но я не знаю размер моей разреженной матрицы

justin fiedler 18.03.2022 12:00

@justinfiedler, вы должны иметь возможность проверить размер своих данных. Проверить мои изменения

K.Mat 18.03.2022 12:29

Редактирование разреженной матрицы CSR должно быть безумно медленный, поскольку оно не предназначено для этого использования (особенно из-за низкой разреженности). Я думаю, что это должно занять больше оперативной памяти из-за того, что Scipy изменяет размер вектора nnz для каждого элемента (требуется двойное использование памяти CSR) и типа данных по умолчанию. Заполнение разреженной матрицы CSR 1000x1000 заняло на моей машине 3м30. Я ожидаю, что это займет несколько десятков часов для ввода OP. Матрицы LIL подходят для этого лучше, но использование памяти выше, так что в итоге это займет больше места и будет намного медленнее, чем плотная матрица...

Jérôme Richard 18.03.2022 12:53

Каждый раз, когда вы добавляете значение в матрицу CSR, вся существующая матрица копируется и все указатели вычисляются заново. Вы взяли O(N) проблему и решили ее O(2^N).

CJR 18.03.2022 14:38

Только @CJR O(N^2) который уже большой ;) .

Jérôme Richard 18.03.2022 18:51

Чтобы было ясно, lil_matrix может быть быстрым по сравнению с итеративным построением csr_matrix, но будет использовать больше памяти, чем массив numpy (при такой разреженности), и медленнее, чем просто использование этого массива numpy. Вы только что неправильно измерили размер объектов вложенного списка (что должно быть очевидно из того факта, что добавление к нему значений не меняет использование памяти).

CJR 19.03.2022 16:11

Как ты делаешь это? # for each valid value? csr_matrix использует np.nonzero для поиска ненулевых значений. Но если у вас уже есть массивы i, j, v для всех ненулевых значений, почему бы просто не создать массив, используя входные данные стиля coo, np.csr_matrix((v, (i,j))). Нет необходимости повторять, устанавливая одно значение за раз.

hpaulj 19.03.2022 18:15
Ответ принят как подходящий

Можете ли вы сказать, где происходит сбой?

band =  ds.GetRasterBand(1)
temp = band.ReadAsArray()
array = np.array(temp)    # if temp is already an array, you don't need this
csr_matrix(array)

Если array равно 4,4 ГБ, (79530, 54980)

In [62]: (79530 * 54980) / 1e9
Out[62]: 4.3725594    # 4.4gB makes sense for 1 byte/element
In [63]: (79530 * 54980) * 0.16        # 16% density
Out[63]: 699609504.0                # number of nonzero values

создание csr требует выполнения np.nonzero(array) для получения индексов. Это создаст 2 массива этого размера 0,7 * 8 ГБ (индексы - 8-байтовые целые числа). coo на самом деле требуются эти 2 массива плюс 0,7 для ненулевых значений — около 12 Гб. Преобразовав в csr, атрибут row уменьшится до 79530 элементов — примерно 7 Гб. (с поправкой на 8 байт/элемент)

Таким образом, при плотности 16% разреженный формат в лучшем случае все еще больше, чем плотная версия.

Ошибка памяти при преобразовании матрицы в разреженную матрицу, указанный dtype недействителен

это недавний случай ошибки памяти, которая произошла на шаге nonzero.

Ни 0,7Гб, ни 0,7Гб на индексы массива. Вы забыли умножить размер на 4 или 8, так как индексы закодированы в int32 (по умолчанию в Windows) или int64 (по умолчанию в Linux). Так что это не 2,1 Гб, а 6,3 Гб, что слишком много для ОП и вызывает сбой из-за нехватки памяти.

Jérôme Richard 19.03.2022 18:28

Другие вопросы по теме