Функциональное приложение поверх строки / столбца матрицы numpy

#python #numpy #map-function

#python #numpy #map-функция

Вопрос:

Я использую Numpy для хранения данных в матрицах. Исходя из предыстории R, был чрезвычайно простой способ применить функцию к строке / столбцам или к обоим элементам матрицы.

Есть ли что-то подобное для комбинации python / numpy? Написать мою собственную небольшую реализацию не проблема, но мне кажется, что большинство версий, которые я придумаю, будут значительно менее эффективными / потребляющими больше памяти, чем любая из существующих реализаций.

Я хотел бы избежать копирования из матрицы numpy в локальную переменную и т.д., Возможно ли это?

Функции, которые я пытаюсь реализовать, в основном представляют собой простые сравнения (например, сколько элементов определенного столбца меньше числа x или сколько из них имеют абсолютное значение, большее y).

Комментарии:

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

Ответ №1:

Почти все функции numpy работают с целыми массивами и / или могут быть настроены для работы с определенной осью (строка или столбец).

Пока вы можете определять свою функцию в терминах функций numpy, действующих на массивы numpy или фрагменты массива, ваша функция будет автоматически работать с целыми массивами, строками или столбцами.

Возможно, было бы более полезно спросить о том, как реализовать конкретную функцию, чтобы получить более конкретный совет.


Numpy предоставляет np.vectorize и np.frompyfunc для преобразования функций Python, которые работают с числами, в функции, которые работают с массивами numpy.

Например,

 def myfunc(a,b):
    if (a>b): return a
    else: return b
vecfunc = np.vectorize(myfunc)
result=vecfunc([[1,2,3],[5,6,9]],[7,4,5])
print(result)
# [[7 4 5]
#  [7 6 9]]
  

(Элементы первого массива заменяются соответствующим элементом второго массива, когда второй становится больше.)

Но не стоит слишком волноваться; np.vectorize и np.frompyfunc это всего лишь синтаксический сахар. На самом деле они не ускоряют ваш код. Если ваша базовая функция Python работает с одним значением за раз, то np.vectorize будет передавать ему по одному элементу за раз, и вся операция будет довольно медленной (по сравнению с использованием функции numpy, которая вызывает некоторую базовую реализацию C или Fortran).


Чтобы подсчитать, сколько элементов столбца x меньше числа y , вы могли бы использовать такое выражение, как:

 (array['x']<y).sum()
  

Например:

 import numpy as np
array=np.arange(6).view([('x',np.int),('y',np.int)])
print(array)
# [(0, 1) (2, 3) (4, 5)]

print(array['x'])
# [0 2 4]

print(array['x']<3)
# [ True  True False]

print((array['x']<3).sum())
# 2
  

Комментарии:

1. Итак, нет простого способа запустить универсальную функцию? (просто любопытно, в общем случае функций numpy должно быть достаточно — мне нужно только выполнить простые сравнения, например, сколько элементов столбца x меньше числа y)

2. Звучит так, как будто вы могли бы делать подобные вещи с помощью срезов.

3. Большое вам спасибо! .. итак, если я использую array[‘x’]<3, это обрабатывается более быстрой реализацией numpy по сравнению с моей собственной векторизованной функцией?

4. Да, проблема с векторизованной функцией ручной работы заключается в том, что она сравнивает каждый элемент в подмассиве array['x'] с 3 по отдельности в цикле на основе Python. Если вы сравните весь подмассив array['x'] со скаляром (например, 3) в виде одного выражения ( array['x']<3 ), то numpy будет использовать широковещательную передачу для фактического обновления 3 до массива из 3 элементов той же формы, что и array['x'] , и выполнит поэлементное сравнение в C. За исключением того, что реальный массив из 3 элементов не создается, и операция выполняется намного быстрее, чем функция, закодированная на Python / векторизованная функция.

5. удивительно, это, вероятно, было бы способом оптимизировать его, не создавая слишком больших проблем. (другой способ, о котором я думал, — получить доступ к двум столбцам с помощью моей собственной реализации с использованием некоторых библиотек C-Python, но ускорение может не оправдать затраченных усилий)

Ответ №2:

Выбор элементов из массива NumPy на основе одного или нескольких условий прост, используя удивительно плотный синтаксис NumPy:

 >>> import numpy as NP
