#python #numpy
#python #numpy
Вопрос:
У меня есть массив NumPy со следующими свойствами:
- форма: (9986080, 2)
- dtype: np.float32
У меня есть метод, который перебирает диапазон массива, выполняет операцию, а затем вводит результат в новый массив:
def foo(arr):
new_arr = np.empty(arr.size, dtype=np.uint64)
for i in range(arr.size):
x, y = arr[i]
e, n = ''
if x < 0:
e = '1'
else:
w = '2'
if y > 0:
n = '3'
else:
s = '4'
new_arr[i] = int(f'{abs(x)}{e}{abs(y){n}'.replace('.', ''))
Комментарии:
1. Какова цель этого кода? Я вижу, что она буквально делает, но я не уверен, почему вы это делаете. Знание того, что она должна делать, может помочь найти лучший ответ.
2. @Iguananaut Он принимает значения X Y, которые представляют центральные точки произвольных фигур, определяет квадрант, в котором находятся их широты и долготы, и создает из этого уникальный идентификатор, окончательно преобразуя его в int . Это преобразование необходимо для сохранения в хранилище в формате файла позже.
3. Хорошо, это имеет смысл. Мне любопытно, хотя, что это за формат файла? У меня недостаточно информации, чтобы сказать, хорошо ли мотивирована эта схема, но она кажется ужасно непрозрачной, если вы спросите меня.
4. К сожалению, @Iguananaut проприетарный. Тем не менее, я пытаюсь векторизовать это или найти способ применить это по всему массиву, а не повторять диапазон.
5. Каковы домены x и y, хотя. Если я не ошибаюсь, это не возвращает уникальное значение для любых произвольных x и y .
Ответ №1:
Я согласен с комментарием Iguananaut о том, что эта структура данных кажется немного странной. Моя самая большая проблема с этим заключается в том, что действительно сложно попытаться векторизовать объединение целых чисел в строку, а затем повторно преобразовать это в целое число. Тем не менее, это, безусловно, поможет ускорить работу функции:
def foo(arr):
x_values = arr[:,0]
y_values = arr[:,1]
ones = np.ones(arr.shape[0], dtype=np.uint64)
e = np.char.array(np.where(x_values < 0, ones, ones * 2))
n = np.char.array(np.where(y_values < 0, ones * 3, ones * 4))
x_values = np.char.array(np.absolute(x_values))
y_values = np.char.array(np.absolute(y_values))
x_values = np.char.replace(x_values, '.', '')
y_values = np.char.replace(y_values, '.', '')
new_arr = np.char.add(np.char.add(x_values, e), np.char.add(y_values, n))
return new_arr.astype(np.uint64)
Здесь значения x и y входного массива сначала разделяются. Затем мы используем векторизованное вычисление, чтобы определить, где e
и n
должно быть 1 или 2, 3 или 4. В последней строке используется стандартное понимание списка для выполнения бита слияния строк, который по-прежнему нежелательно медленный для сверхбольших массивов, но быстрее, чем обычный цикл for. Также векторизация предыдущих вычислений должна значительно ускорить работу функции.
Редактировать: я ошибался раньше. У Numpy действительно есть хороший способ обработки конкатенации строк с использованием метода np.char.add(). Для этого требуется преобразование x_values
и y_values
в массивы символов Numpy с использованием np.char.array()
. Также по какой-то причине np.char.add()
метод принимает только два массива в качестве входных данных, поэтому необходимо сначала объединить x_values
и e
и y_values
и n
и, а затем объединить эти результаты. Тем не менее, это векторизирует вычисления и должно быть довольно быстрым. Код все еще немного неуклюж из-за довольно странной операции, которую вы выполняете, но я думаю, что это поможет вам значительно ускорить работу функции.
Комментарии:
1. Предположим, вы сохранили ее в виде массива строк, есть ли более быстрый способ указать количество байтов, необходимое для строки, и присвоить строковое значение каждому элементу массива?
2. Изучил этот вопрос и нашел лучший способ сделать это. Смотрите мое обновление выше. Я думаю, что это довольно эффективно выполняет то, что вы хотели сделать.
Ответ №2:
Вы можете использовать np.apply_along_axis
. Когда вы передаете эту функцию другой функции, которая принимает строку (или столбец) в качестве аргумента, она делает то, что вы хотите сделать.
В вашем случае вы можете переписать функцию, как показано ниже:
def foo(row):
x, y = row
e, n = ''
if x < 0:
e = '1'
else:
w = '2'
if y > 0:
n = '3'
else:
s = '4'
return int(f'{abs(x)}{e}{abs(y){n}'.replace('.', ''))
# Where you want to you use it.
new_arr = np.apply_along_axis(foo, 1, n)