【发布时间】:2014-01-23 07:35:13
【问题描述】:
问题描述
在 python/numpy 中编写蒙特卡洛粒子模拟器(布朗运动和光子发射)。我需要将模拟输出 (>>10GB) 保存到文件中并在第二步中处理数据。与 Windows 和 Linux 的兼容性很重要。
粒子数(n_particles)为 10-100。时间步数 (time_size) 约为 10^9。
模拟有 3 个步骤(以下代码适用于全 RAM 版本):
-
模拟(并存储)一个
emission速率数组(包含许多几乎为 0 的元素):- 形状(
n_particlesxtime_size),float32,大小80GB
- 形状(
-
计算
counts数组,(来自 Poisson 过程的随机值,具有先前计算的速率):-
形状 (
n_particlesxtime_size),uint8,大小 20GBcounts = np.random.poisson(lam=emission).astype(np.uint8)
-
-
查找计数的时间戳(或索引)。计数几乎总是 0,因此时间戳数组将适合 RAM。
# Loop across the particles timestamps = [np.nonzero(c) for c in counts]
我执行第 1 步一次,然后重复第 2-3 步多次(约 100 次)。将来我可能需要在计算counts之前预处理emission(应用cumsum或其他函数)。
问题
我有一个有效的内存实现,我正在尝试了解实现可以扩展到(更多)更长模拟的核外版本的最佳方法。
我希望它存在
我需要将数组保存到文件中,并且我想使用单个文件进行模拟。我还需要一种“简单”的方式来存储和调用模拟参数(标量)字典。
理想情况下,我想要一个文件支持的 numpy 数组,我可以预先分配和填充块。然后,我希望 numpy 数组方法(max、cumsum、...)透明地工作,只需要一个 chunksize 关键字来指定每次迭代时要加载多少数组。
更好的是,我想要一个不在缓存和 RAM 之间而是在 RAM 和硬盘驱动器之间运行的 Numexpr。
有哪些实用的选项
作为首选 我开始尝试使用 pyTables,但我对它的复杂性和抽象性(与 numpy 如此不同)并不满意。此外,我目前的解决方案(见下文)是丑陋的,效率不高。
所以我寻求答案的选择是
实现一个具有所需功能的 numpy 数组(如何实现?)
以更智能的方式使用 pytable(不同的数据结构/方法)
使用另一个库:h5py、blaze、pandas...(目前我还没有尝试过)。
暂定解决方案(pyTables)
我将模拟参数保存在'/parameters'组中:每个参数都转换为一个numpy数组标量。详细的解决方案,但它有效。
我将 emission 保存为可扩展数组 (EArray),因为我以块的形式生成数据,并且需要附加每个新块(尽管我知道最终大小)。保存counts 问题更大。如果将其保存为 pytable 数组,则很难执行“counts >= 2”之类的查询。因此,我将计数保存为多个表(每个粒子一个)[UGLY],并使用.get_where_list('counts >= 2') 进行查询。我不确定这是否节省空间,并且
生成所有这些表而不是使用单个数组,会显着破坏 HDF5 文件。此外,奇怪的是,创建这些表需要创建自定义 dtype(即使对于标准 numpy dtypes):
dt = np.dtype([('counts', 'u1')])
for ip in xrange(n_particles):
name = "particle_%d" % ip
data_file.create_table(
group, name, description=dt, chunkshape=chunksize,
expectedrows=time_size,
title='Binned timetrace of emitted ph (bin = t_step)'
' - particle_%d' % particle)
每个粒子计数“表”都有不同的名称 (name = "particle_%d" % ip),我需要将它们放在 python 列表中以便于迭代。
编辑:这个问题的结果是一个名为PyBroMo的布朗运动模拟器。
【问题讨论】:
-
pandas 为 PyTables 提供了一个不错的接口,请参见此处:pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables。如果您不打算一起查询它们,您应该将单独的数据放在单独的 HDF5 文件中。表是一个很好的简单结构,可以执行您所描述的操作,可以附加和压缩。
-
我明白了。从逻辑上讲,我会将粒子计数存储在一个表中,每个粒子一列(和 10^9 行)。然后我会查询每列的计数> x。那效率低吗?为什么你建议单独的文件?此外,一维表与一维数组中是否存在空间开销(由于索引)?
-
它非常节省空间。您可以使用压缩。您可以单独查询每一列。如果数据是“单独的”,则单独的文件很有用。例如我经常在不同的过程中附加数据,您只能在单独的文件中执行此操作。 (虽然在多进程上阅读是可以的)。 1 gigarow 和 80GB 一样大,但说它大 20%,这真的重要吗?记住压缩在这里有很大帮助。见pytables.github.io/usersguide/optimization.html
-
只是一个想法,当您说:“模拟(并存储)一个发射率数组(包含许多 0 或几乎为 0 的元素)”时,是否有必要将其存储为 float32?如果大多数事情是零或几乎为零,那么只存储不为零的索引及其值是否更好,知道其余部分为零? (我猜是稀疏的东西?) - 另一个问题: num_particles 和 n_particles 是一样的吗?我认为第一个只是一个错字,应该到处都是 n_particles?
-
emission是对粒子轨迹(3D 布朗运动)进行评估的函数的结果。轨迹是“完整阵列”,但在发射计算后被丢弃。大多数时候,排放量很小(但不是 0)。这导致counts在大多数情况下为 0。我原则上只能存储timestamps,这是counts > 0的“索引”。但是counts不适合 ram,因此必须逐块找到索引(这需要一些索引算法)。但是,如果事实证明存储counts过于昂贵,这是一种可能的方法。
标签: numpy pandas pytables h5py blaze