Numpy: как быстро преобразовать массив

Я считаю, что метод astype() массивов numpy не очень эффективен. У меня есть массив, содержащий 3 миллиона очков Uint8. Умножение на матрицу 3x3 занимает 2 секунды, но преобразование результата из uint16 в uint8 занимает вторую секунду.

Точнее:

    print time.clock()
    imgarray = np.dot(imgarray,  M)/255
    print time.clock()
    imgarray = imgarray.clip(0, 255)
    print time.clock()
    imgarray = imgarray.astype('B')
    print time.clock()

dot product и масштабирование занимает 2 сек
отсечение занимает 200 мс преобразование типа занимает 1 секунду

Учитывая время, затраченное на другие операции, я ожидаю, что astype будет быстрее. Есть ли более быстрый способ сделать преобразование типов, или я ошибаюсь, если для этого преобразования типов не должно быть так сложно?

Изменить: цель состоит в том, чтобы сохранить окончательный 8-битный массив в файл

Ответы

Ответ 1

Когда вы используете imgarray = imgarray.astype('B'), вы получаете копию массива, переданную указанному типу. Для этого требуется дополнительное выделение памяти, даже если вы сразу же переверните imgarray, чтобы указать на вновь выделенный массив.

Если вы используете imgarray.view('uint8'), вы получите представление о массиве. Это использует те же данные, за исключением того, что оно интерпретируется как uint8 вместо imgarray.dtype. (np.dot возвращает массив uint32, поэтому после np.dot, imgarray имеет тип uint32.)

Однако проблема с использованием view заключается в том, что 32-разрядное целое число рассматривается как 4 8-битные целые числа, и мы заботимся только о значении в последних 8 бит. Поэтому нам нужно пропустить каждое четвертое 8-битное целое число. Мы можем сделать это с нарезкой:

imgarray.view('uint8')[:,::4]

Команда IPython% timeit показывает, что происходит значительно ускоренное выполнение таких действий:

In [37]: %timeit imgarray2 = imgarray.astype('B')
10000 loops, best of 3: 107 us per loop

In [39]: %timeit imgarray3 = imgarray.view('B')[:,::4]
100000 loops, best of 3: 3.64 us per loop