#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)