Симулятор частиц Python: обработка вне ядра

Описание проблемы

При написании симулятора частиц Монте-Карло (броуновское движение и излучение фотонов) на python / numpy. Мне нужно сохранить результат моделирования (>> 10 ГБ) в файл и обработать данные на втором этапе. Совместимость как с Windows, так и с Linux очень важна.

Количество частиц (n_particles) 10-100. Количество временных шагов (time_size) составляет ~ 10 ^ 9.

Симуляция состоит из 3 шагов (код ниже для версии, полностью в ОЗУ):

  1. Смоделируйте (и сохраните) массив ставок emission (содержит много почти нулевых элементов):

    • shape (n_particles x time_size), float32, size 80GB
  2. Вычислить массив counts (случайные значения из процесса Пуассона с ранее вычисленными скоростями):

    • shape (n_particles x time_size), uint8, размер 20 ГБ

      counts = np.random.poisson(lam=emission).astype(np.uint8)
      
  3. Найдите отметки времени (или индекс) отсчетов. Счетчики почти всегда равны 0, поэтому массивы временных меток умещаются в ОЗУ.

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

Я делаю шаг 1 один раз, затем повторяю шаг 2-3 много (~ 100) раз. В будущем мне может потребоваться предварительная обработка emission (применение cumsum или других функций) перед вычислением counts.

Вопрос

У меня есть работающая реализация в памяти, и я пытаюсь понять, как лучше всего реализовать версию вне ядра, которая может масштабироваться для (гораздо) более длительных симуляций.

Что бы я хотел, чтобы он существовал

Мне нужно сохранять массивы в файл, и я хотел бы использовать один файл для моделирования. Мне также нужен «простой» способ сохранить и вызвать словарь параметров моделирования (скаляры).

В идеале мне нужен массив numpy с файловой поддержкой, который я могу предварительно выделить и заполнить кусками. Затем я хотел бы, чтобы методы массива numpy (max, cumsum, ...) работали прозрачно, требуя только ключевого слова chunksize, чтобы указать, какую часть массива загружать на каждой итерации.

Еще лучше, мне бы хотелось, чтобы Numexpr работал не между кешем и ОЗУ, а между ОЗУ и жестким диском.

Какие есть практические варианты

В качестве первого варианта я начал экспериментировать с pyTables, но меня не устраивают его сложность и абстракции (настолько отличные от numpy). Более того, мое текущее решение (читайте ниже) УЖАСНО и не очень эффективно.

Итак, мои варианты, на которые я ищу ответ, следующие:

  1. реализовать массив numpy с требуемой функциональностью (как?)

  2. умнее использовать pytable (разные структуры данных / методы)

  3. используйте другую библиотеку: h5py, blaze, pandas ... (я пока ни одну из них не пробовал).

Предварительное решение (pyTables)

Я сохраняю параметры моделирования в группе '/parameters': каждый параметр преобразуется в скаляр массива numpy. Подробное решение, но оно работает.

Я сохраняю emission как расширяемый массив (EArray), потому что я генерирую данные по частям, и мне нужно добавлять каждый новый фрагмент (хотя я знаю окончательный размер). Сохранить counts проблематичнее. Если сохранить его как массив pytable, будет сложно выполнять такие запросы, как «counts> = 2». Поэтому я сохранил счетчики в виде нескольких таблиц (по одной на частицу) [УГРОЗНО] и запрашиваю с .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.


person user2304916    schedule 05.01.2014    source источник
comment
pandas предоставляет удобный интерфейс для PyTables, см. здесь: pandas.pydata .org / pandas-docs / dev / io.html # hdf5-pytables. Вам следует поместить отдельные данные в отдельные файлы HDF5, если вы не собираетесь запрашивать их вместе. Таблица - это удобная простая структура для выполнения того, что вы описываете, ее можно добавлять и сжимать.   -  person Jeff    schedule 06.01.2014
comment
Понятно. По логике вещей я бы сохранил количество частиц в одной таблице, по одному столбцу на частицу (и 10 ^ 9 строк). Затем я бы запросил каждый столбец на количество ›x. Это неэффективно? Почему вы предлагаете отдельные файлы? Кроме того, есть ли накладные расходы на пространство (из-за индекса) в таблице 1D по сравнению с массивом 1D?   -  person user2304916    schedule 06.01.2014
comment
Это довольно компактно. Вы можете использовать сжатие. И вы можете запросить каждый столбец индивидуально. Отдельные файлы полезны, если данные «раздельные». например Я часто добавляю данные разными процессами, что можно делать ТОЛЬКО в отдельных файлах. (хотя чтение в многопроцессорном режиме нормально). 1 гигабайт такой же большой, как и 80 ГБ, но, скажем, его на 20% больше, имеет ли это значение? И помните, что здесь очень помогает сжатие. См. pytables.github.io/usersguide/optimization.html.   -  person Jeff    schedule 06.01.2014
comment
просто идея, когда вы говорите: моделируйте (и сохраните) массив интенсивности выбросов (содержащий много элементов 0 или почти 0), нужно ли хранить его как float32? если большинство вещей равны нулю или почти нулю, не лучше ли просто хранить индексы там, где они не равны нулю, и их значения, зная, что остальные равны нулю? (я думаю, что-то вроде разреженного или около того?) Другой вопрос: num_particles и n_particles - одно и то же? Я предполагаю, что первый - просто опечатка и везде должно быть n_particles?   -  person usethedeathstar    schedule 06.01.2014
comment
emission является результатом функции, вычисленной на траекториях частиц (трехмерное броуновское движение). Траектории представляют собой полные массивы, но отбрасываются после расчета выбросов. Выбросы в большинстве случаев небольшие (но не 0). Это приводит к тому, что значение counts в большинстве случаев равно 0. В принципе, я мог бы хранить только timestamps, то есть индекс где counts > 0. Но counts не умещается в оперативной памяти, поэтому индекс нужно искать по частям (что требует некоторой арифметики индекса). Но да, это возможный способ, если окажется, что хранение counts слишком дорого.   -  person user2304916    schedule 07.01.2014


