Итак, сначала было похоже, что я столкнулся с другой ошибкой, но в конце всех тестов я совершенно не уверен, что это действительно ошибка, и что я понимаю, как построить конвейер обработки данных с HDF-пандами.
Садись, давай поедем вместе.Надеюсь, что вы можете уточнить для меня что-то в конце.
Подготовка
SIZE_0 = 10**6
SIZE_1 = 4
df = pd.DataFrame(np.random.rand(SIZE_0, SIZE_1))
print (df.head())
0 1 2 3
0 0.327362 0.084638 0.124322 0.116745
1 0.606545 0.484079 0.977239 0.120613
2 0.014407 0.973912 0.464409 0.959907
3 0.357551 0.641503 0.889408 0.776769
4 0.770845 0.548562 0.587054 0.569719
Положить в магазин на 2 части
cols1 = list(df.columns[:SIZE_1//2])
cols2 = list(df.columns[SIZE_1//2:])
with pd.HDFStore('test.h5') as store:
store.put('df1', df[cols1], 't')
store.put('df2', df[cols2], 't')
Теперь к проблеме.Чтение целого df из HDFStore
с select_as_multiple
на намного медленнее, чем select
:
%%time
with pd.HDFStore('test.h5') as store:
out = store.select_as_multiple(['df1', 'df2'])
print (out.shape)
(1000000, 4)
CPU times: user 24.3 s, sys: 38.6 ms, total: 24.3 s
Wall time: 24.3 s
И обычное select
:
%%time
with pd.HDFStore('test.h5') as store:
df1 = store.select('df1')
df2 = store.select('df2')
out = pd.concat([df1, df2], axis=1)
print (out.shape)
(1000000, 4)
CPU times: user 48.1 ms, sys: 23.9 ms, total: 72 ms
Wall time: 68.3 ms
Так что в этот момент я собирался опубликовать это как проблему с производительностью, но после некоторой первоначальной «тупой проверки» (как я и думал) я получаю неожиданные (по крайней мере для меня) результаты.
Давайте увеличим количество столбцов и посмотрим, что произойдет.
SIZE_1 = 8
df = pd.DataFrame(np.random.rand(SIZE_0, SIZE_1))
cols1 = list(df.columns[:SIZE_1//2])
cols2 = list(df.columns[SIZE_1//2:])
with pd.HDFStore('test.h5') as store:
store.put('df1', df[cols1], 't')
store.put('df2', df[cols2], 't')
Теперь, используя тот же код для select_as_multiple
, мы получим вывод:
(1000000, 8)
CPU times: user 14.7 s, sys: 87.3 ms, total: 14.8 s
Wall time: 14.8 s
Странные вещи.Мы увеличили размер данных вдвое, но время на стене теперь 10s
меньше.
В то же время код для извлечения df на select
выполняется немного медленнее:
(1000000, 8)
CPU times: user 90.6 ms, sys: 27.9 ms, total: 119 ms
Wall time: 115 ms
Ну, после этого я не смог остановить свое любопытство и сделать еще один пробный снимок :).Теперь с созданием экземпляра df с помощью SIZE_1 = 16
(снова все остальные строки кода остаются неизменными - не будем копировать их здесь снова для краткости).
Теперь select_as_multiple
работает даже быстрее :
(1000000, 16)
CPU times: user 8.27 s, sys: 184 ms, total: 8.45 s
Wall time: 8.45 s
Но для простого select
все как и ожидалось - время выполнения увеличилось:
(1000000, 16)
CPU times: user 181 ms, sys: 124 ms, total: 306 ms
Wall time: 302 ms
Но в то же время select
все еще работает очень- намного быстрее.
Напоследок на вопросы:
1.Почему `select_as_multiple` работает так плохо по сравнению с` select`?
Кстати, это проблема не только выбора без указания условия where
:
%%time
with pd.HDFStore('test.h5') as store:
out = store.select_as_multiple(['df1', 'df2'], where='index < 500000')
print (out.shape)
(500000, 16)
CPU times: user 4.65 s, sys: 56.7 ms, total: 4.7 s
Wall time: 4.69 s
И дляselect
:
%%time
with pd.HDFStore('test.h5') as store:
df1 = store.select('df1', where='index < 500000')
df2 = store.select('df2', where='index < 500000')
out = pd.concat([df1, df2], axis=1)
print (out.shape)
(500000, 16)
CPU times: user 871 ms, sys: 89 ms, total: 960 ms
The decreasing of time (with such particular `where`) should be more expected, since we have to
Wall time: 927 ms
Все еще намного быстрее.Но можно отметить , что where
уменьшает время на select_as_multiple
и в то же время увеличивает на select
.Итак, вот еще один вопрос:
2.Зачем указывать выражение `where` _reduces_ time для` select_as_multiple` и в то же время _increases_ его для `select`?
Ожидаемое поведение и увеличивается, или уменьшается для конкретного where
.Но не напротив друг друга.
3.Почему увеличение размера данных в направлении оси = 1 уменьшает время чтения `select_as_multiple`?
Мы увеличиваем размер данных, но выбор выполняется почти в несколько раз быстрее?Это странно.Может быть, это особенность дизайна, которая гласит - Не используйте хранилище HDF, пока у вас не будет действительно большого числа столбцов в вашем df ?Но я не мог вспомнить что-то подобное из документов .Только противоположные варианты использования - именно в разделе с select_as_multiple
, который предлагает разделить ваши данные на столбцы 'запроса' и 'другие' (таким образом уменьшите количество столбцов в хранимых dfs), чтобы ускорить запросы.
Давайте проведем еще больше тестов.
SIZE_0 = 10**6
и SIZE_1 = 16
:
%%time
with pd.HDFStore('test.h5') as store:
out = store.select_as_multiple(['df1', 'df2'])
print (out.shape)
(1000000, 16)
CPU times: user 8.39 s, sys: 232 ms, total: 8.62 s
Wall time: 8.64 s
Увеличение размера dfпо оси = 0 дважды SIZE_0 = 2*10**6
и SIZE_1 = 16
:
%%time
with pd.HDFStore('test.h5') as store:
out = store.select_as_multiple(['df1', 'df2'])
print (out.shape)
(2000000, 16)
CPU times: user 32.3 s, sys: 370 ms, total: 32.6 s
Wall time: 32.6 s
По сравнению с увеличением размера df по оси = 1 дважды SIZE_0 = 10**6
иSIZE_1 = 2*16
:
%%time
with pd.HDFStore('test.h5') as store:
out = store.select_as_multiple(['df1', 'df2'])
print (out.shape)
(1000000, 32)
CPU times: user 9.05 s, sys: 384 ms, total: 9.43 s
Wall time: 9.43 s
Итак 32s v 10s
.
4.Это означает, что для хранения HDF гораздо эффективнее добавлять столбцы вместо строк?!
Это действительно сбивает с толку.Разве это не неправильно?Насколько я понимаю, PyTables
так же, как pandas HDF
является «ориентированным на строки»?
В последнем тесте можно было заметить, что мы, наконец, увеличили время выполнения после расширения наших данных в axis=1
направление.Давайте выясним, когда именно это стало правдой:
SIZE_0 = 10**6
SIZE_1s = list(range(4, 40)) # List of SIZE_1 to iterate
# Iterating
timings = []
for SIZE_1 in SIZE_1s:
df = pd.DataFrame(np.random.rand(SIZE_0, SIZE_1))
cols1 = list(df.columns[:SIZE_1//2])
cols2 = list(df.columns[SIZE_1//2:])
with pd.HDFStore('test.h5') as store:
# Put to store
store.put('df1', df[cols1], 't')
store.put('df2', df[cols2], 't')
# Read from store, note the time
start = pd.Timestamp.now()
out = store.select_as_multiple(['df1', 'df2'])
# Appending timings
timings.append((pd.Timestamp.now()-start).total_seconds())
# Plotting
to_plot = pd.DataFrame(timings,
index=pd.Index(SIZE_1s, name='column_C'),
columns=['read time'])
_ = to_plot.plot(figsize = (14, 6),
title = 'DF read time from HDF store by DFs column count',
color = 'blue')
Итак, начальная динамика (до 23-24 столбцов) проста -больше столбцов = более быстрое чтение.
5.Является ли число столбцов 24 (мы должны разделить здесь для 2 df, то есть около 12 столбцов) что-то вроде порога проектирования?И только после достижения этого следует подумать об использовании HDF store?
Некоторая информация о системе:
pd.__version__
tables.__version__
'0.24.2'
'3.5.1'
Также есть 24GB
, установленная на 64-немного Ubuntu 19.04.И в то же время самый большой из использованных dfs в тестах был размером 300MB
.Так что это не должно было создать никаких проблем.
РЕДАКТИРОВАТЬ: , так как не было дано никакого объяснения - я открыл выпуск .