Вопрос

Я работаю с большой матрицей (250x250x30 = 1,875,000 ячеек), и я хотел бы найти способ установить произвольное количество флагов для каждой ячейки в этой матрице каким-либо простым в использовании способом с разумной экономией места.

Моим первоначальным планом был массив списков размером 250x250x30, где каждый элемент был чем-то вроде: ["FLAG1","FLAG8","FLAG12"].Затем я изменил его на хранение только целых чисел вместо этого: [1,8,12].Эти целые числа внутренне сопоставляются функциями получения / установки с исходными строками флага.При этом используется всего 250 мбайт с 8 флагами на точку, что вполне нормально с точки зрения объема памяти.

Мой вопрос заключается в следующем:не упускаю ли я еще один очевидный способ структурирования такого рода данных?

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

Редактировать:erm исходный код, который у меня был здесь (с использованием наборов в качестве базового элемента 3d-массива numpy), занимал МНОГО памяти.Эта новая версия использует около 500 мб при заполнении randint(0,2**1000).

import numpy

FLAG1=2**0
FLAG2=2**1
FLAG3=2**2
FLAG4=2**3

(x,y,z) = (250,250,30)

array = numpy.zeros((x,y,z), dtype=object)


def setFlag(location,flag):
    array[location] |= flag
def unsetFlag(location,flag):
    array[location] &= ~flag
Это было полезно?

Решение

Обычно я бы использовал numpy массив (предположительно из коротких целых чисел, по 2 байта каждое, поскольку вам может потребоваться более 256 различных значений) - это заняло бы менее 4 МБ для <2 миллиона клеток.

Если бы по какой-то причине я не мог позволить себе зависимость от numpy (например, от App Engine, который не поддерживает numpy), я бы использовал стандартную библиотеку массив модуль - он поддерживает только одномерные массивы, но он так же экономичен по объему, как numpy для больших однородных массивов, а упомянутые вами процедуры получения / установки прекрасно могут "линеаризовать" кортеж из 3 элементов, который является вашим естественным индексом, в единый целочисленный индекс в одномерном массиве.

В общем, рассматривайте numpy (или array) всякий раз, когда у вас есть большие однородные, плотные векторы или матрицы чисел - встроенные списки Python в этом случае сильно расходуют пространство (из-за их общности, которую вы не используете и которая вам здесь не нужна!-), и экономия памяти косвенно также приводит к экономии времени (лучшее кэширование, меньшее количество уровней косвенности и т.д. и т.п.).

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

Ваше решение прекрасно, если каждая отдельная ячейка будет иметь флаг.Однако, если вы работаете с разреженным набором данных, где только небольшой подраздел ваших ячеек будет иметь флаги, то вам действительно нужен словарь.Вы бы хотели настроить диктонарий таким образом, чтобы ключ был кортежем для определения местоположения ячейки, а значение - списком флагов, как у вас в вашем решении.

allFlags = {(1,1,1):[1,2,3], (250,250,30):[4,5,6]}

Здесь у нас есть ячейка 1,1,1 с флагами 1,2 и 3, а ячейка 250,250,30 с флагами 4,5 и 6

редактировать - исправлены ключевые кортежи, спасибо Андре, и синтаксис словаря.

Вы можете определить некоторые константы с разной степенью двух значений как:

FLAG1 = 0x01
FLAG8 = 0x02
FLAG12 = 0x04
...

И используйте их с логической логикой для хранения флагов только в одном целом числе, например.:

flags = FLAG1 | FLAG8

Чтобы проверить, включен ли флаг, вы можете использовать & оператор:

flag1_enabled = flags & FLAG1

Если флаг включен, это выражение вернет ненулевое значение, которое будет оценено как True при любой логической операции.Если флаг отключен, выражение вернет 0, которое вычисляется как False в логических операциях.

Рассмотрите возможность использования шаблона Flyweight для совместного использования свойств ячейки:

http://en.wikipedia.org/wiki/Flyweight_pattern

Набор битов это то, что вы хотите, поскольку это позволяет вам хранить сразу много флагов, используя только целое число фиксированного размера (тип Int).

Развиваем предложение Робби еще на один шаг...

flags = set()
x, y, flag = 34, 201, 3
flags.add((x, y, flag)) # set flag 3 at position (34, 201)
if (3, 2, 1) in flags: # check if flag 1 is at position (3, 2)
    # do something
else:
    # do something else

Вы также можете создать вспомогательный класс.

class Flags(object):
    def __init__(self):
        self.data = set()
    def add(self, x, y, flag):
        self.data.add((x, y, flag))
    def remove(self, x, y, flag):
        self.data.remove((x, y, flag))
    def contains(self, x, y, flag):
        return (x, y, flag) in self.data

Вы также могли бы реализовать специальные методы Python, такие как __contains__ чтобы с ним было легче работать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top