Как избежать копирования кода для обработки исключений, неопределенных переменных и некоторых сравнений одинаково - PullRequest
0 голосов
/ 26 мая 2018

У меня есть фрагмент кода, который соответствует каждому вокселю в кубе данных с гауссовской плюс ненулевой наклонной линейной базовой линией.Каждый воксел представляет собой спектр излучения линии + континуума, который, в зависимости от местоположения, может быть довольно шумным, и известно, что он не ведет себя хорошо на краях изображения или спектрального диапазона.Поэтому иногда бывает необходимо установить гауссовские и линейные компоненты отдельно в тех частях спектров, где каждый из них наиболее вероятен, либо потому, что исходная подгонка не удалась, либо потому, что параметры подгонки были бессмысленными, и я могу видеть линию, которую не удалосьподходит, несмотря на шум.Я бы перешел прямо к кусочной версии, если бы мог, но разрывы могут быть проблематичными, не говоря уже о том, что это обычно более дорогая процедура с точки зрения использования времени и памяти.

Итак, моя ситуация такова: Я хочу, чтобы моя программа отвечала на несколько возможных условий, где некоторые являются исключениями (ValueError и RuntimeError), а другие - логическими или реляционными условиями, с той же процедурой .Это может быть сделано?Прямо сейчас у меня есть две копии одной и той же процедуры, одна в блоке исключений, а другая в блоке else с внутренним оператором if, и это меня раздражает.Если я не могу объединить их, есть ли способ перенаправить один или другой оператор в один и тот же блок кода без определения новой функции?

Редактировать: я изначально не включал это, потому что мои попыткиреорганизация делала беспорядок в моем коде в то время, когда я писал это.Теперь, когда я немного собрал все вместе (это все еще беспорядок, поскольку каждый протокол соответствует примерно 6 различным случаям 3 разных категорий, которые, насколько я знаю, невозможно проверить одновременно), вот соответствующийчасти моего кода:

if fitfn not in ['gauss','lorentz']:
    raise IOError("Fitting function name must be 'lorentz' or 'gauss'")