Ответы (5)


Dask.array может выполнять операции с фрагментами, такие как max, cumsum и т. Д., В массиве на диске, таком как PyTables или h5py.

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
person MRocklin    schedule 01.05.2015

См. здесь, чтобы узнать, как сохранить свои параметры в файле HDF5 ( это соленые огурцы, поэтому вы можете хранить их в том виде, в котором они есть; их размер составляет 64 КБ).

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)                                                                                                                                                                                                                                                                       
    # http://stackoverflow.com/questions/16997048/how-does-one-append-large-amounts-of-

    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')         

В качестве альтернативы вы можете сделать каждую частицу data_column и выбирать их индивидуально.

и некоторый выход (в данном случае довольно активная эмиссия :)

    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]
person Jeff    schedule 06.01.2014
comment
Спасибо за ответы. Я проверяю ваш подход и скоро доложу. - person user2304916; 07.01.2014
comment
Я не могу запустить часть вашего кода, генерирующую счетчики, там написано: NotImplementedError: indexing 64-bit unsigned integer columns is not supported yet, sorry - person user2304916; 07.01.2014
comment
Вы можете увидеть ошибку в конце этой записной книжки: nbviewer.ipython.org/gist/tritemio/ 8293590 - person user2304916; 07.01.2014
comment
вам нужны pytables 3.0.0, pandas 0.12 - person Jeff; 07.01.2014
comment
Вот что у меня от Анаконды - person user2304916; 07.01.2014
comment
может быть ошибка преобразования типа в 0.12: попробуйте использовать int8 вместо uint8; также может быть проблема с архитектурой / ОС - у вас 64-битная версия? Linux? uint иногда бывает странным. обуви counts.dtypes перед добавлением; ты не хочешь uint64 - person Jeff; 07.01.2014
comment
Я подтвердил, что в версии 0.13 этот апкаст работает правильно, но в 32-битной системе он преобразуется в uint64. Просто сделайте counts.astype(int64).iloc[:,0:4].sum(1) для вычисления суммы и избегайте uint64 (или просто используйте int8). В любом случае у них одинаковые требования к хранилищу. - person Jeff; 07.01.2014

Решение PyTable

Поскольку функциональность, предоставляемая Pandas, не требуется, а обработка выполняется намного медленнее (см. Блокнот ниже), лучшим подходом является прямое использование PyTables или h5py. Пока я пробовал только подход pytables.

Все тесты проводились в этом ноутбуке:

Введение в структуры данных pytables

Ссылка: Официальные документы PyTables

Pytables позволяет хранить данные в файлах 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 во время создания.

Размер Array и CArray фиксируется во время создания. Однако вы можете заполнять / записывать массив по частям после создания. И наоборот, EArray можно расширить с помощью метода .append().

Таблицы

table - совсем другой зверь. По сути, это стол. У вас есть только одномерный индекс, и каждый элемент представляет собой строку. Внутри каждой строки есть типы данных столбцов, каждый столбец может иметь свой тип. Если вы знакомы с множественными массивами записей, таблицей в основном представляет собой одномерный массив записей, каждый элемент которого имеет множество полей в качестве столбцов.

1D или 2D массивы numpy могут храниться в таблицах, но это немного сложнее: нам нужно создать тип данных строки. Например, чтобы сохранить массив numpy 1D uint8, нам нужно сделать:

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')

Это не только просто (по сравнению с массивами, где нам нужно сканировать весь файл по частям и строить index в цикле), но и очень быстро.

Как сохранить параметры моделирования

Лучший способ сохранить параметры моделирования - использовать группу (например, /parameters), преобразовать каждый скаляр в массив numpy и сохранить его как CArray.

Массив для emission

emission - это самый большой массив, который создается и читается последовательно. Для этого шаблона использования хорошей структурой данных является EArray. На смоделированных данных с ~ 50% нулевых элементов сжатие blosc (level=5) достигает степени сжатия 2,2x. Я обнаружил, что размер блока 2 ^ 18 (256 КБ) имеет минимальное время обработки.

Хранение counts

Сохранение также counts увеличит размер файла на 10% и на вычисление отметок времени уйдет на 40% больше времени. Сохранение counts не является преимуществом само по себе, потому что в конце нужны только временные метки.

Преимущество состоит в том, что реконструировать индекс (временные метки) проще, потому что мы запрашиваем полную временную ось одной командой (.get_where_list('counts >= 1')). И наоборот, с фрагментированной обработкой нам нужно выполнить некоторую арифметику индекса, что немного сложно и, возможно, обременительно для обслуживания.

Однако сложность кода может быть небольшой по сравнению со всеми другими операциями (сортировкой и объединением), которые необходимы в обоих случаях.

Хранение timestamps

Метки времени могут накапливаться в ОЗУ. Однако мы не знаем размер массивов перед запуском, и необходим последний hstack() вызов для объединения различных фрагментов, хранящихся в списке. Это удваивает требования к памяти, поэтому ОЗУ может быть недостаточно.

Мы можем сохранять текущие временные метки в таблице, используя .append(). В конце мы можем загрузить таблицу в память с помощью .read(). Это всего на 10% медленнее, чем вычисление всего в памяти, но позволяет избежать требования двойной RAM. Более того, мы можем избежать окончательной полной загрузки и минимизировать использование оперативной памяти.

H5Py

H5py - гораздо более простая библиотека, чем pytables. Для этого варианта использования (в основном) последовательная обработка кажется более подходящей, чем pytables. Единственная отсутствующая особенность - отсутствие сжатия blosc. Если это приведет к большому снижению производительности, еще предстоит проверить.

person user2304916    schedule 09.01.2014
comment
этот тест полностью вводит в заблуждение. В вашем коде структура данных имеет гораздо более длинный столбец, чем строка, что не оптимально как для pytables, так и для pandas. Затем вы забыли выполнить fsync после flush (), что, конечно же, игнорирует фактическое время записи на диск для pytables. Вы также забыли установить ожидаемые строки в HDFStore. Последнее, что вы забыли отключить механизм индексации на лету во время store.append (). Если вы исправите всю эту проблему, pytables и pandas.HDFStore фактически выдадут одинаковую производительность. - person Wang; 12.08.2017

Используйте OpenMM для моделирования частиц (https://github.com/SimTk/openmm) и MDTraj (https://github.com/rmcgibbo/mdtraj) для обработки ввода-вывода траектории.

person user1973192    schedule 22.03.2014

pytables vs pandas.HDFStore тесты в принятом ответе полностью вводят в заблуждение:

  • Первая критическая ошибка заключается в том, что синхронизация не применяется os.fsync после промывки, что делает тест скорости нестабильным. Так что иногда функция pytables оказывается намного быстрее.

  • Вторая проблема заключается в том, что версии pytables и pandas имеют совершенно разные формы из-за неправильного понимания аргумента формы pytables.EArray. Автор пытается добавить столбец в версию pytables, но добавить строку в версию pandas.

  • Третья проблема - автор использовал разные chunkshape при сравнении.

  • Автор также забыл отключить генерацию индексов таблиц на store.append(), что занимает много времени.

В следующей таблице показаны результаты производительности его версии и моих исправлений. tbold - его версия pytables, pdold - его версия pandas. tbsync и pdsync являются его версией с fsync() после flush() и также отключают генерацию индекса таблицы во время добавления. tbopt и pdopt - моя оптимизированная версия с blosc:lz4 и завершенным 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 имеет больший размер данных. Это потому, что панды используют pytables.Table вместо EArray. И pandas.DataFrame всегда имеет столбец индекса. Первый столбец объекта Table - это индекс DataFrame, для сохранения которого требуется дополнительное пространство. Это лишь немного влияет на производительность ввода-вывода, но предоставляет больше функций, таких как запросы вне ядра. Так что я все еще рекомендую здесь панд. @MRocklin также упомянул о более приятном внешнем пакете dask, если большинство функций, которые вы использовали, представляют собой просто операции с массивами, а не запросы в виде таблицы. Но в производительности ввода-вывода заметной разницы не будет.

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)
        # http://stackoverflow.com/questions/16997048/how-does-one-append-large-
        # 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,
}
person Wang    schedule 12.08.2017