Функция массива Numpy с помощью оператора if

#python #numpy #matplotlib

#python #numpy #matplotlib

Вопрос:

Я использую Matplotlib и Numpy для создания некоторых графиков. Я хочу определить функцию, которая по заданному массиву возвращает другой массив со значениями, вычисляемыми поэлементно, например:

 def func(x):
     return x*10

x = numpy.arrange(-1,1,0.01)
y = func(x)
  

Это нормально. Теперь, однако, я хочу иметь внутри if-statement func , например:

 def func(x):
     if x<0:
          return 0
     else:
          return x*10

x = numpy.arrange(-1,1,0.01)
y = func(x)
  

К сожалению, это приводит к следующей ошибке

 Traceback (most recent call last):
  File "D:Scriptstest.py", line 17, in <module>
    y = func(x)
  File "D:Scriptstest.py", line 11, in func
    if x<0:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
  

Я просмотрел документацию для all() и any() , и они не соответствуют требованиям к тому, что мне нужно. Итак, есть ли хороший способ заставить функцию обрабатывать массивы по элементам, как в первом примере?

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

1. Вы должны решить, хотите ли вы обрабатывать x как int или как numpy.array . Или используйте ininstance() , чтобы проверить, какой тип передается

2. Что вы имеете в виду под x<0 ? x является массивом, поэтому неясно, что это должно означать.

3. @Bjoern Pollex Я знаю, что именно поэтому python запутался, я хочу применить эту функцию к каждому элементу массива по отдельности (т. Е. поэлементно)

4. @pajton Действительно, но есть ли способ сделать вторую обработку такой же изящной, как в первом примере?

5. Итак, каким именно вы хотите, чтобы результат был для иллюстрированного ввода?

Ответ №1:

Я знаю, что уже слишком поздно для этого ответа, но я взволнован изучением NumPy. Вы можете векторизовать функцию самостоятельно с помощью numpy.where.

 def func(x):
    import numpy as np
    x = np.where(x<0, 0., x*10)
    return x   
  

Примеры

Использование скаляра в качестве входных данных:

 x = 10
y = func(10)
y = array(100.0)
  

использование массива в качестве входных данных:

 x = np.arange(-1,1,0.1)
y = func(x)
y = array([ -1.00000000e 00,  -9.00000000e-01,  -8.00000000e-01,
    -7.00000000e-01,  -6.00000000e-01,  -5.00000000e-01,
    -4.00000000e-01,  -3.00000000e-01,  -2.00000000e-01,
    -1.00000000e-01,  -2.22044605e-16,   1.00000000e-01,
     2.00000000e-01,   3.00000000e-01,   4.00000000e-01,
     5.00000000e-01,   6.00000000e-01,   7.00000000e-01,
     8.00000000e-01,   9.00000000e-01])
  

Предостережения:

1) Если x это массив с маской, вам нужно использовать np.ma.where вместо этого, поскольку это работает для массивов с маской.

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

1. Ваше решение похоже на мое ( np.where и np.choose ведет себя аналогично в этом случае), но лучше!

Ответ №2:

Используйте numpy.vectorize для переноса функции перед применением ее к массиву x :

 from numpy import vectorize
vfunc = vectorize(func)
y = vfunc(x)
  

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

1. Аккуратно. Однако я верю, что мое решение работает быстрее, если вы используете операции, для которых у Numpy есть собственные реализации (хотя я не тестировал это, просто ощущение).

2. Конечно. Все встроенные операции, такие как те, которые вы создаете, будут быстрее, чем чистый python. Я просто нацеливался на «есть ли хороший способ заставить функцию обрабатывать массивы по элементам?» вопрос.

3. Интересно, есть ли способ сделать это эффективным при использовании функций, написанных на C (например, с помощью Cython). У меня такое чувство, что это может быть.

4. Принял этот ответ, так как мне был нужен читаемый код больше, чем скорость (в конце концов, это всего лишь небольшой скрипт) 🙂

Ответ №3:

Это должно делать то, что вы хотите:

 def func(x):
    small_indices = x < 10
    x[small_indices] = 0
    x[invert(small_indices)] *= 10
    return x
  

invert является Numpy-функцией. Обратите внимание, что это изменяет аргумент. Чтобы предотвратить это, вам придется изменить и вернуть copy of x .

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

1. Спасибо, кажется, это работает. Мне любопытно, почему x[x<0]=0 etc , похоже, не работает?

2. @Dan: Это действительно работает, но, как я это показываю, потенциально дорогостоящая операция x<0 должна выполняться только один раз (если подумать, invert вероятно, так же дорого, вам придется это проверить).

3. Ну, «x[x<0]= 0» работает для меня. (Python 2.6.1 numpy 2.0.0.dev-3071eab)

4. Я думаю, что использование np.where еще лучше.

Ответ №4:

(Я понимаю, что это старый вопрос, но …)

Есть еще один вариант, который здесь не упоминался — использование np.choose .

 np.choose(
    # the boolean condition
    x < 0,
    [
        # index 0: value if condition is False
        10 * x,
        # index 1: value if condition is True
        0
    ]
)
  

Хотя это и не очень читабельно, это всего лишь одно выражение (не серия операторов) и не ставит под угрозу скорость, присущую numpy (как np.vectorize делает).

Ответ №5:

 x = numpy.arrange(-1,1,0.01)
mask = x>=0
y = numpy.zeros(len(x))
y[mask] = x[mask]*10
  

mask является логическим массивом, который соответствует True индексам массива, соответствующим условию и False в другом месте. Последняя строка заменяет все значения в исходном массиве на это значение, умноженное на 10.

Отредактировано, чтобы отразить соответствующий комментарий Бьорна

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

1. Было бы еще приятнее (и правильнее), если бы вы сделали y = zeroes(len(x)) и затем y[mask] = x[mask] * 10 .

Ответ №6:

не уверен, зачем вам нужна функция

 x = np.arange(-1, 1, 0.01)
y = x * np.where(x < 0, 0, 10)