Эффективный способ извлечения последних двух цифр каждого элемента в массиве numpy - PullRequest
4 голосов
/ 06 февраля 2020

рассмотрим этот пример:

sample = np.array([0, 1, 2, 3, 4])

Мне нужен самый быстрый способ создания списка / массива из последних 2 цифр двоичного представления каждого значения в sample. Таким образом, я получаю двоичное представление:

bin_sample = [bin(x) for x in sample]
>>> ['0b0', '0b1', '0b10', '0b11']

Я анализирую каждый из них следующим образом и получаю правильный вывод:

output = [bin(x)[-2:].replace('b','0') for x in sample]
>>> ['00', '01', '10', '11', '00']

Проблема в том, что это слишком медленно, я м обработки больших массивов, какие-либо предложения? Спасибо

РЕДАКТИРОВАТЬ: требуется 5 seconds для обработки 5 million элементов. Мне нужно взять ~ 1 second РЕДАКТИРОВАТЬ # 2: Любая оптимизация, которая получает ~ 500% увеличение скорости приемлемо сопоставимо с предыдущим алгоритмом.

Ответы [ 5 ]

3 голосов
/ 06 февраля 2020

Вот немного сложное решение:

def pp():
    a64 = a.astype(np.int64)
    return (((a64&1)<<32)+((a64&2)>>1)+ord('0')*0x100000001).view('U2')

Правильный поиск:

bits_map = np.array(['00', '01', '10', '11'])
def AMC_pp():
    return bits_map[a & 3]
3 голосов
/ 06 февраля 2020

Быстрые тесты

Настройка

import numpy as np
test_arr = np.random.randint(0, 10000000, 10000000)

1. Исходное решение

def last_two_bits(arr_in):
    return [bin(num)[-2:].replace('b','0') for num in arr_in]

Время: ~ 5200 мс


2. Решение от @ aminrd

bits_map = ['00','01','10','11']
def last_two_bits_nv(arr_in):
    return bits_map[arr_in % 4]

last_two_bits = np.vectorize(last_two_bits_nv)

Время: ~ 2600 мс


3. Мой твик к решению: @ aminrd

bits_map = np.array(['00', '01', '10', '11'])
def last_two_bits(arr_in):
    return bits_map[arr_in % 4]

Время: ~ 170 мс


4. Первое решение от @Paul Panzer

def last_two_bits(arr_in):
    return (((arr_in & 1) << 32) + ((arr_in & 2) >> 1) + ord('0') * 0x100000001).view('U2')

Время: ~ 100 мс


5. Оптимизированная версия метода 3, Пол Панцер

bits_map = np.array(['00', '01', '10', '11'])
def last_two_bits(arr_in):
    return bits_map[arr_in & 3]

Время: ~ 60 мс


6. Решение от @Mad Physicist

def last_two_bits(arr_in):
    output = np.empty((arr_in.size, 2), dtype=np.uint8)
    np.bitwise_and(arr_in >> 1, 1, out=output[:, 0], casting='unsafe')
    np.bitwise_and(arr_in, 1, out=output[:, 1], casting='unsafe')
    output += 48
    return output.view(dtype='S2').ravel()

Время: ~ 60 мс

1 голос
/ 06 февраля 2020

У меня нет доступа к инструменту бенчмаркинга, но мне интересно, поможет ли это:

output = np.empty((sample.size, 2), dtype=np.uint8)
np.bitwise_and(sample >> 1, 1, out=output[:, 0], casting='unsafe')
np.bitwise_and(sample, 1, out=output[:, 1], casting='unsafe')
output += 48
output = output.view(dtype='S2').ravel()
1 голос
/ 06 февраля 2020

Если вы ищете последние два бита в двоичном представлении, почему бы не отобразить элементы в ['00','01','10','11'] на основе element % 4.

import numpy as np
sample = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

map_list = ['00','01','10','11']

def f(x):
    return map_list[x % 4]

f = np.vectorize(f)

output = f(sample)

#['00', '01', '10', '11', '00', '01', '10', '11', '00', '01', '10']
0 голосов
/ 06 февраля 2020

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

import numpy as np

sample = np.array([0, 1, 2, 3, 4])
print([np.binary_repr(x, width=2)[-2:] for x in sample])

Это возвращает следующий вывод:

['00', '01', '10', '11', '00']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...