Применение функции над строкой / столбцом матрицы numpy - PullRequest
36 голосов
/ 10 ноября 2011

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

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

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

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

Ответы [ 4 ]

43 голосов
/ 10 ноября 2011

Почти все функции 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 будет подавать по одному элементу за раз, и весь Операция будет довольно медленной (по сравнению с использованием простой функции, которая вызывает некоторую базовую реализацию 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
14 голосов
/ 10 ноября 2011

Выбор элементов из массива 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 использует двоеточие ":" нотация для обозначения "не нарезанный" , например, в R, чтобы получить первые три строки в A, вы должны использовать, A [1: 3,].В NumPy вы должны использовать A [0: 2,:] (в NumPy «0» не требуется, фактически предпочтительнее использовать A [: 2,:]

7 голосов
/ 09 января 2014

Я также пришел из более R-фона и столкнулся с отсутствием более универсального приложения, которое могло бы выполнять короткие настраиваемые функции. Я видел форумы, предлагающие использовать базовые функции 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) - это бит, который мы не можем напечатать напрямую для питона apply_along_axis. С помощью лямбды это легко реализовать ЗА ОДИН НАБОР ЗНАЧЕНИЙ, поэтому:

(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.

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

3 голосов
/ 04 февраля 2014

Панды очень полезны для этого.Например, DataFrame.apply () и apply by groupby () должны помочь вам.

...