Numpy: транслировать значения строк в каналы

1

У меня есть набор данных, где первые 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)
  • 0
    Итак, являются ли items массивом NumPy?
  • 0
    @Divakar Да, это не список и не может быть.
Показать ещё 3 комментария
Теги:
numpy
performance
vectorization

2 ответа

1
Лучший ответ

Подход №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.

  • 1
    @Slowpoke Посмотрите, принесет ли только что добавленный подход 2 еще большую радость с вашим сценарием использования.
  • 0
    Спасибо, я попробовал второй код, но похоже, что узкого места сейчас нет. Я тренирую нейронную сеть на графическом процессоре, и это пользовательский уровень предварительной обработки, поэтому его необходимо учитывать, когда Keras рассчитывает время. Время по-прежнему 17 мс / шаг, в то время как загрузка одного ядра процессора немного снизилась с 85-86% для первого кода до 80-82% для второго. Я думаю, это означает, что предварительная обработка хороша и обеспечивает подачу нового пакета вовремя, в то время как узкое место в настоящее время является частью вычислений на GPU. Спасибо, тем не менее!
1

Это еще одно решение, используя повторение и конкатенацию.

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)
  • 0
    Я уже выбрал ответ, первый из которых уже сократил время выполнения моего кода с 1 часа до 8 минут, где производительность определяется другими узкими местами, но, тем не менее, спасибо! Возможно, это будет быстрее для более загруженного трубопровода, чем мой.
  • 1
    Нет проблем! Это мое упражнение на NumPy. Мне понравилось.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню