Python粒子模拟器:离线处理

17

问题描述

在Python/Numpy中编写一个蒙特卡罗粒子模拟器(布朗运动和光子发射)。我需要将模拟输出(>>10GB)保存到文件中,并在第二步处理数据。与Windows和Linux的兼容性很重要。

粒子数 (n_particles) 在10-100之间。时间步数(time_size) 大约是10的9次方。

仿真有3个步骤(以下代码是适用于纯内存版本的):

  1. 模拟(并存储)一个emission速率数组(包含许多几乎为0的元素):

    • 形状为 (n_particles x time_size),float32类型,大小为80GB
  2. 计算counts数组,(从先前计算的速率中随机生成泊松过程的值):

    • 形状为 (n_particles x time_size),uint8类型,大小为20GB
counts = np.random.poisson(lam=emission).astype(np.uint8)
  • 查找计数的时间戳(或索引)。计数几乎总是为0,因此时间戳数组将适合于内存中。

  • # Loop across the particles
    timestamps = [np.nonzero(c) for c in counts]
    

    我会执行步骤1一次,然后重复步骤2-3多次(大约100次)。未来我可能需要在计算counts之前对emission进行预处理(应用cumsum或其他函数)。

    问题

    我有一个内存中的工作实现,我正在尝试了解最佳方法来实现一个可扩展到(更长的)模拟的外部核心版本。

    我希望它存在的功能

    我需要将数组保存到文件中,并且希望为模拟使用单个文件。我还需要一种“简单”的方法来存储和调用模拟参数(标量)的字典。

    理想情况下,我希望有一个支持文件的numpy数组,可以预先分配空间并按块填充。然后,我希望numpy数组方法(maxcumsum,...)能够透明地工作,只需要一个chunksize关键字来指定每次迭代加载数组的多少。

    更好的是,我希望有一个Numexpr,它不是在缓存和RAM之间操作,而是在RAM和硬盘之间进行操作。

    实际可选方案

    首选项是我开始尝试使用pyTables,但我对其复杂性和抽象化(与numpy非常不同)不满意。此外,我的当前解决方案(请参见下文)很丑陋,效率也不高。

    因此,我寻求答案的选项是:

    1. 实现具有所需功能的numpy数组(如何?)

    2. 以更聪明的方式使用PyTable(不同的数据结构/方法)

    3. 使用另一个库:h5py、blaze、pandas等(到目前为止我还没有尝试过任何一个)。

    初步解决方案(PyTables)

    我将模拟参数保存在'/parameters'组中:每个参数转换为numpy数组标量。这是一种冗长的解决方案,但它可以工作。

    我将emission保存为可扩展数组(EArray),因为我按块生成数据,并且需要附加每个新块(尽管我知道最终大小)。保存counts更棘手。如果像pytable数组一样保存它,则难以执行查询“计数>=2”。因此,我将计数保存为多个表(每个粒子一个表)[丑],并使用.get_where_list('counts >= 2')查询。我不确定这是否是空间有效的,并且生成所有这些表而不是使用单个数组显着破坏了HDF5文件。此外,奇怪的是,创建这些表需要创建自定义dtype(即使对于标准的numpy dtype也是如此):

        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接口,详见这里:http://pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables。如果您不打算一起查询它们,应将单独的数据放在单独的HDF5文件中。表是一个很好的直截了当的结构,可以追加和压缩,用于您所描述的操作。 - Jeff
    我明白了。从逻辑上讲,我会将粒子计数存储在单个表中,每个粒子占据一列(共10^9行)。然后,我会查询每个列以获取计数 > x 的结果。这种方法是否低效?为什么您建议使用单独的文件?此外,在1D表与1D数组中,由于索引而产生的空间开销是多少? - user2304916
    它非常节省空间。您可以使用压缩。您可以单独查询每个列。如果数据是“分离的”,则单独的文件非常有用。例如,我经常有不同的进程附加数据,这只能在单独的文件中完成(但在多进程读取时可以)。1千兆行很大,80GB也很大,但是如果说它比原来大20%,那真的重要吗?请记住,压缩在这里可以帮助很多。请参见http://pytables.github.io/usersguide/optimization.html。 - Jeff
    只是一个想法,当你说:“模拟(并存储)一个排放速率数组(包含许多0或几乎为0的元素)”时,有必要将其存储为float32吗?如果大多数元素都是零或几乎为零,那么仅存储它们不为零的索引和值是否更好,知道其余部分为零?(我猜类似于稀疏矩阵之类的东西?)-另一个问题:num_particles和n_particles是相同的吗?我假设第一个只是一个打字错误,并且应该在所有地方使用n_particles? - usethedeathstar
    emission 是在粒子轨迹(3D布朗运动)上评估的函数的结果。这些轨迹是“完全数组”,但在发射计算后被丢弃。发射大多数情况下很小(但不为0)。这导致 counts 大部分时间都是0。原则上,我可以只存储 timestamps,即 counts> 0 的“索引”。但是 counts 不适合于 RAM 存储,因此必须逐块找到索引(这需要一些索引算术)。但如果存储 counts 太昂贵,这也是一种可能的方法。 - user2304916
    5个回答

    3

    Dask.array 可以对像 PyTables 或 h5py 这样的磁盘上的数组执行分块操作,如 maxcumsum 等。

    import h5py
    d = h5py.File('myfile.hdf5')['/data']
    import dask.array as da
    x = da.from_array(d, chunks=(1000, 1000))
    

    X看起来和numpy数组类似,并且复制了大部分API。对x的操作将创建一个内存中操作的DAG,使用多个核心从磁盘流式传输,以高效地执行。

    da.exp(x).mean(axis=0).compute()
    

    http://dask.pydata.org/en/latest/

    conda install dask
    or 
    pip install dask
    

    2

    查看此处了解如何将参数存储在HDF5文件中(它会使用pickle,所以您可以按照自己的方式存储它们; pickle大小限制为64kb)。

    import pandas as pd                                                                                                                                                                                                                                                                                               
    import numpy as np                                                                                                                                                                                                                                                                                                
    
    n_particles = 10                                                                                                                                                                                                                                                                                                  
    chunk_size = 1000                                                                                                                                                                                                                                                                                                 
    
    # 1) create a new emission file, compressing as we go                                                                                                                                                                                                                                                             
    emission = pd.HDFStore('emission.hdf',mode='w',complib='blosc')                                                                                                                                                                                                                                                   
    
    # generate simulated data                                                                                                                                                                                                                                                                                         
    for i in range(10):                                                                                                                                                                                                                                                                                               
    
        df = pd.DataFrame(np.abs(np.random.randn(chunk_size,n_particles)),dtype='float32')                                                                                                                                                                                                                            
    
        # create a globally unique index (time)                                                                                                                                                                                                                                                                       
        # https://dev59.com/JWQn5IYBdhLWcg3wNlDt
    
        data-to-a-pandas-hdfstore-and-get-a-natural/16999397#16999397                                                                                                                                                              
            try:                                                                                                                                                                                                                                                                                                          
                nrows = emission.get_storer('df').nrows                                                                                                                                                                                                                                                                   
            except:                                                                                                                                                                                                                                                                                                       
                nrows = 0                                                                                                                                                                                                                                                                                                 
    
            df.index = pd.Series(df.index) + nrows                                                                                                                                                                                                                                                                        
            emission.append('df',df)                                                                                                                                                                                                                                                                                      
    
        emission.close()                                                                                                                                                                                                                                                                                                  
    
        # 2) create counts                                                                                                                                                                                                                                                                                                
        cs = pd.HDFStore('counts.hdf',mode='w',complib='blosc')                                                                                                                                                                                                                                                           
    
        # this is an iterator, can be any size                                                                                                                                                                                                                                                                            
        for df in pd.read_hdf('emission.hdf','df',chunksize=200):                                                                                                                                                                                                                                                         
    
            counts = pd.DataFrame(np.random.poisson(lam=df).astype(np.uint8))                                                                                                                                                                                                                                             
    
            # set the index as the same                                                                                                                                                                                                                                                                                   
            counts.index = df.index                                                                                                                                                                                                                                                                                       
    
            # store the sum across all particles (as most are zero this will be a 
            # nice sub-selector                                                                                                                                                                                                                       
            # better maybe to have multiple of these sums that divide the particle space                                                                                                                                                                                                                                  
            # you don't have to do this but prob more efficient                                                                                                                                                                                                                                                           
            # you can do this in another file if you want/need                                                                                                                                                                                                                                                               
            counts['particles_0_4'] = counts.iloc[:,0:4].sum(1)                                                                                                                                                                                                                                                           
            counts['particles_5_9'] = counts.iloc[:,5:9].sum(1)                                                                                                                                                                                                                                                           
    
            # make the non_zero column indexable                                                                                                                                                                                                                                                                          
            cs.append('df',counts,data_columns=['particles_0_4','particles_5_9'])                                                                                                                                                                                                                                         
    
        cs.close()                                                                                                                                                                                                                                                                                                        
    
        # 3) find interesting counts                                                                                                                                                                                                                                                                                      
        print pd.read_hdf('counts.hdf','df',where='particles_0_4>0')                                                                                                                                                                                                                                                      
        print pd.read_hdf('counts.hdf','df',where='particles_5_9>0')         
    

    你可以选择将每个粒子作为一个数据列,并对它们进行单独选择。
    以下是一些输出(在这种情况下非常活跃的发射 :)
        0  1  2  3  4  5  6  7  8  9  particles_0_4  particles_5_9
    0   2  2  2  3  2  1  0  2  1  0              9              4
    1   1  0  0  0  1  0  1  0  3  0              1              4
    2   0  2  0  0  2  0  0  1  2  0              2              3
    3   0  0  0  1  1  0  0  2  0  3              1              2
    4   3  1  0  2  1  0  0  1  0  0              6              1
    5   1  0  0  1  0  0  0  3  0  0              2              3
    6   0  0  0  1  1  0  1  0  0  0              1              1
    7   0  2  0  2  0  0  0  0  2  0              4              2
    8   0  0  0  1  3  0  0  0  0  1              1              0
    10  1  0  0  0  0  0  0  0  0  1              1              0
    11  0  0  1  1  0  2  0  1  2  1              2              5
    12  0  2  2  4  0  0  1  1  0  1              8              2
    13  0  2  1  0  0  0  0  1  1  0              3              2
    14  1  0  0  0  0  3  0  0  0  0              1              3
    15  0  0  0  1  1  0  0  0  0  0              1              0
    16  0  0  0  4  3  0  3  0  1  0              4              4
    17  0  2  2  3  0  0  2  2  0  2              7              4
    18  0  1  2  1  0  0  3  2  1  2              4              6
    19  1  1  0  0  0  0  1  2  1  1              2              4
    20  0  0  2  1  2  2  1  0  0  1              3              3
    22  0  1  2  2  0  0  0  0  1  0              5              1
    23  0  2  4  1  0  1  2  0  0  2              7              3
    24  1  1  1  0  1  0  0  1  2  0              3              3
    26  1  3  0  4  1  0  0  0  2  1              8              2
    27  0  1  1  4  0  1  2  0  0  0              6              3
    28  0  1  0  0  0  0  0  0  0  0              1              0
    29  0  2  0  0  1  0  1  0  0  0              2              1
    30  0  1  0  2  1  2  0  2  1  1              3              5
    31  0  0  1  1  1  1  1  0  1  1              2              3
    32  3  0  2  1  0  0  1  0  1  0              6              2
    33  1  3  1  0  4  1  1  0  1  4              5              3
    34  1  1  0  0  0  0  0  3  0  1              2              3
    35  0  1  0  0  1  1  2  0  1  0              1              4
    36  1  0  1  0  1  2  1  2  0  1              2              5
    37  0  0  0  1  0  0  0  0  3  0              1              3
    38  2  5  0  0  0  3  0  1  0  0              7              4
    39  1  0  0  2  1  1  3  0  0  1              3              4
    40  0  1  0  0  1  0  0  4  2  2              1              6
    41  0  3  3  1  1  2  0  0  2  0              7              4
    42  0  1  0  2  0  0  0  0  0  1              3              0
    43  0  0  2  0  5  0  3  2  1  1              2              6
    44  0  2  0  1  0  0  1  0  0  0              3              1
    45  1  0  0  2  0  0  0  1  4  0              3              5
    46  0  2  0  0  0  0  0  1  1  0              2              2
    48  3  0  0  0  0  1  1  0  0  0              3              2
    50  0  1  0  1  0  1  0  0  2  1              2              3
    51  0  0  2  0  0  0  2  3  1  1              2              6
    52  0  0  2  3  2  3  1  0  1  5              5              5
    53  0  0  0  2  1  1  0  0  1  1              2              2
    54  0  1  2  2  2  0  1  0  2  0              5              3
    55  0  2  1  0  0  0  0  0  3  2              3              3
    56  0  1  0  0  0  2  2  0  1  1              1              5
    57  0  0  0  1  1  0  0  1  0  0              1              1
    58  6  1  2  0  2  2  0  0  0  0              9              2
    59  0  1  1  0  0  0  0  0  2  0              2              2
    60  2  0  0  0  1  0  0  1  0  1              2              1
    61  0  0  3  1  1  2  0  0  1  0              4              3
    62  2  0  1  0  0  0  0  1  2  1              3              3
    63  2  0  1  0  1  0  1  0  0  0              3              1
    65  0  0  1  0  0  0  1  5  0  1              1              6
       .. .. .. .. .. .. .. .. .. ..            ...            ...
    
    [9269 rows x 12 columns]
    

    谢谢回答。我正在测试您的方法,并会尽快汇报结果。 - user2304916
    我无法运行你代码中的计数生成部分,它显示:NotImplementedError: 尚不支持索引64位无符号整数列,抱歉 - user2304916
    您可以在此笔记本的末尾看到错误:http://nbviewer.ipython.org/gist/tritemio/8293590 - user2304916
    1
    你需要安装PyTables 3.0.0和Pandas 0.12。 - Jeff
    1
    可能在0.12版本中存在类型转换错误:尝试使用int8而不是uint8;也可能是架构/操作系统问题 - 您是64位吗?Linux?uint有时会很奇怪。在附加之前检查shoe counts.dtypes;您不想要uint64。 - Jeff
    显示剩余2条评论

    2

    PyTable解决方案

    由于Pandas提供的功能不需要,并且处理速度较慢(请参见下面的笔记本),最好的方法是直接使用PyTables或h5py。到目前为止,我只尝试过pytables方法。

    所有测试都在这个笔记本中进行:

    介绍pytables数据结构

    参考:官方PyTables文档

    Pytables允许使用2种格式将数据存储在HDF5文件中:数组和表格。

    数组

    有3种类型的数组Array、CArray和EArray。它们都允许使用类似于numpy切片的符号存储和检索(多维)切片。

    # Write data to store (broadcasting works)
    array1[:]  = 3
    
    # Read data from store
    in_ram_array = array1[:]
    

    为了在某些使用情况下进行优化,CArray 以“块”的形式保存,可以在创建时选择chunk_shape的大小。 ArrayCArray 的大小在创建时固定。但是,您可以在创建后逐块填充/写入数组。相反,EArray 可以使用.append() 方法扩展。

    表格

    table 是一个完全不同的实体。它基本上是一张“表”。您只有1D索引,每个元素都是一行。在每行内部,有“列”数据类型,每个列可以具有不同的类型。如果您熟悉numpy记录数组,则表格基本上是一个1D记录数组,每个元素具有许多字段作为列。
    可以将1D或2D numpy数组存储在表中,但需要更多技巧:我们需要创建一个行数据类型。例如,要存储1D uint8 numpy数组,我们需要执行以下操作:
    table_uint8 = np.dtype([('field1', 'u1')])
    table_1d = data_file.create_table('/', 'array_1d', description=table_uint8)
    

    那么为什么要使用表格呢?因为与数组不同,表格可以有效地查询。例如,如果我们想在一个巨大的基于磁盘的表格中搜索元素> 3,我们可以这样做:

    index = table_1d.get_where_list('field1 > 3')
    

    不仅它很简单(与数组相比,我们需要分块扫描整个文件并在循环中构建“索引”),而且它也非常快。
    如何存储模拟参数
    存储模拟参数的最佳方法是使用组(即“/ parameters”),将每个标量转换为numpy数组,并将其存储为“CArray”。
    用于“发射”的数组
    “发射”是生成和按顺序读取的最大数组。对于这种使用模式,一个好的数据结构是“EArray”。在 ~50%的零元素的“模拟”数据上,“blosc”压缩(“level = 5”)实现了2.2倍的压缩比。我发现2 ^ 18(256k)的块大小具有最小处理时间。
    存储“计数”
    存储“计数”还会增加文件大小10%,并需要40%的额外时间来计算时间戳。仅存储时间戳并没有优势,因为最终只需要时间戳。
    优点在于重新构建索引(时间戳)更简单,因为我们可以在单个命令中查询完整的时间轴(“.get_where_list('counts> = 1')”)。相反,在分块处理中,我们需要执行一些索引算术,这有点棘手,可能是维护的负担。
    但是代码复杂度可能比两种情况下都需要的所有其他操作(排序和合并)要小。
    存储“时间戳”
    时间戳可以在RAM中累积。但是,在开始之前,我们不知道数组大小,并且需要一个最终的“hstack()”调用来“合并”存储在列表中的不同块。这将使内存需求翻倍,因此RAM可能不足。
    我们可以使用“.append()”将按需进行的时间戳存储到表中。最后,我们可以使用“.read()”将表加载到内存中。这仅比全内存计算慢10%,但避免了“双倍RAM”要求。此外,我们可以避免最终的完全加载,并且具有最小的RAM使用量。
    H5Py
    H5py比pytables简单得多。对于(主要)顺序处理的此用例,它似乎比pytables更适合。唯一缺少的功能是缺乏“blosc”压缩。如果这导致大的性能损失,仍然需要测试。

    这个测试是完全误导性的。在你的代码中,你的数据结构列比行长得多,这对于pytables和pandas都不是最优的。然后,在flush()之后你忘记了做fsync(),这当然会忽略pytables实际的磁盘写入时间。你还忘记在HDFStore中设置expectedrows。最后,你忘记在store.append()期间禁用即时索引机制。如果你解决了所有这些问题,pytables和pandas.HDFStore实际上会给出相同的性能。 - Wang

    0

    pytables vs pandas.HDFStore测试中的最佳回答完全具有误导性:

    • 第一个关键错误是在刷新后没有应用os.fsync,这使得速度测试不稳定。因此,有时,pytables函数会意外地更快。

    • 第二个问题是由于误解pytables.EArray的形状参数,pytables和pandas版本具有完全不同的形状。作者尝试将列附加到pytables版本中,但将行附加到pandas版本中。

    • 第三个问题是作者在比较过程中使用了不同的chunkshape

    • 作者还忘记在store.append()期间禁用表索引生成,这是一个耗时的过程。

    下表显示了他的版本和我的修复版的性能结果。 tbold 是他的 pytables 版本,pdold 是他的 pandas 版本。tbsyncpdsync 是在 flush() 后使用 fsync() 并禁用追加期间的表索引生成的版本。tboptpdopt 是我优化过的版本,使用 blosc:lz4 和 complevel 9。

    | name   |   dt |   data size [MB] |   comp ratio % | chunkshape   | shape         | clib            | indexed   |
    |:-------|-----:|-----------------:|---------------:|:-------------|:--------------|:----------------|:----------|
    | tbold  | 5.11 |           300.00 |          84.63 | (15, 262144) | (15, 5242880) | blosc[5][1]     | False     |
    | pdold  | 8.39 |           340.00 |          39.26 | (1927,)      | (5242880,)    | blosc[5][1]     | True      |
    | tbsync | 7.47 |           300.00 |          84.63 | (15, 262144) | (15, 5242880) | blosc[5][1]     | False     |
    | pdsync | 6.97 |           340.00 |          39.27 | (1927,)      | (5242880,)    | blosc[5][1]     | False     |
    | tbopt  | 4.78 |           300.00 |          43.07 | (4369, 15)   | (5242880, 15) | blosc:lz4[9][1] | False     |
    | pdopt  | 5.73 |           340.00 |          38.53 | (3855,)      | (5242880,)    | blosc:lz4[9][1] | False     |
    

    pandas.HDFStore 使用 pytables 库。因此,如果我们正确使用它们,它们应该完全没有区别。

    我们可以看到 pandas 版本具有更大的数据大小。这是因为 pandas 使用 pytables.Table 而不是 EArray。并且 pandas.DataFrame 总是有一个索引列。Table 对象的第一列是这个 DataFrame 索引,需要一些额外的空间来保存。这只会稍微影响 IO 性能,但提供了更多的功能,如 out-of-core 查询。因此,我仍然推荐使用 pandas。@MRocklin 还提到了一个更好的 out-of-core 包 dask,如果你使用的大多数功能只是数组操作而不是类似表格的查询。但 IO 性能不会有明显的差异。

    h5f.root.emission._v_attrs
    Out[82]: 
    /emission._v_attrs (AttributeSet), 15 attributes:
       [CLASS := 'GROUP',
        TITLE := '',
        VERSION := '1.0',
        data_columns := [],
        encoding := 'UTF-8',
        index_cols := [(0, 'index')],
        info := {1: {'names': [None], 'type': 'RangeIndex'}, 'index': {}},
        levels := 1,
        metadata := [],
        nan_rep := 'nan',
        non_index_axes := [(1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])],
        pandas_type := 'frame_table',
        pandas_version := '0.15.2',
        table_type := 'appendable_frame',
        values_cols := ['values_block_0']]
    

    这里是函数:

    def generate_emission(shape):
        """Generate fake emission."""
        emission = np.random.randn(*shape).astype('float32') - 1
        emission.clip(0, 1e6, out=emission)
        assert (emission >=0).all()
        return emission
    
    
    
    def test_puretb_earray(outpath,
                            n_particles = 15,
                            time_chunk_size = 2**18,
                            n_iter = 20,
                           sync = True,
                           clib = 'blosc',
                           clevel = 5,
                           ):
    
        time_size = n_iter * time_chunk_size
        data_file = pytb.open_file(outpath, mode="w")
        comp_filter = pytb.Filters(complib=clib, complevel=clevel)
        emission = data_file.create_earray('/', 'emission', atom=pytb.Float32Atom(),
                                           shape=(n_particles, 0),
                                           chunkshape=(n_particles, time_chunk_size),
                                           expectedrows=n_iter * time_chunk_size,
                                           filters=comp_filter)
    
        # generate simulated emission data
        t0 =time()
        for i in range(n_iter):
            emission_chunk = generate_emission((n_particles, time_chunk_size))
            emission.append(emission_chunk)
    
        emission.flush()
        if sync:
            os.fsync(data_file.fileno())
        data_file.close()
        t1 = time()
        return t1-t0
    
    
    def test_puretb_earray2(outpath,
                            n_particles = 15,
                            time_chunk_size = 2**18,
                            n_iter = 20,
                           sync = True,
                           clib = 'blosc',
                           clevel = 5,
                           ):
    
        time_size = n_iter * time_chunk_size
        data_file = pytb.open_file(outpath, mode="w")
        comp_filter = pytb.Filters(complib=clib, complevel=clevel)
        emission = data_file.create_earray('/', 'emission', atom=pytb.Float32Atom(),
                                           shape=(0, n_particles),
                                           expectedrows=time_size,
                                           filters=comp_filter)
    
        # generate simulated emission data
        t0 =time()
        for i in range(n_iter):
            emission_chunk = generate_emission((time_chunk_size, n_particles))
            emission.append(emission_chunk)
    
        emission.flush()
    
        if sync:
            os.fsync(data_file.fileno())
        data_file.close()
        t1 = time()
        return t1-t0
    
    
    def test_purepd_df(outpath,
                       n_particles = 15,
                       time_chunk_size = 2**18,
                       n_iter = 20,
                       sync = True,
                       clib='blosc',
                       clevel=5,
                       autocshape=False,
                       oldversion=False,
                       ):
        time_size = n_iter * time_chunk_size
        emission = pd.HDFStore(outpath, mode='w', complib=clib, complevel=clevel)
        # generate simulated data
        t0 =time()
        for i in range(n_iter):
    
            # Generate fake emission
            emission_chunk = generate_emission((time_chunk_size, n_particles))
    
            df = pd.DataFrame(emission_chunk, dtype='float32')
    
            # create a globally unique index (time)
            # https://dev59.com/JWQn5IYBdhLWcg3wNlDt
            # amounts-of-data-to-a-pandas-hdfstore-and-get-a-natural/16999397#16999397
            try:
                nrows = emission.get_storer('emission').nrows
            except:
                nrows = 0
    
            df.index = pd.Series(df.index) + nrows
    
            if autocshape:
                emission.append('emission', df, index=False,
                                expectedrows=time_size
                                )
            else:
                if oldversion:
                    emission.append('emission', df)
                else:
                    emission.append('emission', df, index=False)
    
        emission.flush(fsync=sync)
        emission.close()
        t1 = time()
        return t1-t0
    
    
    def _test_puretb_earray_nosync(outpath):
        return test_puretb_earray(outpath, sync=False)
    
    
    def _test_purepd_df_nosync(outpath):
        return test_purepd_df(outpath, sync=False,
                              oldversion=True
                              )
    
    
    def _test_puretb_earray_opt(outpath):
        return test_puretb_earray2(outpath,
                                   sync=False,
                                   clib='blosc:lz4',
                                   clevel=9
                                   )
    
    
    def _test_purepd_df_opt(outpath):
        return test_purepd_df(outpath,
                              sync=False,
                              clib='blosc:lz4',
                              clevel=9,
                              autocshape=True
                              )
    
    
    testfns = {
        'tbold':_test_puretb_earray_nosync,
        'pdold':_test_purepd_df_nosync,
        'tbsync':test_puretb_earray,
        'pdsync':test_purepd_df,
        'tbopt': _test_puretb_earray_opt,
        'pdopt': _test_purepd_df_opt,
    }
    

    0

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