Bir istisna gibi nasıl numpy uyarısı yakalayabilirim?


174

Yaptığım bir proje için Python'da bir Lagrange polinomu yapmam gerekiyor. Ben Newton'un bölünmüş fark tarzı biri yerine açık bir for-loop kullanmaktan kaçınmak için barycentric tarzı bir yapıyorum. Benim sorunum sıfıra bir bölümü yakalamak gerekir, ama Python (veya belki numpy) sadece normal bir istisna yerine bir uyarı yapar.

Nasıl yapacağımı bilmem gereken şey bu uyarıyı bir istisna gibi yakalamak. Bu sitede bulduğum ilgili sorulara ihtiyacım olan şekilde cevap verilmedi. İşte benim kod:

import numpy as np
import matplotlib.pyplot as plt
import warnings

class Lagrange:
    def __init__(self, xPts, yPts):
        self.xPts = np.array(xPts)
        self.yPts = np.array(yPts)
        self.degree = len(xPts)-1 
        self.weights = np.array([np.product([x_j - x_i for x_j in xPts if x_j != x_i]) for x_i in xPts])

    def __call__(self, x):
        warnings.filterwarnings("error")
        try:
            bigNumerator = np.product(x - self.xPts)
            numerators = np.array([bigNumerator/(x - x_j) for x_j in self.xPts])
            return sum(numerators/self.weights*self.yPts) 
        except Exception, e: # Catch division by 0. Only possible in 'numerators' array
            return yPts[np.where(xPts == x)[0][0]]

L = Lagrange([-1,0,1],[1,0,1]) # Creates quadratic poly L(x) = x^2

L(1) # This should catch an error, then return 1. 

Bu kod yürütüldüğünde, aldığım çıktı:

Warning: divide by zero encountered in int_scalars

Yakalamak istediğim uyarı bu. Liste kavrayışı içinde gerçekleşmelidir.


2
Emin Warning: ...misin? np.array([1])/0Benim gibi şeyleri denemek RuntimeWarning: ...çıktı olarak alırım .
Bakuriu

1
@MadPhysicist Kopya değil; NumPy'nin pitonların üstünde, özellikle kontrol edilebilecek kendi iç uyarı mimarisi vardır (Bakuríu'nun cevabına bakınız).
gerrit

@gerrit. Düzeltildim ve yeni bir şey öğrendim. Rozet koleksiyonu çılgınlığını tetiklememek için orijinal yorumumu sildim.
Mad Physicist

Kullanabileceğiniz başka bir yaklaşım, paydanın bölmeden önce 0 olup olmadığını kontrol etmektir, bu da numpy'nin uyarı sistemi ile uğraşma yükünü önler. (Her ne kadar bu büyük olasılıkla herhangi bir payda sıfır olup olmadığını kontrol eden bir döngü içine temiz liste kavrayışını genişletmek zorunda anlamına gelecektir.)
Oliver

Yanıtlar:


198

Sizin yapılandırma kullanıyor görünüyor printseçeneğini numpy.seterr:

>>> import numpy as np
>>> np.array([1])/0   #'warn' mode
__main__:1: RuntimeWarning: divide by zero encountered in divide
array([0])
>>> np.seterr(all='print')
{'over': 'warn', 'divide': 'warn', 'invalid': 'warn', 'under': 'ignore'}
>>> np.array([1])/0   #'print' mode
Warning: divide by zero encountered in divide
array([0])

Bu, gördüğünüz uyarının gerçek bir uyarı olmadığı , ancak yalnızca basılan bazı karakterler olduğu anlamına gelir stdout(için belgelere bakın seterr). Yakalamak istiyorsanız şunları yapabilirsiniz:

  1. İstisnayı numpy.seterr(all='raise')doğrudan artıracak kullanım . Ancak bu, tüm işlemlerin davranışını değiştirir, bu nedenle davranışta oldukça büyük bir değişiklik olur.
  2. Kullanın numpy.seterr(all='warn'), yazdırılan uyarıyı gerçek bir uyarıda dönüştürür ve bu davranış değişikliğini yerelleştirmek için yukarıdaki çözümü kullanabilirsiniz.

Gerçekten bir uyarı aldığınızda warnings, uyarıların nasıl ele alınacağını kontrol etmek için modülü kullanabilirsiniz :

>>> import warnings
>>> 
>>> warnings.filterwarnings('error')
>>> 
>>> try:
...     warnings.warn(Warning())
... except Warning:
...     print 'Warning was raised as an exception!'
... 
Warning was raised as an exception!

filterwarningsYalnızca istediğiniz uyarıyı filtrelemenize izin verdiği ve başka seçenekleri olduğu için belgelerini dikkatle okuyun . Ayrıca catch_warnings, orijinal filterwarningsişlevi otomatik olarak sıfırlayan bir bağlam yöneticisi olana bakmayı düşünürüm :

>>> import warnings
>>> with warnings.catch_warnings():
...     warnings.filterwarnings('error')
...     try:
...         warnings.warn(Warning())
...     except Warning: print 'Raised!'
... 
Raised!
>>> try:
...     warnings.warn(Warning())
... except Warning: print 'Not raised!'
... 
__main__:2: Warning: 

