2009-06-29 10 views
4

Je travaille avec une grande matrice (250x250x30 = 1 875 000 cellules), et j'aimerais trouver un moyen de définir un nombre arbitraire d'indicateurs pour chaque cellule de cette matrice, d'une manière simple et facile à utiliser. raisonnablement efficace dans l'espace.Drapeaux en Python

Mon plan d'origine était une liste de 250x250x30, où chaque élément était quelque chose comme: ["FLAG1","FLAG8","FLAG12"]. Je l'ai ensuite changé pour stocker seulement des entiers à la place: [1,8,12]. Ces entiers sont mappés en interne par les fonctions getter/setter aux chaînes de l'indicateur d'origine. Cela utilise seulement 250 Mo avec 8 drapeaux par point, ce qui est bien en termes de mémoire.

Ma question est la suivante: est-ce que je manque un autre moyen évident de structurer ce genre de données?

Merci à tous pour vos suggestions. J'ai fini par faire quelques suggestions en une, malheureusement je ne peux choisir qu'une réponse et je dois vivre avec les autres:

EDIT: erm le code initial que j'avais ici (en utilisant des ensembles comme élément de base d'un numpy 3d array) utilisé BEAUCOUP de mémoire. Cette nouvelle version utilise environ 500 Mo lorsqu'elle est remplie de 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

Combien de drapeaux avez-vous besoin de prendre en charge? – FogleBird

+0

Je ne suis pas sûr, je ne serais pas confiant en disant quelque chose de plus précis que "plus de 5 et moins de 500". –

Répondre

4

J'utiliserais généralement un tableau numpy (vraisemblablement des nombres courts, 2 octets chacun, puisque vous pourriez avoir besoin de plus de 256 valeurs distinctes) - cela prendrait moins de 4 Mo pour les 2 millions de cellules <.Si pour une raison quelconque je ne pouvais pas me permettre la dépendance numpy (par exemple sur App Engine, qui ne supporte pas numpy), j'utiliserais le module de la bibliothèque standard array - il ne supporte que les tableaux 1 dimension, mais il est aussi efficace que numpy pour les grands tableaux homogènes, et les routines getter/setter que vous mentionnez peuvent parfaitement "linéariser" un tuple de 3 items qui est votre index naturel dans l'index simple entier dans le tableau 1D.

En général, considérons numpy (ou tableau) chaque fois que vous avez de grands vecteurs ou matrices de nombres homogènes et denses - les listes intégrées Python sont très gaspilleuses d'espace dans ce cas d'utilisation (en raison de leur généralité). N'utilisez pas et n'avez pas besoin ici! -), et économiser de la mémoire se traduit indirectement par un gain de temps (meilleure mise en cache, moins de niveaux d'indirection, etc.).

+0

Merci pour la suggestion numpy. Une question sur le type de données. Depuis un ushort peut contenir des valeurs jusqu'à 2 ** 16, je ne peux pas avoir 16 drapeaux si j'utilise ushort? –

+0

Désolé ... Je pensais toujours à bitflags comme dans une réponse précédente. Je vois ce que vous voulez dire maintenant, un tableau 4d, où un [0] [0] [0] retournerait un tableau 1d correct? –

+0

Hmm, j'avais lu votre question différemment, avec juste un seul drapeau (ou aucun) par cellule (avec jusqu'à 500 valeurs différentes), par opposition à jusqu'à 500 drapeaux pour une seule cellule. Pour votre question actuelle, un tableau 4-D (avec 512 bits par cellule, 64 octets) est en effet nécessaire, soit 128 Mo (et la traduction de/par bit et masque, comme d'autres réponses l'ont mentionné) - * SI * vous devez être aussi "dense" que cela (si la cellule _typical_ a quelques drapeaux, alors les listes sont à nouveau attrayantes, surtout en numpy où vous n'avez besoin que de listes au niveau le plus bas). –

1

BitSet est ce que vous voulez, car il vous permet de stocker beaucoup de drapeaux à la fois en utilisant seulement un entier de taille fixe (type Int)

7

Votre solution est bien si chaque cellule va avoir un drapeau. Cependant, si vous travaillez avec un jeu de données épars où seulement une petite partie de vos cellules aura des drapeaux, ce que vous voulez vraiment est un dictionnaire. Vous souhaitez configurer le dictonary de sorte que la clé est un tuple pour l'emplacement de la cellule et la valeur est une liste de drapeaux comme vous avez dans votre solution.

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

Ici, nous avons la cellule 1,1,1 ont les drapeaux 1,2 et 3 et la cellule 250,250,30 ont les drapeaux 4,5, et 6

Édition- tuples clés fixes , merci André, et la syntaxe du dictionnaire.

+0

Il utilise une matrice tridimensionnelle, donc vous auriez besoin de dire quelque chose comme dict ((1,1,1) = [1,2,3] ... –

+0

En fait, cela ne devrait-il pas être {(1,1, 1): [1,2,3], ...). La syntaxe dict (key = val) ne fonctionne que si vos clés sont des identifiants python. – Brian

5

Vous pouvez définir des constantes avec différentes, puissance de deux valeurs:

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

et de les utiliser avec la logique booléenne stocker les drapeaux en un seul entier, pe:

flags = FLAG1 | FLAG8 

Pour vérifier si un drapeau est activé, vous pouvez utiliser l'opérateur &:

flag1_enabled = flags & FLAG1 

Si le drapeau est activé, cette expression renvoie une valeur non nulle, qui sera évalué comme vrai dans toute opération booléenne . Si l'indicateur est désactivé, l'expression renverra 0, évalué comme Faux dans les opérations booléennes.

+0

Cela me permet d'économiser environ 20% de la mémoire nécessaire, même si j'utilise jusqu'à 100 objets, ce qui est très intéressant! Une question, comment puis-je retirer un drapeau? –

+1

Peut-être pas la meilleure approche si vous avez vraiment jusqu'à 500 drapeaux, bien que Python supporte des entiers arbitrairement longs. – FogleBird

+1

Pour enlever un drapeau, vous devez ajouter sa négation (flags & = ~ FLAG1) –

1

Prenant la suggestion de Robbie un peu plus loin ...

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 

Vous pouvez également créer une classe d'aide.

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 

Vous pouvez également mettre en œuvre des méthodes spéciales de Python comme __contains__ pour le rendre plus facile à travailler.