【问题标题】:Increasing speed of python list operations and comparisons in a custom table class提高自定义表类中 python 列表操作和比较的速度
【发布时间】:2011-01-20 23:12:30
【问题描述】:

我正在使用下面的类来创建一个表,我需要找到一种方法,不仅使它更快,而且与它的交互更快:

class Table(object):
    """a three dimensional table object"""
    def __init__(self, xsize=1, ysize=1, zsize=1):
        self.xsize = xsize
        self.ysize = ysize
        self.zsize = zsize
        self.data = [0] * (xsize * ysize * zsize)

    def __getitem__(self, key):
        x, y, z = self.__extractIndices(key)
        return self.data[x + self.xsize * (y + self.ysize * z)]

    def __setitem__(self, key, value):
        x, y, z = self.__extractIndices(key)
        self.data[x + self.xsize * (y + self.ysize * z)] = value

    def __extractIndices(self, key):
        x = y = z = 0
        if (self.ysize > 1):
            if (self.zsize > 1):
                if len(key) != 3:
                    raise IndexError
                else:
                    x, y, z = key
            elif len(key) != 2:
                raise IndexError
            else:
                x, y = key
        elif not isinstance(key, int):
            raise IndexError
        else:
            x = key
        return (x, y, z)

    def resize(self, xsize=1, ysize=1, zsize=1):
        """resize the table preserving data"""
        oldlist = list(self.data)
        self.data = [0] * (xsize * ysize * zsize)
        self.xsize = xsize
        self.ysize = ysize
        self.zsize = zsize
        for i in range(0, oldlist):
            self.data[1] = oldlist[i]

在这一点上,我需要确定两个列表中的数据是否等同于每个 z 所以我这样做了。 self.dataself.map.data 是上面的表类实例

    for x in range(self.map.width - 1):
        for y in range(self.map.height - 1):
            tempflag = False
            #layer 1
            if self.data[x, y, 0] != self.map.data[x, y, 0]:
                tempflag = True
                layer1flag = True
            #layer 2
            if self.data[x, y, 1] != self.map.data[x, y, 1]:
                tempflag = True
                layer2flag = True
            #layer 3
            if self.data[x, y, 2] != self.map.data[x, y, 2]:
                tempflag = True
                layer3flag = True
            #copy the data if it changed
            if tempflag:
                self.data = copy.deepcopy(self.map.data)
                previewflag = True

显然,这是我可以想象的最慢的方法,并且考虑到我正在比较的其中一些表的大小为 200 * 200 * 3 = 120,000 个条目。我需要尽可能快。

我考虑过重写上面的比较,以像这样对一个 z 的所有条目进行切片

tempflag = False
#layer 1
slicepoint1 = 0
slicepoint2 = self.data.xsize * self.data.ysize * 1
data1 = self.data.data[slicepoint1:slicepoint2]
data2 = self.map.data.data[slicepoint1:slicepoint2]
if data1 != data2:
    tempflag = True
    layer1flag = True
#layer 2
slicepoint1 = self.data.xsize * self.data.ysize * 1
slicepoint2 = self.data.xsize * self.data.ysize * 2
data1 = self.data.data[slicepoint1:slicepoint2]
data2 = self.map.data.data[slicepoint1:slicepoint2]
if data1 != data2:
    tempflag = True
    layer2flag = True
#layer 3
slicepoint1 = self.data.xsize * self.data.ysize * 2
slicepoint2 = self.data.xsize * self.data.ysize * 3
data1 = self.data.data[slicepoint1:slicepoint2]
data2 = self.map.data.data[slicepoint1:slicepoint2]
if data1 != data2:
    tempflag = True
    layer3flag = True
#copy the data if it changed
if tempflag:
    self.data = copy.deepcopy(self.map.data)
    previewflag = True

虽然这看起来会更快,但它似乎仍然可以显着改善。例如不能使用 numpy 在 Table 类中构建数据列表吗?

我需要这个类和这个检查尽可能快地运行

如果使用 numpy 可以让我非常快速地遍历表,这样我就可以使用其中的数据进行 blit 操作来构建 tilemap,这也很好

确实需要保留表格类的通用接口,特别是表格数据存储在self.data中的事实

总结 使用numpy可以提高运算速度吗?如果可以,我该怎么做?

【问题讨论】:

  • 这看起来是一种非常缓慢且笨拙的 3D 数据存储方式。即使在 Python 中使用嵌套列表也可能更快更简单,但 numpy 数组可能是更好的答案。

标签: python arrays numpy


【解决方案1】:

我认为是的,通过使用 numpy,您可能会获得很多速度。

您不仅可以制作切片,还可以制作矩形切片,可能还可以制作立方体切片,例如:

>>> a = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> a[:2,1:]
array([[2, 3],
       [5, 6]])

我不确定您想要完成什么,但您也可以轻松地比较 numpy 数组元素:

>>> numpy.array([1,2,3])==numpy.array([9,2,3])
array([False,  True,  True], dtype=bool)

如果您有更多问题,请随时发表评论。

【讨论】:

    【解决方案2】:

    这绝对是 NumPy 的应用程序!它不仅会加快您的代码速度,还会大大简化您的代码,因为索引和比较已经由 NumPy 处理。您必须阅读一些教程来学习 NumPy——在这种情况下,您只需提供一些提示即可。

    通常,我会简单地从numpy.ndarray 派生来定义一个自定义数组类,但你说你肯定需要data 属性,它与numpy.ndarray.data 冲突。您的课程简化为

    class Table(object):
        def __init__(self, xsize=1, ysize=1, zsize=1):
            self.data = numpy.zeros((xsize, ysize, zsize))
    
        def __getitem__(self, key):
            return self.data[key]
    
        def __setitem__(self, key, value):
            self.data[key] = value
    
        def resize(self, xsize=1, ysize=1, zsize=1):
            # This only works for increasing the size of the data,
            # but is easy do adapt to other cases
            newdata = numpy.zeros((xsize, ysize, zsize))
            shape = self.data.shape
            newdata[:shape[0], :shape[1], :shape[2]] = self.data
            self.data = newdata
    

    您的比较代码简化为

    eq = self.data == self.data.map
    layerflags = eq.reshape(-1, 3).any(axis=0)
    if layerflags.any():
        self.data[:] = self.map.data
    

    而且它也会快很多

    【讨论】:

    • 也许我遗漏了一些东西,该比较如何返回一个可以调用 reshape 的对象?您能否将我链接到描述此操作的参考资料?这将帮助我更好地理解您的回答。谢谢。我目前正在阅读 Numpy 文档,所以也许我会在您回复之前找到它,如果我这样做了,我将编辑此评论
    • @Ryex:NumPy 中的所有操作都是矢量化的,即按元素执行。两个数组上的== 会返回一个布尔数组,表示两个数组的哪些元素比较相等。
    • 现在这很酷。作为一个快速的侧面问题,有没有办法将我的表类版本中的相同格式的列表转换为一个 numpy 数组,其中 [0,0,0] 索引操作将返回相同的值?反之亦然?这两个操作只需要在很长一段时间内以导入和导出方法执行一次,因此虽然速度会比较好,但这里实际上并不需要。
    • 我尝试了b = numpy.array(a.data),然后是b.shape = (a.xsize, a.ysize, a.zsize),但结果没有返回正确的索引
    • 从列表到数组:numpy.array(some_list),其他方式:some_array.tolist()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 2020-02-05
    • 1970-01-01
    • 2018-11-12
    • 2016-05-08
    相关资源
    最近更新 更多