cubedims=np.shape(cube)
frames=np.array([(n,w) for n,w in enumerate(wvl) if (lc-0.2<w<lc+0.2)])
#^ 3 sigma is about 27% larger than 2 fwhm
inds,wls=np.transpose(frames)
# store amplitudes of Gaussian/Lorentz profile & their errors:
fdencube=np.zeros((cubedims[1],cubedims[2]))
fduncube=np.zeros((cubedims[1],cubedims[2]))
# Store spectral index (slope) of linear continuum fit:
spindex=np.zeros((cubedims[1],cubedims[2]))
spundex=np.zeros((cubedims[1],cubedims[2]))
# Store continuum-subtracted line profiles:
lincube=np.zeros((len(frames),cubedims[1],cubedims[2]))
elincube=np.zeros((len(frames),cubedims[1],cubedims[2]))
# store line-subtracted continuum profiles:
concube=np.zeros((cubedims))
econcube=np.zeros((cubedims))
for x in xrange(cubedims[1]):
    for y in xrange(cubedims[2]):
        spec = cube[:,x,y]
        uspec = ecube[:,x,y]
        try: 
            p,pcov=curvf(globals()[fitfn], wvl[~np.isnan(spec)], 
                         spec[~np.isnan(spec)],sigma=uspec[~np.isnan(spec)],
                         bounds = [[0.01,min(wvl),np.mean(wvl[1:]- 
                                    wvl[:-1]),-10,0.],
                                   [50.,max(wvl),0.4,10,10.0]])
            fwhm=2*abs(p[2]) if fitfn=='lorentz' else 
                 p[2]*np.sqrt(8*np.log(2))
            if (fwhm < 0.16 and (lc-0.05<p[1]<lc+0.05) and 'pcov' in 
                globals()):
                stdp=np.sqrt(np.diag(pcov))
                cvw=p[-2]*frames[:,1]+p[-1]
                lincube[:,x,y]=spec[inds.astype(int)]-cvw
                elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
                                        stdp[-2]**2+stdp[-1]**2)

                lvw=gauss(wvl,p[0],p[1],p[2],0,0)
                concube[:,x,y]=spec-lvw
                econcube[:,x,y]=np.sqrt(uspec**2+stdp[0]**2+
                                        stdp[1]**2+stdp[2]**2)
                spindex[x,y]=p[-2]
                spundex[x,y]=stdp[-2]
                fdencube[x,y]=p[0]
                fduncube[x,y]=stdp[0]
            else:
                try:
                    s=spec[~inds.astype(int)]
                    u=uspec[~inds.astype(int)]
                    q,qcov=curvf(lreg,wls[~np.isnan(s)],s[~np.isnan(s)],
                                 sigma=u[~np.isnan(s)],bounds = [[-10,0.], 
                                 [10,10.0]])
                    r,rcov=curvf(globals([fitfn],wvl[inds.astype(int)], 
                                 spec[inds.astype(int)],
                                 sigma=uspec[inds.astype(int)],
                                 bounds = [[0.01,min(wvl),np.mean(wvl[1:]- 
                                 wvl[:-1]),-10,0.], 
                                 [50.,max(wvl),0.4,10,10.0]])
                    fwhmr=2*abs(r[2]) if fitfn=='lorentz' else 
                          r[2]*np.sqrt(8*np.log(2))
                    if (fwhmr < 0.16 and (lc-0.05<r[1]<lc+0.05) and 'rcov' 
                        in globals()):
                        stdr=np.sqrt(np.diag(rcov))
                        stdq=np.sqrt(np.diag(qcov))

                        lvw=gauss(wvl,r[0],r[1],r[2],0,0)
                        concube[:,x,y]=spec-lvw
                        econcube[:,x,y]=np.sqrt(uspec**2+stdr[0]**2+
                                        stdr[1]**2+stdr[2]**2)
                        cvw=q[0]*frames[:,1]+q[1]
                        lincube[:,x,y]=spec[inds.astype(int)]-cvw
                        elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
                                                stdq[-2]**2+stdq[-1]**2)

                        spindex[x,y]=q[0]
                        spundex[x,y]=stdq
                        fdencube[x,y]=r[0]
                        fduncube[x,y]=stdr[0]
                except (ValueError,RuntimeError):
                    fdencube[x,y]=np.NaN
                    fduncube[x,y]=np.NaN
                    lincube[:,x,y]=np.NaN
                    elincube[:,x,y]=np.NaN                        
                    try:

                    q,qcov=curvf(lreg,wvl[~np.isnan(spec)], 
                           spec[~np.isnan(spec)],
                           sigma=uspec[~np.isnan(spec)],bounds = [[-10,0.], 
                           [10,10.0]])

                        if 'qcov' in globals():
                            concube[:,x,y]=spec
                            econcube[:,x,y]=uspec
                            spindex[x,y]=q[0]
                            spundex[x,y]=np.sqrt(np.diag(qcov))[0]                              
                        else:
                            concube[:,x,y]=spec
                            econcube[:,x,y]=uspec
                            spindex[x,y]=q[0]
                            spundex[x,y]=np.NaN
                    except (ValueError,RuntimeError):
                        print 'fit failed'
                        concube[:,x,y]=spec
                        econcube[:,x,y]=uspec
                        spindex[x,y]=np.NaN
                        spundex[x,y]=np.NaN
        except (ValueError,RuntimeError):
            try: 
                s=spec[~inds.astype(int)]
                u=uspec[~inds.astype(int)]
                q,qcov=curvf(lreg,wls[~np.isnan(s)],s[~np.isnan(s)],
                             sigma=u[~np.isnan(s)],bounds = [[-10,0.], 
                                   [10,10.0]])
                r,rcov=curvf(globals()[fitfn],wvl[inds.astype(int)], 
                             spec[inds.astype(int)],
                             sigma=uspec[inds.astype(int)],
                             bounds = [[0.01,min(wvl),np.mean(wvl[1:]- 
                                      wvl[:-1]),-10,0.],
                               [50.,max(wvl),0.4,10,10.0]])
                fwhmr=2*abs(r[2]) if fitfn=='lorentz' else 
                      r[2]*np.sqrt(8*np.log(2))
                if (fwhmr < 0.16 and (lc-0.05<r[1]<lc+0.05) and 'rcov' in 
                   globals()):
                    stdr=np.sqrt(np.diag(rcov))
                    stdq=np.sqrt(np.diag(qcov))

                    lvw=gauss(wvl,r[0],r[1],r[2],0,0)
                    concube[:,x,y]=spec-lvw
                    econcube[:,x,y]=np.sqrt(uspec**2+stdr[0]**2+
                                    stdr[1]**2+stdr[2]**2)
                    cvw=q[0]*frames[:,1]+q[1]
                    lincube[:,x,y]=spec[inds.astype(int)]-cvw
                    elincube[:,x,y]=np.sqrt(uspec[inds.astype(int)]**2+
                                            stdq[-2]**2+stdq[-1]**2)

                    spindex[x,y]=q[0]
                    spundex[x,y]=stdq
                    fdencube[x,y]=r[0]
                    fduncube[x,y]=stdr[0]
            except (ValueError,RuntimeError):
                fdencube[x,y]=np.NaN
                fduncube[x,y]=np.NaN
                lincube[:,x,y]=np.NaN
                elincube[:,x,y]=np.NaN                    
                try:
                    q,qcov=curvf(lreg,wvl[~np.isnan(spec)], 
                                spec[~np.isnan(spec)],
                                sigma=uspec[~np.isnan(spec)],bounds = 
                                [[-10,0.],[10,10.0]])
                    if 'qcov' in globals():
                        concube[:,x,y]=spec
                        econcube[:,x,y]=uspec
                        spindex[x,y]=q[0]
                        spundex[x,y]=np.sqrt(np.diag(qcov))[0]
                    else:
                        print 'fit failed'
                        concube[:,x,y]=spec
                        econcube[:,x,y]=uspec
                        spindex[x,y]=np.NaN
                        spundex[x,y]=np.NaN

                except (ValueError,RuntimeError):
                    #if fit fails, assume it's continuum;
                    # continuum spectral profile doesn't matter as much
                    print 'fit failed'
                    concube[:,x,y]=spec
                    econcube[:,x,y]=uspec
                    spindex[x,y]=np.NaN
                    spundex[x,y]=np.NaN