>>> # generate a matrix to demo the code
>>> A = NP.random.randint(0, 10, 40).reshape(8, 5)
>>> A
  array([[6, 7, 6, 4, 8],
         [7, 3, 7, 9, 9],
         [4, 2, 5, 9, 8],
         [3, 8, 2, 6, 3],
         [2, 1, 8, 0, 0],
         [8, 3, 9, 4, 8],
         [3, 3, 9, 8, 4],
         [5, 4, 8, 3, 0]])
  

сколько элементов в столбце 2 больше 6?

 >>> ndx = A[:,1] > 6
>>> ndx
      array([False,  True, False, False,  True,  True,  True,  True], dtype=bool)
>>> NP.sum(ndx)
      5
  

сколько элементов в последнем столбце A имеют абсолютное значение больше 3?

 >>> A = NP.random.randint(-4, 4, 40).reshape(8, 5)
>>> A
  array([[-4, -1,  2,  0,  3],
         [-4, -1, -1, -1,  1],
         [-1, -2,  2, -2,  3],
         [ 1, -4, -1,  0,  0],
         [-4,  3, -3,  3, -1],
         [ 3,  0, -4, -1, -3],
         [ 3, -4,  0, -3, -2],
         [ 3, -4, -4, -4,  1]])

>>> ndx = NP.abs(A[:,-1]) > 3
>>> NP.sum(ndx)
      0
  

сколько элементов в первых двух строках A больше или равно 2?

 >>> ndx = A[:2,:] >= 2
>>> NP.sum(ndx.ravel())    # 'ravel' just flattens ndx, which is originally 2D (2x5)
      2
  

Синтаксис индексации NumPy довольно близок к R; учитывая ваше свободное владение R, вот ключевые различия между R и NumPy в этом контексте:

Индексы NumPy основаны на нуле, в R индексация начинается с 1

NumPy (как и Python) позволяет вам индексировать справа налево с использованием отрицательных индексов — например,

 # to get the last column in A
A[:, -1], 

# to get the penultimate column in A
A[:, -2] 

# this is a big deal, because in R, the equivalent expresson is:
A[, dim(A)[0]-2]
  

NumPy использует двоеточие «:» для обозначения «unsliced», например, в R, для
получите первые три строки в A, которые вы бы использовали, A[1: 3, ]. В NumPy вы
использовал бы [0: 2, :] (в NumPy «0» не обязательно, на самом деле это
предпочтительнее использовать [:2, :]

Комментарии:

1. спасибо, я уже обращал внимание на адресацию массива numpy раньше, тем не менее, всегда полезно иметь хорошее резюме 🙂

Ответ №3:

Я также более разбираюсь в языке R и столкнулся с отсутствием более универсального приложения, которое могло бы использовать короткие настраиваемые функции. Я видел форумы, предлагающие использовать базовые функции numpy, потому что многие из них обрабатывают массивы. Тем не менее, я запутался в том, как «собственные» функции numpy обрабатывают массив (иногда 0 по строке и 1 по столбцу, иногда наоборот).

Моим личным решением для более гибких функций с помощью apply_along_axis было объединить их с неявными лямбда-функциями, доступными в python. Лямбда-функции должны быть очень просты для понимания тем, кто ориентирован на язык R и использует более функциональный стиль программирования, например, в функциях R apply, sapply, lapply и т.д.

Так, например, я хотел применить стандартизацию переменных в матрице. Обычно в R есть функция для этого (масштабирование), но вы также можете легко создать ее с помощью apply:

(R-код)

 apply(Mat,2,function(x) (x-mean(x))/sd(x) ) 
  

Вы видите, как тело функции внутри apply (x-mean(x)) / sd (x) — это бит, который мы не можем ввести напрямую для python apply_along_axis. С помощью lambda это легко реализовать ДЛЯ ОДНОГО НАБОРА ЗНАЧЕНИЙ, поэтому:

(Python)

 import numpy as np
vec=np.random.randint(1,10,10)  # some random data vector of integers

(lambda x: (x-np.mean(x))/np.std(x)  )(vec)
  

Затем все, что нам нужно, это подключить это к python apply и передать интересующий массив через apply_along_axis

 Mat=np.random.randint(1,10,3*4).reshape((3,4))  # some random data vector
np.apply_along_axis(lambda x: (x-np.mean(x))/np.std(x),0,Mat )
  

Очевидно, что лямбда-функция могла бы быть реализована как отдельная функция, но я предполагаю, что весь смысл заключается в использовании довольно небольших функций, содержащихся в строке, из которой исходил apply.

Я надеюсь, вы найдете это полезным!

Ответ №4: