Установить тип в pytables

У меня есть данные в следующем виде:

"blue red"
"blue magenta cyan"
"yellow red"
"black" 

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

`label in row`

Например, вернуть все документы, содержащие метку blue (результатом будут первые две строки). Каким будет наиболее эффективный способ добиться этого, учитывая тот факт, что Pytables не имеет типа данных set?


person elyase    schedule 07.12.2013    source источник


Ответы (1)


Тот факт, что у вас есть только максимум N=10, это здорово. Это означает, что такие сравнения возможны. Что вам нужно сделать, так это иметь 10 строковых столбцов, где каждый столбец является меткой. Если у вас меньше 10 меток для строки, вы заполняете ее пустыми строками.

Это позволит вам писать эффективные выражения запросов, которые вы можете использовать в командах Table.where() и Table.read_where() [1]. Предположим, столбцы имеют глупые имена 'col0', 'col1' и т. д. Поскольку сравнение строк в numexpr выполняется точно, а собственного типа набора нет, вам нужно явно развернуть сравнение на равенство:

cond = ("col0 == 'blue' | col1 == 'blue' | col2 == 'blue' | col3 == 'blue' | "
        "col4 == 'blue' | col5 == 'blue' | col6 == 'blue' | col7 == 'blue' | "
        "col8 == 'blue' | col9 == 'blue'")
rows = [row[:] for row in table.where(cond)]

К счастью, строку cond легко создать программно:

cond = " | ".join(["col{0} == 'blue'".format(i) for i in range(10)])

Однако есть еще кое-что, что вы можете сделать. Сравнение строк громоздкое и медленное. Это связано с тем, что все ваши строки должны иметь одинаковый размер, что означает, что размер столбца определяется вашей самой длинной меткой. Это приводит к большому количеству потерянного пространства. Вместо этого у вас должно быть сопоставление с целыми числами ваших меток. Затем вы можете сохранить целые числа, сравнить их очень быстро. Например, используя списковые индексы:

labels = ['', 'blue', 'red', 'yellow', ...]
labels_to_idx = dict(zip(labels, range(len(labels))))
cond = " | ".join(["col{0} == '{1}'".format(i, labels_to_idx['blue']) 
                   for i in range(10)])
rows = [[labels[x] for x in row[:]] for row in table.where(cond)]

Вы даже можете сохранить список меток в PyTables как EArray, чтобы всегда получать один и тот же порядок индексов, а также иметь возможность расширить список разрешенных меток.

Кроме того, поскольку метки будут использоваться повторно, особенно метка пустой строки, я настоятельно рекомендую включить сжатие.

К сожалению, поскольку индексируются столбцы (а не таблицы), вы не можете индексировать эти запросы.

Со сжатием и отображением в/из целых чисел это, вероятно, самое быстрое и самое маленькое, что вы можете получить.

  1. http://pytables.github.io/usersguide/libref/structured_storage.html?highlight=read_where#tables.Table.read_where
person Anthony Scopatz    schedule 09.12.2013