Как вы можете видеть, это беспорядок дублирования, потому что я не знаю, как проверить исключения, существование переменной и сравнение переменной (если она существует) с другойПеременная все в одной строке.Я нахожусь в процессе написания модуля, который проверяет наличие переменных и выполняет логические и реляционные операции, так что если переменная не определена, возвращаемое значение всегда False, но это все равно оставляет исключения.Есть ли способ сохранить исключение?

Ответы [ 2 ]

0 голосов
/ 29 мая 2018

Я понял, как решить суть моей проблемы.Чтобы проверить существование переменной и использовать реляционный или логический оператор на ней в той же строке - например, проверить, существует ли var1 и находится ли он между var2 - 0.05 и var2 + 0.05 - синтаксис следующий:

if 'var1' in globals() and (var2 - 0.05) < globals()['var1'] < (var2 + 0.05):
     do stuff

Вы также можете заменить globals() на locals(), если вы ищете переменную, которая, за исключением исключения, должна быть определена в той же функции, в которой вы должны проверить ее вболее позднее if-утверждение.Есть какой-то нюанс в принятии решения о том, какой из них использовать, и который ненадолго вызвал у меня небольшую головную боль (более подробная информация на В чем разница между globals (), locals () и vars ()? ): если вашопределение локальной переменной включает в себя глобальную переменную, locals() может не поймать ее.Но если вы используете globals(), вы должны убедиться, что, если вы работаете в командной строке или в консоли (например, IPython или JuPyter), вы не использовали имя переменной, для которой вы собираетесь проверять,Если у вас есть, вы должны удалить его с помощью команды del. Несмотря на это, часть if 'var1' in globals() / locals() важна - это то, что не дает последующему условному выражению поднять NameError.

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

  • полностью потерпеть неудачу (т.е. вызвать ValueError или RuntimeError)
  • приведет к невозможности(полная инфс) ковариационная матрица или
  • имеют определенные параметры, согласующиеся с математически приемлемыми, но физически нереалистичными значениями, от которых невозможно избавиться с помощью kwarg "bounds" scipy.optimize.curve_fit (или другой подходящей подпрограммы)может просто вернуть ваши пределы в качестве параметров, которые последующие операторы if не перехватят).

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

try:
    p,pcov=curve_fit(gauss,wavelens,spectrum,sigma=s,bounds=lims)
    fwhm = p[2]*np.sqrt(8*np.log(2))
    goodlc = linecenter-0.05<p[1]<linecenter+0.05
    goodcov = np.inf not in pcov
except (ValueError,RuntimeError,NameError) as e:
    #'as e' isn't needed if you don't use it below
    print e #I don't technically need this anymore;
    #I only used it to see why it threw an OptimizeWarning when pcov was fine
    pass #necessary if previous line isn't included.

if (('fwhm' in locals() and locals()['fwhm']<0.16) and 
    ('goodlc' in globals() and globals()['goodlc'] is True) and
    ('goodcov' in locals() and locals()['goodcov'] is True)):
    #store results in arrays and stuff...
else:
    try:
        #...etc, same stuff as above with a different function & nested.
        #my program tries 3 different functional forms before giving up

Curve_fit выбрасывает ValueError, если в данных есть NaN для подгонки, и RuntimeError, если не удается найти оптимальные параметры.NameError ловит попытки вычислить FWHM, если соответствующий параметр в p не существует.Другие строки перед исключением определяют некоторые условные выражения, чтобы последний оператор if не становился слишком длинным.

0 голосов
/ 26 мая 2018

Технически, да.
Ваш код должен захватывать исключение в except и обрабатывать любые частичные действия в блоке try, которые могли произойти.Логика для всего этого должна быть после блока excpet

def i_div(x,y):
    exp = None
    try:
        res =  x/y;
    except Exception as e:
        exp = e
    if isinstance(exp, ZeroDivisionError) and x == 0:
        return 'undefined'
    elif isinstance(exp, ZeroDivisionError):
        return 'infinity'
    elif isinstance(exp, TypeError):
        return 'some_value missing'
    else: 
        return res

i_div(1, 0)
i_div(0, 0)
i_div(0, 1)
i_div(1, 2)

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

Если возможно, избегайте такого подхода.Exception - это способ идентифицировать плохое, предотвращающее ожидаемый поток (успешное выполнение) программы.

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