【问题标题】:Flags in PythonPython中的标志
【发布时间】:2010-11-06 16:53:41
【问题描述】:

我正在使用一个大矩阵(250x250x30 = 1,875,000 个单元格),我想要一种方法来为这个矩阵中的每个单元格设置任意数量的标志,以某种易于使用且合理节省空间的方式.

我最初的计划是一个 250x250x30 的列表数组,其中每个元素类似于:["FLAG1","FLAG8","FLAG12"]。然后我将其更改为仅存储整数:[1,8,12]。这些整数由 getter/setter 函数在内部映射到原始标志字符串。这仅使用 250mb,每点 8 个标志,这在内存方面很好。

我的问题是:我是否错过了另一种构建此类数据的明显方法?

感谢大家的建议。我最终将一些建议合二为一,遗憾的是我只能选择一个答案,不得不忍受对其他人的支持:

编辑:erm 我在这里的初始代码(使用集合作为 3d numpy 数组的基本元素)使用了大量内存。这个新版本在填充randint(0,2**1000) 时使用了大约 500mb。

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

【问题讨论】:

  • 你需要支持多少个标志?
  • 我不确定,我没有信心说出比“大于 5 且小于 500”更准确的话。

标签: python matrix numpy flags


【解决方案1】:

如果每个单元格都有一个标志,那么您的解决方案很好。但是,如果您使用的是稀疏数据集,其中只有一小部分单元格将带有标记,那么您真正想要的是字典。您可能希望设置字典,因此键是单元格位置的元组,值是您在解决方案中的标志列表。

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

编辑-固定键元组,感谢 Andre 和字典语法。

【讨论】:

  • 他使用的是三维矩阵,所以你需要这样说 dict((1,1,1)=[1,2,3]...
  • 其实不应该是{(1,1,1):[1,2,3], ...)。 dict(key=val) 语法仅在您的键是 python 标识符时才有效。
【解决方案2】:

我通常会使用 numpy 数组(可能是短整数,每个 2 字节,因为您可能需要超过 256 个不同的值)——对于

如果由于某种原因我无法承受 numpy 依赖项(例如,在不支持 numpy 的 App Engine 上),我会使用标准库 array 模块 - 它只支持一维数组,但是对于大型同构数组,它与 numpy 一样节省空间,并且您提到的 getter/setter 例程可以很好地“线性化”一个 3 项元组,该元组是您对一维数组的单个整数索引的自然索引。

一般来说,只要你有大量同质、密集的向量或数字矩阵,就可以考虑使用 numpy(或数组)——在这个用例中,Python 内置列表非常浪费空间(由于它们的通用性)在这里不使用也不需要!-),节省内存也间接转化为节省时间(更好的缓存,更少的间接级别等)。

【讨论】:

  • 感谢 numpy 的建议。关于数据类型的一个问题。由于 ushort 最多可以容纳 2**16 的值,如果我使用 ushort,我不能只有 16 个标志吗?
  • 对不起...我仍然在考虑之前的答案中的位标志。我明白你现在的意思了,一个 4d 数组,其中 a[0][0][0] 会正确返回一个 1d 数组吗?
  • 嗯,我以不同的方式阅读了您的问题,每个单元格只有一个标志(或没有)(最多 500 个不同的值),而不是单个单元格最多 500 个标志。对于您的实际问题,确实需要一个 4-D 数组(每个单元 512 位,64 字节),所以这是 128 MB(以及按位移位和掩码进行的转换,正如其他答案所提到的)-如果你确实需要像那样“密集”(如果 典型 单元格有一些标志,那么列表再次有吸引力,特别是在 numpy 中,你只需要在最低级别)。
  • 感谢这个想法。我最终使用了一个 3d numpy 集合数组(假设集合比列表更适合于此)。
  • 呃,套路很烂。我在问题中粘贴的示例代码使用了 python long 的魔力。感谢您提供 numpy 数组提示。
【解决方案3】:

您可以定义一些具有不同的两个值的幂的常量:

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

并将它们与布尔逻辑一起使用以将标志存储在一个整数中,p.e.:

flags = FLAG1 | FLAG8

要检查是否启用了标志,您可以使用& 运算符:

flag1_enabled = flags & FLAG1

如果启用该标志,则此表达式将返回一个非零值,在任何布尔运算中都将被评估为 True。如果该标志被禁用,则表达式将返回 0,即在布尔运算中被评估为 False。

【讨论】:

  • 即使我使用多达 100 个对象,这也为我节省了大约 20% 的所需内存,非常有趣!一个问题,如何删除标志?
  • 如果你真的可能有多达 500 个标志,这可能不是最好的方法,尽管 Python 确实支持任意长的整数。
  • 要删除一个标志,你必须添加它的否定(标志 &= ~FLAG1)
  • @Alex Jurkiewicz: new_flags = old_flags & ~flags_to_remove。
  • 感谢您的建议。虽然我将使用集合而不是位域(我不需要在这里节省额外的空间),但将来我肯定会需要这样的东西。
【解决方案4】:

考虑使用享元模式来共享单元格属性:

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

【讨论】:

    【解决方案5】:

    BitSet 是你想要的,因为它允许你只使用一个固定大小的整数(Int 类型)一次存储许多标志

    【讨论】:

      【解决方案6】:

      进一步采纳 Robbie 的建议...

      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__,使其更易于使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-15
        • 2012-07-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多