Numpy: применить массив функций к той же длине 2d-массива значений, как если бы умножать элементарно? (использование функции python в качестве оператора?)

я numpy.arrays, где столбцы содержат разные типы данных, и столбцы также должны иметь разные функции, применяемые к ним. У меня также есть функции в массиве.

допустим:

a = array([[ 1, 2.0, "three"],
           [ 4, 5.0, "six"  ]], dtype=object)

functions_arr = array([act_on_int, act_on_float, act_on_str])
functions_arr*a

и получить эффект

array([[act_on_int(1), act_on_float(2.0), act_on_str("three")],
       [act_on_int(4), act_on_float(5.0), act_on_str("six")  ]])

знаете ли вы способ достичь чего-то в этом направлении?

Edit: я изменил определение массива в вопросе, чтобы включить dtype=[object] как указывали люди, это важно для массива для хранения типов так, как я намеревался.

Спасибо за ваши ответы и комментарии! Я принял ответ senderles и чувствую, что это очень близко к тому, что у меня было в разум.

поскольку, похоже, была некоторая путаница в том, как я рассматриваю операцию как умножение, позвольте мне уточнить это с другим примером:

как вы хорошо знаете, операция, как:

v = array([1,2,3])
u = array([[5,7,11],
           [13,17,19]])
v*u

будет транслировать v со строки u и урожайности

array([[ 1*5, 2*7,  3*11],
       [1*13, 2*17, 3*19]])

то есть

array([[ 5, 14, 33],
       [13, 34, 57]])

если бы мы сейчас заменили v С, например,оператор-дель - мы бы (ниже не фактически рабочий код python:)

V = array([(d/dx),(d/dy),(d/dz)])
u = array([[5,7,11],
           [13,17,19]])
V*u

уступая (по духу)

array([[(d/dx)5, (d/dy)7, (d/dz)11]],
       [(d/dx)13,(d/dy)17,(d/dz)19]])

я признаю, что взятие производной от пучка констант не было бы самой интересной операцией, поэтому не стесняйтесь заменять u С некоторым символическим математическим выражением в x ,y и z. Во всяком случае, я надеюсь, что это, по крайней мере, делает более ясными как мои рассуждения, так и бит о "(используя функцию python как оператор?)" в названии.

3 ответов


как напомнил мне Свен Марнах, созданный вами массив, вероятно, представляет собой массив объектов Python. Любая операция на них, вероятно, будет намного медленнее, чем pure numpy операции. Тем не менее, вы можете сделать то, что вы просили довольно легко, если вы на самом деле не ожидаете, что это будет очень быстро! Это не слишком отличается от того, что AFoglia предложил, но это ближе к тому, что вы просили:

>>> a = numpy.array([[ 1, 2.0, "three"],
...                  [ 4, 5.0, "six"  ]], dtype=object)
>>> funcs = [lambda x: x + 10, lambda x: x / 2, lambda x: x + '!']
>>> apply_vectorized = numpy.vectorize(lambda f, x: f(x), otypes=[object])
>>> apply_vectorized(funcs, a)
array([[11, 1.0, three!],
       [14, 2.5, six!]], dtype=object)

также повторяя AFoglia здесь, есть хороший шанс, что вы лучше использовать массив записей - это позволяет разделить массив, как вам нравится, и работать с ним более естественным образом, используя numpy ufuncs -- которые намного быстрее, чем функции Python, как правило:

rec.array([(1, 2.0, 'three'), (4, 5.0, 'six')], 
      dtype=[('int', '<i8'), ('float', '<f8'), ('str', '|S10')])
>>> a['int']
array([1, 4])
>>> a['float']
array([ 2.,  5.])
>>> a['str']
rec.array(['three', 'six'], 
      dtype='|S10')
>>> a['int'] += 10
>>> a['int']
array([11, 14])

вы ищете встроенную функцию zip ()

простой пример использования lists:

>>> a=[[ 1, 2.0, "three"],[ 4, 5.0, "six"  ]]

>>> funcs=[lambda x:x**2,lambda y:y*2,lambda z:z.upper()]

>>> [[f(v) for v,f in zip(x,funcs)]for x in a]
[[1, 4.0, 'THREE'], [16, 10.0, 'SIX']]

Это не вещание, потому что исходный массив имел только одно измерение. Похоже, что он имеет 2 измерения, потому что каждый элемент имеет три элемента (int, float и строку), но для numpy это просто тип, а количество измерений-одно.

и это не умножение, потому что Вы применив функцию к каждому элементу. (Это не больше умножения, чем сложения, так что functions_arr * a вводит в заблуждение синтаксис.)

тем не менее, вы можете написать что-то аналогично тому, что вы хотите. Я бы попробовал numpy.векторизовать. Без тестирования и при условии, что выходной dtype совпадает с исходным массивом. Я представляю, как это будет...

def act_on_row(row) :
    return (act_on_int(row["int_field"]),
            act_on_float(row["float_field"]),
            act_on_str(row["str_field"]))

act_on_array = numpy.vectorize(act_on_row, otypes=[a.dtype])

acted_on = act_on_array(a)

Я никогда не пробовал векторизировать, и я не знаю, сложно ли работать со структурированными dtypes, но это должно заставить вас начать.

более простым решением было бы просто зациклить массив по полю.

rslt = numpy.empty((len(a),), dtype=a.dtype)

rslt["int_field"] = act_on_int(a["int_field"])
rslt["float_field"] = act_on_float(a["float_field"])
rslt["str_field"] = act_on_str(a["str_field"])

(возможно, Вам потребуется векторизовать каждого человека функция, в зависимости от того, что они делают.)