存储整数列表的最有效方法

时间:2016-09-20 20:41:15

标签: python list python-3.x memory integer

我最近做了一个项目,其中一个目标是使用尽可能少的内存来使用Python 3存储一系列文件。除了一个列表之外,几乎所有文件占用的空间都很小。整数大约为333,000整数的整数,整数大小约为8000

我目前正在使用pickle来存储列表,该列表占用7mb左右,但我觉得必须有更高效的内存方式来实现这一目标。

我尝试将其存储为文本文件csv,并将这两种文件用于超过10mb的空间。

3 个答案:

答案 0 :(得分:2)

您可以使用的一个stdlib解决方案来自array的数组,来自文档:

  

该模块定义了一种对象类型,它可以紧凑地表示基本值的数组:字符,整数,浮点数。数组是序列类型,其行为与列表非常相似,只是存储在其中的对象类型受到约束。

这通常会给大型列表留下一些内存,例如,列表中有一个1000万个元素,数组会被修整11mb

import pickle    
from array import array

l = [i for i in range(10000000)]
a = array('i', l)

# tofile can also be used.
with open('arrfile', 'wb') as f:  
    pickle.dump(a, f)

with open('lstfile', 'wb') as f:
    pickle.dump(l, f)

尺寸:

!du -sh ./*
39M     arrfile
48M     lstfile

答案 1 :(得分:2)

这是一个小型演示,它使用Pandas模块:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_enabled="true"
        android:state_pressed="false"
        android:drawable="@drawable/b59" />
    <item
        android:state_enabled="true"
        android:state_pressed="true"
        android:drawable="@drawable/y59" />
    <item
        android:state_enabled="false"
        android:drawable="@drawable/b59" />
</selector>

DataFrame信息:

import numpy as np
import pandas as pd
import feather

# let's generate an array of 1M int64 elements...
df = pd.DataFrame({'num_col':np.random.randint(0, 10**9, 10**6)}, dtype=np.int64)
df.info()

%timeit -n 1 -r 1 df.to_pickle('d:/temp/a.pickle')

%timeit -n 1 -r 1 df.to_hdf('d:/temp/a.h5', 'df_key', complib='blosc', complevel=5)
%timeit -n 1 -r 1 df.to_hdf('d:/temp/a_blosc.h5', 'df_key', complib='blosc', complevel=5)
%timeit -n 1 -r 1 df.to_hdf('d:/temp/a_zlib.h5', 'df_key', complib='zlib', complevel=5)
%timeit -n 1 -r 1 df.to_hdf('d:/temp/a_bzip2.h5', 'df_key', complib='bzip2', complevel=5)
%timeit -n 1 -r 1 df.to_hdf('d:/temp/a_lzo.h5', 'df_key', complib='lzo', complevel=5)

%timeit -n 1 -r 1 feather.write_dataframe(df, 'd:/temp/a.feather')

结果(速度):

In [56]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 1 columns):
num_col    1000000 non-null int64
dtypes: int64(1)
memory usage: 7.6 MB

结果(大小):

In [49]: %timeit -n 1 -r 1 df.to_pickle('d:/temp/a.pickle')
1 loop, best of 1: 16.2 ms per loop

In [50]: %timeit -n 1 -r 1 df.to_hdf('d:/temp/a.h5', 'df_key', complib='blosc', complevel=5)
1 loop, best of 1: 39.7 ms per loop

In [51]: %timeit -n 1 -r 1 df.to_hdf('d:/temp/a_blosc.h5', 'df_key', complib='blosc', complevel=5)
1 loop, best of 1: 40.6 ms per loop

In [52]: %timeit -n 1 -r 1 df.to_hdf('d:/temp/a_zlib.h5', 'df_key', complib='zlib', complevel=5)
1 loop, best of 1: 213 ms per loop

In [53]: %timeit -n 1 -r 1 df.to_hdf('d:/temp/a_bzip2.h5', 'df_key', complib='bzip2', complevel=5)
1 loop, best of 1: 1.09 s per loop

In [54]: %timeit -n 1 -r 1 df.to_hdf('d:/temp/a_lzo.h5', 'df_key', complib='lzo', complevel=5)
1 loop, best of 1: 32.1 ms per loop

In [55]: %timeit -n 1 -r 1 feather.write_dataframe(df, 'd:/temp/a.feather')
1 loop, best of 1: 3.49 ms per loop

结论:如果您需要速度和合理的尺寸,请注意HDF5(+ { temp } » ls -lh a* /d/temp -rw-r--r-- 1 Max None 7.7M Sep 20 23:15 a.feather -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a.h5 -rw-r--r-- 1 Max None 7.7M Sep 20 23:15 a.pickle -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a_blosc.h5 -rw-r--r-- 1 Max None 4.0M Sep 20 23:15 a_bzip2.h5 -rw-r--r-- 1 Max None 4.1M Sep 20 23:15 a_lzo.h5 -rw-r--r-- 1 Max None 3.9M Sep 20 23:15 a_zlib.h5 blosc压缩),如果只需Feather-format,请注意关心速度 - 比Pickle快4倍!

答案 2 :(得分:0)

我喜欢使用Jim's suggestion模块的array。如果您的数值小到足以适合机器的本机int类型,那么这是一个很好的解决方案。 (我更喜欢使用array.tofile方法序列化数组,而不是使用pickle。)如果int是32位,那么每个数字使用4个字节。

我想问你如何处理你的文本文件。如果我在[0,8000]范围内创建一个333000整数的文件,每行一个数字,

import random

with open('numbers.txt', 'w') as ostr:
    for i in range(333000):
        r = random.randint(0, 8000)
        print(r, file=ostr)

它的大小只有1.6MiB,与二进制表示法使用的1.3MiB相比,这并不是那么糟糕。如果你有一天碰巧有一个超出原始int类型范围的值,那么文本文件将很快处理它而不会溢出。

此外,如果我使用gzip 压缩文件,文件大小会缩小到686KiB。这比压缩二进制数据更好!使用bzip2时,文件大小仅为562KiB。 Python的标准库支持gzipbz2,因此您可能希望再次尝试使用纯文本格式加压缩。