Bence bu bir başlangıç. Ama aslında sorunumu düzeltmiyor. Deneme bloğundaki koduma warnings.warn (Warning ())) eklersem uyarıyı yakalar. Bazı nedenlerden dolayı bölünmeyi sıfır uyarısı ile yakalamaz. İşte tam uyarı mesajı: Uyarı: int_scalars'ta karşılaşılan sıfıra böl
John K.

@JohnK. Sorunuzu düzenlemeli ve kesin çıktıyı eklemelisiniz, aksi takdirde neyin yanlış olduğunu söyleyemeyiz. Olabilir numpy bu uyarı sınıfı bir yere tanımlar mümkün ve bunu yakalamak edebilmek için hangi alt paketin içinde Discovere gerekiyor. Boş ver, kullanman gerektiğini keşfettim RuntimeWarning. Yanıt güncellendi.
13'te Bakuriu

Emin misiniz? Kodumu RuntimeWarning dışında kullanmak için değiştirdim :. Hala çalışmıyor = /
John K.

@JohnK. Dokümantasyonda RuntimeWarninga'nın yükseltildiğini belirtir . Sorun, numpy konfigürasyonunuzun printuyarıyı basan seçeneği kullanması olabilir, ancak warningsmodül tarafından işlenen gerçek bir uyarı değildir ... Bu durumda, kullanmayı numpy.seterr(all='warn')ve tekrar deneyebilirsiniz.
Bakuriu

3
Benim sürümünde, numpykullanamazsınız numpy.seterr(all='error'), errorolması gerekiyor raise.
detly

41

@ Bakuriu'nun cevabına biraz eklemek için:

Uyarının nerede meydana gelme olasılığını zaten biliyorsanız , kodunuzda nerede olursa olsun aynı türden sonraki tüm uyarıları aynı şekilde ele almak numpy.errstateyerine , içerik yöneticisini kullanmak genellikle daha temizdir numpy.seterr:

import numpy as np

a = np.r_[1.]
with np.errstate(divide='raise'):
    try:
        a / 0   # this gets caught and handled as an exception
    except FloatingPointError:
        print('oh no!')
a / 0           # this prints a RuntimeWarning as usual

Düzenle:

Orijinal a = np.r_[0]örneğimde vardı , ancak görünüşe göre numpy'nin davranışında, payın tamamen sıfır olduğu durumlarda sıfıra bölme farklı işlenecek şekilde bir değişiklik oldu. Örneğin, numpy 1.16.4'te:

all_zeros = np.array([0., 0.])
not_all_zeros = np.array([1., 0.])

with np.errstate(divide='raise'):
    not_all_zeros / 0.  # Raises FloatingPointError

with np.errstate(divide='raise'):
    all_zeros / 0.  # No exception raised

with np.errstate(invalid='raise'):
    all_zeros / 0.  # Raises FloatingPointError

Karşılık gelen uyarı mesajları da farklıdır: 1. / 0.olarak kaydedilir RuntimeWarning: divide by zero encountered in true_divideoysa 0. / 0.olarak kaydedilir RuntimeWarning: invalid value encountered in true_divide. Emin neden tam bu değişikliğin yapıldığı değilim, ama sonucu gerçeği ile ilgisi yoktur şüpheli 0. / 0.bir sayı olarak gösterilebilen değil (numpy döner bu durumda bir NaN) oysa 1. / 0.ve -1. / 0.dönüş + Inf ve -Inf sırasıyla , IEE 754 standardına göre.

Her iki hata türünü de yakalamak istiyorsanız, her zaman geçebilirsiniz np.errstate(divide='raise', invalid='raise')veya herhangi bir kayan nokta hatası all='raise'için bir istisna oluşturmak istiyorsanız .


Özellikle yükselir FloatingPointError, değil ZeroDivisionError.
gerrit

Bu çalışmaz Python 3.6.3ile numpy==1.16.3. Lütfen güncelleyebilir misiniz?
anilbey

1
@anilbey Görünüşe göre numpy'nin davranışında, sıfıra bölünmenin artık payın da sıfır (sıfır) olup olmadığına bağlı olarak farklı işlendiği anlamına geliyor.
ali_m

27

Yukarıdaki @ Bakuriu'nun cevabını detaylandırmak için, bunun bir hata uyarısını nasıl yakalayacağımla aynı şekilde bir çalışma zamanı uyarısı yakalamamı sağladığını gördüm, uyarıyı güzelce yazdırıyorum:

import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        answer = 1 / 0
    except Warning as e:
        print('error found:', e)

Muhtemelen warnings.catch_warnings () yerleşimini yerleştirerek, bu şekilde hataları yakalamakla ne kadar büyük bir şemsiye atmak istediğinize bağlı olarak oynayabilirsiniz.


3
answer =
1/0

8

Uyarıları kaldırın. Filtre uyarıları ve ekleyin:

numpy.seterr(all='raise')
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.