【问题标题】:Replacing 3 lists with 2 generators用 2 个生成器替换 3 个列表
【发布时间】:2012-07-21 02:10:27
【问题描述】:

我想使用生成器优化我的应用程序,而不是创建 3 个列表,我想使用 2 个生成器。这是我的应用在当前版本中的简短方案:

1) 从二进制文件加载数据 -> 第一个列表

self.stream_data = [ struct.unpack(">H", data_file.read(2))[0] for foo in
                       xrange(self.columns*self.rows) ]

2) 创建所谓的非零抑制数据(所有数据为零)-> 第二个列表

self.NZS_data = list()
for row in xrange(self.rows):
    self.NZS_data.append( [ self.stream_data[column + row * self.rows ] 
                          for column in xrange(self.columns) ] )

3) 创建零抑制数据(坐标不为零)-> 第三个列表

self.ZS_data = list()
for row in xrange(self.rows):
    for column in xrange(self.columns):
        if self.NZS_data[row][column]:
            self.ZS_data.append( [ column, row, self.NZS_data[row][column] ] )

(我知道这可以使用 itertools.product 压缩到单个列表理解中)

4) 将 ZS_data 列表保存到文件中。

我使用了 Python 的 cProfiler,大部分时间(除了读取和解包)都用于创建这两个(NZS_data 和 ZS_data)列表。因为我只需要它们来将数据保存到我一直在考虑使用 2 个生成器的文件中:

1) 创建一个用于读取文件的生成器 -> 第一个生成器

self.stream_data = ( struct.unpack(">H", data_file.read(2))[0] for foo in
                       xrange(self.columns*self.rows) )

2) 创建 ZS_data 生成器(我真的不需要这个 NZS 数据)

self.ZS_data = ( [column, row, self.stream_data.next()]
                 for row, column in itertools.product(xrange(self.rows),
                 xrange(self.columns))
                 if self.stream_data.next() )

这当然不能正常工作,因为我从生成器中得到了两个不同的值。

3) 使用生成器将数据保存到文件中。

我想知道如何做到这一点。 也许您有其他与此应用程序可能的优化相关的想法?

添加
基于生成器的解决方案:

def create_ZS_data(self):
    self.ZS_data = ( [column, row, self.stream_data[column + row * self.rows ]]
                     for row, column in itertools.product(xrange(self.rows), xrange(self.columns))
                     if self.stream_data[column + row * self.rows ] )

分析器信息:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     3257    1.117    0.000   71.598    0.022 decode_from_merlin.py:302(create_ZS_file)
   463419   67.705    0.000   67.705    0.000 decode_from_merlin.py:86(<genexpr>)

乔恩的解决方案:

create_ZS_data(self):
    self.ZS_data = list()
    for rowno, cols in enumerate(self.stream_data[i:i+self.columns] for i in xrange(0, len(self.stream_data), self.columns)):
        for colno, col in enumerate(cols):
            # col == value, (rowno, colno) = index
            if col:
                self.ZS_data.append([colno, rowno, col])


探查器信息:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     3257   18.616    0.006   19.919    0.006 decode_from_merlin.py:83(create_ZS_data)

【问题讨论】:

  • 您是否考虑过为此使用numpy
  • 你的意思是用dtype/fromfile代替struct吗?
  • 是的 - 然后 reshape 它到行/列 - 然后迭代 izip(*a.nonzero()) 这将为您提供 (row, col) 元组,使用它来获取值,然后写出你想要的任何东西。
  • 只记得np.transpose(np.nonzero(a))izip(*a.nonzero())
  • 好主意,我一定会试一试,但是我刚刚检查过,我们的生产服务器上没有 numpy。

标签: python list optimization generator


【解决方案1】:

你可以让解包更有效率...

self.data_stream = struct.unpack_from('>{}H'.format(self.rows*self.columns), data_file)

将循环减少为:

for rowno, cols in enumerate(self.data_stream[i:i+self.columns] for i in xrange(0, len(self.data_stream), self.columns)):
    for colno, col in enumerate(cols):
        # col == value, (rowno, colno) = index
        if col == 0:
            pass # do something
        else:
            pass # do something else

注意 - 未经测试

【讨论】:

  • 哇,我很震惊......你的解决方案比我的快 10 倍。首先,使用 unpack_from 更快,我明白为什么没有迭代(在我的情况下快 3 倍)。但我无法理解为什么你的循环会有这样的改进。你能解释一下那里发生了什么吗?
  • @Wysek unpack_from 返回tuple 并保留为tuple,而不是按照原件构建list。数据只循环一次,对元组的一部分进行切片是一种快速操作。对于每一行/列,您有条件地检查它是否为零/非零/其他,并做适当的事情——中间列表没有附加元素,然后大概循环遍历那些 again...
  • 很抱歉打扰你,但我只是好奇,想学习一些新东西:) 我添加了两段代码,你的快了近 4 倍。他们使用相同的列表 self.stream_data 而我的完全基于生成器。魔法在哪里?非常感谢您的解决方案和回答。
  • @Wysek listcomp/genexp 总是有一点开销,但不是 4x - 你确定你的时间安排吗?
  • 我刚刚从分析器中添加了几行。我仍然不知道为什么您的代码如此之快。我唯一的猜测是多次调用生成器(463419)可能会导致开销,但我不希望它会这么大。
猜你喜欢
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-22
相关资源
最近更新 更多