将Python列表转换为Numpy数组(原地操作)

3

我有一个巨大的Python列表(16 GB),我想将其原地转换为NumPy数组。但是这个语句会耗费太多内存,我承受不起。

huge_array = np.array(huge_list).astype(np.float16)

我正在寻找一些有效的方法将这个huge_list转换为numpy数组,而不进行复制。
有人可以建议一个高效的方法吗?可能需要先将列表保存到磁盘上,然后再加载为numpy数组,我可以接受这种方法。
我非常感谢任何帮助。
编辑1: huge_list是一个在运行时创建的内存中的Python列表,已经占用了16GB。 我需要将它转换为numpy float16数组。

你考虑过排除 np.save 和 np.load 的可能性了吗? - user1121588
那个np.array方法执行时间太长,还是会产生内存错误? - hpaulj
我关心的是内存,而不是速度。因此,np.array很可能会抛出内存错误。 @DanPatterson,huge_list并没有保存在磁盘上,它是在运行时生成的内存数据,然后我必须将其转换为numpy float16数组。 - Ahmed
2
@Ahmed:生成列表的是什么?你能不能直接将其生成成一个数组呢? - Eric
@Eric,该列表是逐步生成的,我们事先不知道它的大小。因此,它是通过list.append()方法生成的。由于numpy数组是不可变的,所以直接创建numpy数组实际上没有节省内存的意义。 - Ahmed
显示剩余3条评论
3个回答

4

np.array(huge_list, dtype=np.float16)会更快,因为它只复制了一次列表,而不是两次。


你可能不需要担心这个复制,因为复制的大小比原始列表小得多:

>>> x = [float(i) for i in range(10000)]
>>> sys.getsizeof(x)
83112
>>> y = np.array(x, dtype=np.float16)
>>> sys.getsizeof(y)
20096

但这还不是最糟糕的 - 使用Python列表时,列表中的每个数字都占用了自己的内存空间:

>>> sum(sys.getsizeof(i) for i in x)
240000

所以,NumPy数组要小约15倍!

1
内存是首要考虑的,速度是次要问题。 - Ahmed
假设huge_list包含浮点数,那么这种方法使用的内存只有np.array(huge_list).view(np.float16)的五分之一。 - Eric
谢谢指出,我已经纠正了,但是我的机器安装了32GB内存。该列表的大小为16GB(float16),我不想通过创建另一个16GB的副本来溢出我的内存。 - Ahmed
1
@Ahmed:复制品不会是16GB。x = [1.0]*100sys.getsizeof(x) == 864sys.getsizeof(np.array(x, dtype=np.float16)) == 296 - Eric

2

如我之前提到的,最简单的方法是将数组转储到文件中,然后将该文件加载为numpy数组。

首先,我们需要巨大列表的大小:

huge_list_size = len(huge_list)

下一步,我们将其转储到磁盘上。
dumpfile = open('huge_array.txt', 'w')

for item in huge_list:
    dumpfile.write(str(item)+"\n")
dumpfile.close()

确保如果所有操作都在同一环境中进行,我们清除内存

del huge_list

下面我们定义一个简单的读取生成器。
def read_file_generator(filename):
    with open(filename) as infile:
        for i, line in enumerate(infile):
            yield [i, line]

然后,我们创建一个由零组成的numpy数组,并使用刚刚创建的生成器填充它。
huge_array = np.zeros(huge_list_size, dtype='float16')

for i, item in read_file_generator('huge_array.txt'):
    huge_array[i] = item

我之前的回答是错误的。 我建议以下内容是解决方案,但正如hpaulj所评论的那样,它并不是解决方案。

You can do this in a multiple ways, the easiest would be to just dump the array to a file and then load that file as a numpy array:

dumpfile = open('huge_array.txt', 'w')

for item in huge_array:
  print>>dumpfile, item

Then load it as a numpy array

huge_array = numpy.loadtxt('huge_array.txt')

If you want to perform further computations on this data you can also use the joblib library for memmapping, which is extremely usefull in handling large numpy array cmputations. Available at https://pypi.python.org/pypi/joblib


2
但是loadtxt将值加载到列表中,并在所有值都加载完毕后执行np.array(alist) - hpaulj
我检查了numpy 1.10.4中np.loadtxt的源代码确保信息无误。在npyio中的loadtxt方法的第808行中,列表确实被指定并在短时间内读取。在第936行通过“np.array(X,dtype)”调用将列表转换为numpy数组。谢谢您,我之前不知道这些信息。我会尽快调整我的答案。 - Laurens
如承诺所说,已更新答案。我必须同意hpaulj的观点,即切块可能是更好的解决方案,但这必须在生成数据期间完成,并不能回答问题。 - Laurens

0
你可以使用numpy的save和load函数:
你可以将一个普通的Python列表作为参数传递给np.save,然后np.load将直接加载到一个numpy数组中。
示例:
from tempfile import TemporaryFile
outfile = TemporaryFile()
x = [1, 2, 3]
np.save(outfile, x)

outfile.seek(0)
np.load(outfile)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接