«ValueError: Значение истинности массива с более чем одним элементом неоднозначно».при использовании scipy.integrate.dblquad - PullRequest
0 голосов
/ 11 июня 2019

Я пытаюсь выполнить двойную интеграцию для этой функции

def prob(x,y):
    ch = np.sqrt((3-y)**2 + x**2)
    hb = np.sqrt((4-x)**2 + y**2)
    if np.isclose(4 * y, 12 - 3 * x):
        # Without the if statement, any x, y values that satisfy the condition will return nan
        return 0.5
    else:
        return (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)

Контурный график функции

from scipy import integrate as integ
integ.dblquad(prob(x,y), 0, 4, lambda x: 0, lambda x: 3)

Однако я получил это сообщение об ошибкеуказывая на оператор if

ValueError: Значение истинности массива с более чем одним элементом неоднозначно.Используйте a.any () или a.all ()

Тривиально, если я использую math.isclose вместо np.isclose в операторе if, Я получаю это сообщение об ошибке, указывающее на ту же фразуделать с этими ошибками, но проверка всего, включая оператор if, дает скалярное логическое значение.

Так есть ли способ обойти это?

Ответы [ 3 ]

1 голос
/ 11 июня 2019

Возможно, это типографская ошибка.

Из внесенного мною редактирования похоже, что вы звоните:

integ.dblquad(prob(x, y), 0, 4, lambda x: 0, lambda x: 3)

Когда на самом деле вы должны звонить:

integ.dblquad(prob, 0, 4, lambda x: 0, lambda x: 3)

Полагаю, у вас, вероятно, есть и массив x, и массив y, которые вызывают ошибку при вызове dblquad первым способом.

Например,

x = np.array([1,2,3])
y = np.array([3,4,5])
integ.dblquad(prob(x, y), 0, 4, lambda x: 0, lambda x: 3)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

integ.dblquad(prob, 0, 4, lambda x: 0, lambda x: 3)
(4.42854383700761, 1.8525461432365937e-08)
0 голосов
/ 11 июня 2019

Краткий ответ - вам необходимо предоставить полную трассировку и спецификацию вызова.И вам нужно правильно вызвать dblquad.

===

Что такое x,y, когда вы звоните с

integ.dblquad(prob(x,y), 0, 4, lambda x: 0, lambda x: 3)

В зависимости от этих двух переменных,ошибка может возрастать до того, как dblquad даже будет вызван.

Первый аргумент dblquad должен быть функцией, а не массивом.Использование prob вместо prob(x,y) может работать.

Учитывая 2 скаляра prob работает:

In [674]: prob(1,2)                                                                                    
Out[674]: 0.4685835209054995

Учитывая два массива, мы получаем ошибку неоднозначности:

In [675]: prob(np.arange(3),np.arange(1,4))                                                            
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-675-4b16a88f567e> in <module>
----> 1 prob(np.arange(3),np.arange(1,4))

<ipython-input-673-e31785dd54a5> in prob(x, y)
      2     ch = np.sqrt((3-y)**2 + x**2)
      3     hb = np.sqrt((4-x)**2 + y**2)
----> 4     if np.isclose(4 * y, 12 - 3 * x):
      5         # Without the if statement, any x, y values that satisfy the condition will return nan
      6         return 0.5

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

И полная обратная трассировка (которую вы должны были предоставить нам) показывает, что проблема заключается в вызове isclose:

In [676]: np.isclose(np.arange(3),np.arange(1,4))                                                      
Out[676]: array([False, False, False])

. Он создает логический массив, который нельзя использовать в if предложение.

Использование math.isclose вызывает ошибку, поскольку функции math принимают только скаляры.Они не работают с массивами (многозначными).

При правильном использовании dblquad это работает:

In [678]: integ.dblquad(prob, 0,4,lambda x:0, lambda x:3)                                              
Out[678]: (4.42854383700761, 1.8525461432365937e-08)

dblquad передает скалярные значения в вашу функцию, поэтому использованиеisclose (либо числовой, либо математический) не проблема

Использование math функций со скалярными входами быстрее:

def probm(x,y):
    ch = math.sqrt((3-y)**2 + x**2)
    hb = math.sqrt((4-x)**2 + y**2)
    if math.isclose(4 * y, 12 - 3 * x):
        # Without the if statement, any x, y values that satisfy the condition will return nan
        return 0.5
    else:
        return (math.acos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*math.pi)

In [683]: integ.dblquad(probm, 0,4,lambda x:0, lambda x:3)                                             
Out[683]: (4.428543836134556, 1.8890715880459652e-08)

намного быстрее:

In [685]: timeit integ.dblquad(prob, 0,4,lambda x:0, lambda x:3)                                       
11.7 s ± 24.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [686]: timeit integ.dblquad(probm, 0,4,lambda x:0, lambda x:3)                                      
272 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
0 голосов
/ 11 июня 2019

Ну, ошибки, которые вы получаете, достаточно полезны с точки зрения диагностики.Проблема в том, что np.isclose вернет массив логических значений, и попытка привести такой массив в одно значение (что делает предложение if) вызывает ошибку.(1)

Как это исправить, больше зависит от того, чего вы хотите достичь.Предполагая, что prob(x,y) должен возвращать пустой массив тех же размеров, что и x / y, где значения заменяются на 0.5 в местах, где условие выполняется, вы можете использовать:

output = (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)
output[np.isclose(4 * y, 12 - 3 * x)] = .5
return output

Обратите внимание, что это можетне лучший способ решить вашу актуальную проблему.Часто имеет смысл сохранять NaN в тех местах, где расчет не удался (для этого и нужны NaN).Если вы хотите заменить NaN на 0,5, вам лучше проверить NaN, а не по математическим условиям:

output = (np.arccos((ch**2 + hb**2 - 25) / (2 * ch * hb)))/(2*np.pi)
output[np.isnan(output)] = .5
return output

(1) Можно утверждать, что любой непустой массив должен иметь значение * 1014.* аналогично тому, что списки делают в непустом Python, и независимо от значений (т. е. np.asarray([False]) оценивается как True).Я, например, счастлив, что numpy вызовет исключение, а не попытается угадать, что я хочу в этой ситуации (которая может быть all, any, len(array)>0, или как здесь поэлементная операция, которую необходимо переписать).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...