2009-06-29 3 views
4

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

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

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

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

EDIT: 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 
+0

Сколько флагов вам нужно поддержать? – FogleBird

+0

Я не уверен, я бы не стал уверенно говорить что-то более точное, чем «более 5 и менее 500». –

ответ

4

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

Если по какой-то причине я не мог позволить себе зависимость numpy (например, от App Engine, которая не поддерживает numpy), я бы использовал стандартный библиотечный модуль array - он поддерживает только одномерные массивы, но это так же, как пространственно-эффективный, как numpy для больших однородных массивов, и описанные вами процедуры getter/setter вполне могут «линеаризовать» кортеж из 3 предметов, который является вашим естественным индексом, в единый целочисленный индекс в 1-мерный массив.

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

+0

Спасибо за предложение numpy. Один вопрос о типе данных. Так как пользователь может содержать значения до 2 ** 16, не могу ли я иметь только 16 флагов, если я использую ushort? –

+0

Извините ... Я все еще думал о битах, как в предыдущем ответе. Я понимаю, что вы имеете в виду сейчас, 4d-массив, где [0] [0] [0] вернет 1d-массив правильно? –

+0

Хмм, я прочитал ваш вопрос по-другому, только с одним флагом (или без него) на ячейку (до 500 различных значений), а не до 500 флагов для одной ячейки. Для вашего фактического вопроса действительно нужен 4-D массив (с 512 бит на ячейку, 64 байта), так что это 128 МБ (и перевод в/из бит по смене и маске, как упоминались другие ответы) - * IF * вам нужно быть таким «плотным», как это (если в ячейке _typical_ есть несколько флагов, списки снова привлекательны, особенно в numpy, где вам нужны только списки на самом низком уровне). –

1

BitSet является то, что вы хотите, так как он позволяет хранить множество флагов одновременно, используя только целое фиксированного размера (тип Int)

7

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

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

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

+0

Он использует трехмерную матрицу, поэтому вам нужно было бы сказать что-то вроде dict ((1,1,1) = [1,2,3] ... –

+0

На самом деле, не должно быть {{1,1, 1): [1,2,3], ...). Синтаксис dict (key = val) работает только в том случае, если ваши ключи являются идентификаторами python. – Brian

5

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

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

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

flags = FLAG1 | FLAG8 

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

flag1_enabled = flags & FLAG1 

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

+0

Это экономит около 20% необходимой памяти, даже если я использую до 100 объектов, очень интересно! Один вопрос, как удалить флаг? –

+1

Возможно, это не лучший подход, если у вас действительно может быть до 500 флагов, хотя Python поддерживает сколь угодно длинные целые числа. – FogleBird

+1

Чтобы удалить флаг, вы должны добавить его отрицание (flags & = ~ FLAG1) –

1

Принимая Робби рекомендации еще один шаг ...

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__, чтобы сделать его легче работать.

Смежные вопросы