В PyTables, как создать вложенный массив переменной длины?

Я использую PyTables 2.2.1 w/Python 2.6, и мне бы хотелось создать таблицу, содержащую вложенные массивы переменной длины.

Я искал документацию PyTables, а пример учебника (PyTables Tutorial 3.8) показывает, как создать вложенный массив длины = 1. Но для этого примера, как бы добавить переменное число строк в данные "info2/info3/x" и "info2/info3/y"?

Для, возможно, более простой для понимания структуры таблицы, здесь мой пример доморощенного:

"""Desired Pytable output:

DIEM    TEMPUS  Temperature             Data
5       0       100         Category1 <--||-->  Category2
                         x <--| |--> y          z <--|
                        0           0           0
                        2           1           1
                        4           1.33        2.67
                        6           1.5         4.5
                        8           1.6         6.4
5       1       99
                        2           2           0   
                        4           2           2
                        6           2           4
                        8           2           6
5       2       96
                        4           4           0
                        6           3           3
                        8           2.67        5.33


Note that nested arrays have variable length.
"""

import tables as ts

tableDef =      {'DIEM': ts.Int32Col(pos=0),
                'TEMPUS': ts.Int32Col(pos=1), 
                'Temperature' : ts.Float32Col(pos=2),
                'Data': 
                    {'Category1': 
                        {
                        'x': ts.Float32Col(), 
                        'y': ts.Float32Col()
                        }, 
                    'Category2': 
                        {
                        'z': ts.Float32Col(), 
                        }
                    }
                }

# create output file
fpath = 'TestDb.h5'
fh = ts.openFile(fpath, 'w')
# define my table
tableName = 'MyData'
fh.createTable('/', tableName, tableDef)
tablePath = '/'+tableName
table = fh.getNode(tablePath)

# get row iterator
row = table.row
for i in xrange(3):
    print '\ni=', i
    # calc some fake data
    row['DIEM'] = 5
    row['TEMPUS'] = i
    row['Temperature'] = 100-i**2

    for j in xrange(5-i):
        # Note that nested array has variable number of rows
        print 'j=', j,
        # calc some fake nested data
        val1 = 2.0*(i+j)
        val2 = val1/(j+1.0)
        val3 = val1 - val2

        ''' Magic happens here...
        How do I write 'j' rows of data to the elements of 
        Category1 and/or Category2?

        In bastardized pseudo-code, I want to do:

        row['Data/Category1/x'][j] = val1
        row['Data/Category1/y'][j] = val2
        row['Data/Category2/z'][j] = val3
        '''

    row.append()
table.flush()

fh.close()

Я не нашел никаких указаний в документах PyTables, что такая структура невозможна... но в случае, если такая структура на самом деле невозможна, каковы мои альтернативы вложенным столбцам переменной длины?

  • EArray? VLArray? Если да, то как интегрировать эти типы данных в описанную выше структуру?
  • какая-то другая идея?

Любая помощь очень ценится!

EDIT с дополнительной информацией: Похоже, что гуру PyTables уже обратились к вопросу "такая структура возможна":

PyTables Mail Forum - Иератические наборы данных

Так кто-нибудь понял, как создать аналогичную структуру данных PyTable?

Еще раз спасибо!

Ответ 1

Это обычная вещь, которую люди хотят начать с PyTables. Конечно, это была первая попытка I. С 2009 года я не думаю, что эта функциональность была поддержана. Вы можете посмотреть здесь одно решение "Я всегда рекомендую":

http://www.mail-archive.com/[email protected]/msg01207.html

Короче говоря, просто поместите каждый VLArray в отдельное место. Если вы это сделаете, возможно, вам не понадобится VLArrays. Если вы храните отдельные VLArrays для каждой пробной версии (или что-то еще), вы можете хранить метаданные в этих VLArrays (гарантируется, что они будут синхронизироваться с массивом между переименованиями, перемещениями и т.д.) Или помещать их в таблицу (проще искать).

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

