【问题标题】:Split of numpy array into unequal chunks将numpy数组拆分为不相等的块
【发布时间】:2015-08-28 23:27:17
【问题描述】:

在我的程序中,我用元素填充了一个大的numpy 数组,其中的数量我事先不知道。由于每次向 numpy 数组添加单个元素效率低下,我将其大小增加了长度为 10000 的块,初始化为零。这导致最终我有一个带有零尾的数组。我想要的是数组,它的长度恰好是有意义元素的数量(因为后来我无法将垃圾零与具有零值的实际数据点区分开来)。然而,直接复制切片会使 RAM 消耗翻倍,这确实是不可取的,因为我的数组非常大。我查看了numpy.split 函数,但它们似乎只将数组拆分为大小相等的块,这当然不适合我。

我用下面的代码说明问题:

import numpy, os, random

def check_memory(mode_peak = True, mark = ''):
    """Function for measuring the memory consumption (Linux only)"""
    pid = os.getpid()
    with open('/proc/{}/status'.format(pid), 'r') as ifile:
        for line in ifile:
            if line.startswith('VmPeak' if mode_peak else 'VmSize'):
                memory = line[: -1].split(':')[1].strip().split()[0]
                memory = int(memory) / (1024 * 1024)
                break
    mode_str = 'Peak' if mode_peak else 'Current'
    print('{}{} RAM consumption: {:.3f} GB'.format(mark, mode_str, memory))

def generate_element():
    """Test element generator"""
    for i in range(12345678):
        yield numpy.array(random.randrange(0, 1000), dtype = 'i4')

check_memory(mode_peak = False, mark = '#1 ')
a = numpy.zeros(10000, dtype = 'i4')
i = 0
for element in generate_element():
    if i == len(a):
        a = numpy.concatenate((a, numpy.zeros(10000, dtype = 'i4')))
    a[i] = element
    i += 1
check_memory(mode_peak = False, mark = '#2 ')
a = a[: i]
check_memory(mode_peak = False, mark = '#3 ')
check_memory(mode_peak = True, mark = '#4 ')

这个输出:

#1 Current RAM consumption: 0.070 GB
#2 Current RAM consumption: 0.118 GB
#3 Current RAM consumption: 0.118 GB
#4 Peak RAM consumption: 0.164 GB

谁能帮我找到一个不会严重影响运行时间或 RAM 消耗的解决方案?

编辑:

我尝试使用

a = numpy.delete(a, numpy.s_[i: ])

还有

a = numpy.split(a, (i, ))[0]

但是同样会导致内存消耗翻倍

【问题讨论】:

  • 相对于内存,速度可能对您来说并不重要,但我不知道如何测试我的系统(mac os x)上的内存消耗。无论如何,我构建一个列表然后在最后转换为数组的速度大约快 2 倍。对我来说最快的(虽然我不知道它是如何实现的)是np.fromiter,但我认为你的生成器只是用于测试而不是你实际使用的。此外,如果您生成标量而不是数组(如您的 element),那当然会快得多,除非每个 element 在您的用例中实际上都有一些长度。
  • @askewchan 在我的例子中,array 生成是大程序中的一个步骤,它只贡献了总运行时间的一小部分,因此速度并不重要。另一方面,这一步是内存瓶颈。而生成器当然要复杂得多,并且涉及从网络接收数据。

标签: python arrays numpy memory-management


【解决方案1】:

numpy.split 不必将数组拆分为大小相等的块。如果您使用indices_or_sections 参数,您可以给出一个整数列表,它将用于拆分数组。例如:

>>> x = np.arange(8.0)
>>> np.split(x, [3, 5, 6, 10])
[array([ 0.,  1.,  2.]),   # x[:3]
 array([ 3.,  4.]),        # x[3:5]
 array([ 5.]),             # x[5:6]
 array([ 6.,  7.]),        # x[6:10]
 array([], dtype=float64)] # x[10:]

【讨论】:

  • 我的错误是我没有正确理解numpy.split 的语法。但是,这会导致与切片相同的双倍 RAM 消耗:a = numpy.split(a, (i,))[0]
【解决方案2】:

我终于想通了。事实上,额外的内存不仅在修剪阶段被消耗,而且在连接期间也被消耗。因此,在#2 输出点引入峰值内存检查:

#2 Peak RAM consumption: 0.164 GB

但是,有 resize() 方法,它可以就地更改数组的大小/形状:

check_memory(mode_peak = False, mark = '#1 ')
page_size = 10000
a = numpy.zeros(page_size, dtype = 'i4')
i = 0
for element in generate_element():
    if (i != 0) and (i % page_size == 0):
        a.resize(i + page_size)
    a[i] = element
    i += 1
a.resize(i)
check_memory(mode_peak = False, mark = '#2 ')
check_memory(mode_peak = True, mark = '#2 ')

这导致输出:

#1 Current RAM consumption: 0.070 GB
#2 Current RAM consumption: 0.118 GB
#2 Peak RAM consumption: 0.118 GB

此外,由于不再进行重新分配,性能也显着提高。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多