У меня есть набор данных, где первые 48 наблюдений представляют собой временные ряды, а остальные 12 - статические переменные:
h1 h2 h3 h4 ... h48 v1 v2 v3 v4 v5 v6 .. vn
h1 h2 h3 h4 ... h48 v1 v2 v3 v4 v5 v6 .. vn
форма одного элемента - (367, 60)
.
Я хочу передать переменные v1 v2 v3 v4 v5 v6.. vn
качестве дополнительных каналов для временных рядов, т.е. Создать массив формы (367, 48, 13)
. Я хочу сделать это на лету, так как полностью преобразованный набор данных не вписывается в мою оперативную память.
Код, который я использую сейчас, очень неэффективен (items
являются пакетными):
def preprocessor(items):
items_new = np.zeros(shape=(items.shape[0], 367, 48, 13), dtype=np.float32)
for idx_item, item in enumerate(items):
train_data = item[:,:48]
train_vars = item[:,48:]
train_new = np.zeros((train_data.shape[0], train_data.shape[1],(train_vars.shape[1]+1)))
for idx_row, row in enumerate(train_data):
for idx_col, elem in enumerate(row):
train_new[idx_row, idx_col, :] = np.concatenate([[elem], train_vars[idx_row]])
items_new[idx_item] = train_new
return items_new
Могу ли я сделать это быстрее без циклов?
РЕДАКТИРОВАТЬ:
Минимальный воспроизводимый пример:
arr = np.random.randn(5,367,60)
arr2 = preprocessor(arr)
print(arr2.shape) # (5, 367, 48, 13)
Подход №1
Мы могли бы использовать широковещательное назначение массива для векторизованного решения -
def array_assign(items):
L = 48 # slice at this column ID
N = items.shape[-1]
out = np.empty(shape= items.shape[:2] + (L,N-L+1), dtype=np.float32)
out[...,1:] = items[...,None,L:]
out[...,0] = items[...,:L]
return out
Подход №2
Мы могли бы также использовать широковещательный вид, а затем объединиться -
def broadcast_concat(items):
L = 48 # slice at this column ID
N = items.shape[-1]
a = items[...,:L,None]
shp_b = items.shape[:2] + (L,N-L)
b = np.broadcast_to(items[...,None,L:],shp_b)
out = np.concatenate((a,b),axis=-1)
return out
Сроки -
In [321]: items = np.random.rand(5,367,60)
In [322]: %timeit array_assign(items)
1000 loops, best of 3: 923 µs per loop
In [323]: %timeit broadcast_concat(items)
1000 loops, best of 3: 781 µs per loop
Для справедливого сравнения мы должны позволить второму методу использовать более эффективный float32
dtype. Позвольте использовать этот dtype для настройки входных данных и повторного тестирования -
In [335]: items = np.random.rand(5,367,60).astype(np.float32)
In [336]: %timeit array_assign(items)
1000 loops, best of 3: 897 µs per loop
In [337]: %timeit broadcast_concat(items)
1000 loops, best of 3: 348 µs per loop
Таким образом, для большинства наиболее эффективных для случая, когда требуется преобразование dtype, мы могли бы использовать items = np.asarray(items, dtype=np.float32)
в начале подхода # 2.
Это еще одно решение, используя повторение и конкатенацию.
a = items[:,:,:48, np.newaxis]
b = items[:,:,48:].repeat(a.shape[2], axis=1).reshape(*a.shape[:-1], -1)
return np.concatenate([a,b], axis=3)
items
массивом NumPy?