Trial Data
1     0.4, 0.5, 0.45
2     0.3, 0.4, 0.45, 0.56

становится

Trial Timepoint Data
1     1         0.4
1     2         0.5
...
2     4         0.56

Данные выше - это одно число, но это может быть, например, атом 4x5x3.

Если вложенные VLArrays теперь поддерживаются в PyTables, я, конечно же, буду знать!

В качестве альтернативы, я думаю, что h5py поддерживает полный набор функций HDF5, поэтому, если вы действительно привержены макете вложенных данных, у вас может быть больше удачи. Тем не менее, вы будете проигрывать на множестве приятных функций! И, по моему опыту, наивные нейрофизиологи в конечном итоге имеют довольно низкую производительность, так как они не получают интеллектуальных решений для анализа данных, фрагментации и т.д. Пожалуйста, сообщите, если вы идете по этому маршруту!

Ответ 2

У меня есть аналогичная задача: сбрасывать данные фиксированного размера с массивами переменной длины.

Сначала я попытался использовать поля фиксированного размера StringCol (64 * 1024) для хранения данных переменной длины (они всегда < 64K). Но он был довольно медленным и тратил много дискового пространства, несмотря на сжатие blosc.

После нескольких дней исследования я закончил следующее решение:

(спойлер: мы сохраняем поля массива в отдельных экземплярах EArray, одно поле EArray для одного массива)

  • Я храню данные фиксированного размера в обычной таблице pytables.
  • Я добавил в эти таблицы 2 дополнительных поля: arrFieldName_Offset и arrFieldName_Length:

    class Particle(IsDescription):
       idnumber  = Int64Col()
       ADCcount  = UInt16Col()
       TDCcount  = UInt8Col()
       grid_i    = Int32Col()
       grid_j    = Int32Col()
       pressure  = Float32Col()
       energy    = FloatCol()
       buffer_Offset = UInt32() # note this field!
       buffer_Length = UInt32() # and this one too!
    
  • Я также создаю один экземпляр EArray для каждого поля массива:

    datatype = StringAtom(1)
    buffer = h5file.createEArray('/detector', 'arr', datatype, (0,), "")
    
  • Затем я добавляю строки, соответствующие данным фиксированного размера:

    row['idnumber'] = ...
    ...
    row['energy'] = ...
    row['buffer_Offset'] = buffer.nrows
    # my_buf is a string (I get it from a stream)
    row['buffer_Length'] = len(my_buf)
    table.append(row)
    
  • Ta-дах! Добавьте буфер в массив.

    buffer.append(np.ndarray((len(my_buf),), buffer=my_buf, dtype=datatype))
    
  • Это трюк. В моих экспериментах этот подход в 2-10 раз быстрее, чем хранение оборванных массивов фиксированного размера (например, StringAtom (HUGE_NUMBER)), и полученная БД несколько раз меньше (2-5x)

  • Получение данных буфера легко. Предположим, что строка - это одна строка, которую вы читаете из своей БД:

    # Open array for reading
    buffer = h5file.createEArray('/detector', 'Particle.buffer', datatype, (0,), "")
    ...
    row = ...
    ...
    bufferDataYouNeed = buffer[ row['buffer_Offset'] : row['buffer_Offset'] + row['buffer_Length']]
    

Ответ 3

Я также столкнулся с этим, и я закончил использовать размер фиксированного массива. Массивы, которые я пытался хранить, имели переменную длину, поэтому я создал новые из них с правильной фиксированной длиной

Я сделал что-то в строках

def filled_list(src_list, targ_len):
    """takes a varible len() list and creates a new one with a fixed len()"""
    for i in range(targ_len):
        try:
            yield src_list[i]
        except IndexError:
            yield 0

src_list = [1,2,3,4,5,6,7,8,9,10,11]
new_list = [x for x in filled_list(src_list, 100)]

Это помогло мне.