Use math module for isfinite or isnan for scalars

Various places in the library attempt to check if scalars are finite
via numpy methods, which are intended to be used on numpy arrays.  Using
the math module equivalent functions on scalars is significantly faster.

In a few places, I also use numpy methods explicitly (np.all vs. all)
This commit is contained in:
Ogi Moore 2021-04-20 21:42:01 -07:00
parent b0769f4be9
commit 314121192a
9 changed files with 40 additions and 26 deletions

View File

@ -60,12 +60,10 @@ def siScale(x, minVal=1e-25, allowUnicode=True):
if isinstance(x, decimal.Decimal):
x = float(x)
try:
if np.isnan(x) or np.isinf(x):
if not math.isfinite(x):
return(1, '')
except:
print(x, type(x))
raise
if abs(x) < minVal:
m = 0
@ -295,9 +293,7 @@ def mkColor(*args):
else:
raise TypeError(err)
args = [r,g,b,a]
args = [0 if np.isnan(a) or np.isinf(a) else a for a in args]
args = list(map(int, args))
args = [int(a) if math.isfinite(a) else 0 for a in (r, g, b, a)]
return QtGui.QColor(*args)
@ -458,7 +454,7 @@ def eq(a, b):
1. Returns True if a IS b, even if a==b still evaluates to False.
2. While a is b will catch the case with np.nan values, special handling is done for distinct
float('nan') instances using np.isnan.
float('nan') instances using math.isnan.
3. Tests for equivalence using ==, but silently ignores some common exceptions that can occur
(AtrtibuteError, ValueError).
4. When comparing arrays, returns False if the array shapes are not the same.
@ -472,7 +468,7 @@ def eq(a, b):
# The above catches np.nan, but not float('nan')
if isinstance(a, float) and isinstance(b, float):
if np.isnan(a) and np.isnan(b):
if math.isnan(a) and math.isnan(b):
return True
# Avoid comparing large arrays against scalars; this is expensive and we know it should return False.

View File

@ -4,7 +4,7 @@ from ..python2_3 import asUnicode
import numpy as np
from ..Point import Point
from .. import debug as debug
from math import ceil, floor, log, log10
from math import ceil, floor, log, log10, isfinite
import sys
import weakref
from .. import functions as fn
@ -512,7 +512,7 @@ class AxisItem(GraphicsWidget):
def setRange(self, mn, mx):
"""Set the range of values displayed by the axis.
Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView <pyqtgraph.AxisItem.linkToView>`"""
if any(np.isinf((mn, mx))) or any(np.isnan((mn, mx))):
if not isfinite(mn) or not isfinite(mx):
raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx)))
self.range = [mn, mx]
if self.autoSIPrefix:
@ -767,7 +767,7 @@ class AxisItem(GraphicsWidget):
## remove any ticks that were present in higher levels
## we assume here that if the difference between a tick value and a previously seen tick value
## is less than spacing/100, then they are 'equal' and we can ignore the new tick.
values = list(filter(lambda x: all(np.abs(allValues-x) > spacing/self.scale*0.01), values))
values = list(filter(lambda x: np.all(np.abs(allValues-x) > spacing/self.scale*0.01), values))
allValues = np.concatenate([allValues, values])
ticks.append((spacing/self.scale, values))

View File

@ -1,4 +1,5 @@
from ..Qt import QtGui, QtCore
import math
import numpy as np
from ..colormap import ColorMap
from .GraphicsObject import GraphicsObject
@ -94,7 +95,7 @@ class NonUniformImage(GraphicsObject):
value = 0.0
elif np.isposinf(value):
value = 1.0
elif np.isnan(value):
elif math.isnan(value):
continue # ignore NaN
else:
value = (value - mn) / (mx - mn) # normalize

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from ..Qt import QtCore, QtGui, QtWidgets
HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget')
import math
import warnings
import numpy as np
from .GraphicsObject import GraphicsObject
@ -131,6 +131,8 @@ class PlotCurveItem(GraphicsObject):
elif ax == 1:
d = y
d2 = x
else:
raise ValueError("Invalid axis value")
## If an orthogonal range is specified, mask the data now
if orthoRange is not None:
@ -149,7 +151,7 @@ class PlotCurveItem(GraphicsObject):
# All-NaN data is acceptable; Explicit numpy warning is not needed.
warnings.simplefilter("ignore")
b = (np.nanmin(d), np.nanmax(d))
if any(np.isinf(b)):
if math.isinf(b[0]) or math.isinf(b[1]):
mask = np.isfinite(d)
d = d[mask]
if len(d) == 0:

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import warnings
import math
import numpy as np
from .. import metaarray as metaarray
from ..Qt import QtCore
@ -660,7 +661,7 @@ class PlotDataItem(GraphicsObject):
eps = np.finfo(y.dtype).eps
else:
eps = 1
y = np.sign(y) * np.log10(np.abs(y)+eps)
y = np.copysign(np.log10(np.abs(y)+eps), y)
ds = self.opts['downsample']
if not isinstance(ds, int):
@ -799,10 +800,10 @@ class PlotDataItem(GraphicsObject):
# All-NaN data is handled by returning None; Explicit numpy warning is not needed.
warnings.simplefilter("ignore")
ymin = np.nanmin(self.yData)
if np.isnan( ymin ):
if math.isnan( ymin ):
return None # most likely case for all-NaN data
xmin = np.nanmin(self.xData)
if np.isnan( xmin ):
if math.isnan( xmin ):
return None # less likely case for all-NaN data
ymax = np.nanmax(self.yData)
xmax = np.nanmax(self.xData)

View File

@ -1425,7 +1425,7 @@ class Handle(UIGraphicsItem):
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
## Make sure it is still ok to remove this handle
removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois])
removeAllowed = all(r.checkRemoveHandle(self) for r in self.rois)
self.removeAction.setEnabled(removeAllowed)
pos = ev.screenPos()
menu.popup(QtCore.QPoint(pos.x(), pos.y()))

View File

@ -6,6 +6,7 @@ try:
except ImportError:
imap = map
import itertools
import math
import numpy as np
import weakref
from ..Qt import QtGui, QtCore, QT_LIB
@ -116,7 +117,7 @@ def renderSymbol(symbol, size, pen, brush, device=None):
for more information).
"""
## Render a spot with the given parameters to a pixmap
penPxWidth = max(np.ceil(pen.widthF()), 1)
penPxWidth = max(math.ceil(pen.widthF()), 1)
if device is None:
device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32)
device.fill(0)
@ -950,6 +951,8 @@ class ScatterPlotItem(GraphicsObject):
elif ax == 1:
d = self.data['y']
d2 = self.data['x']
else:
raise ValueError("Invalid axis value")
if orthoRange is not None:
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import weakref
import sys
import math
from copy import deepcopy
import numpy as np
from ...Qt import QtGui, QtCore
@ -552,7 +553,7 @@ class ViewBox(GraphicsWidget):
xpad = 0.0
# Make sure no nan/inf get through
if not all(np.isfinite([mn, mx])):
if not math.isfinite(mn) or not math.isfinite(mx):
raise Exception("Cannot set range [%s, %s]" % (str(mn), str(mx)))
# Apply padding
@ -903,11 +904,11 @@ class ViewBox(GraphicsWidget):
targetRect[ax] = childRange[ax]
args['xRange' if ax == 0 else 'yRange'] = targetRect[ax]
# check for and ignore bad ranges
# check for and ignore bad ranges
for k in ['xRange', 'yRange']:
if k in args:
if not np.all(np.isfinite(args[k])):
r = args.pop(k)
if not math.isfinite(args[k][0]) or not math.isfinite(args[k][1]):
_ = args.pop(k)
#print("Warning: %s is invalid: %s" % (k, str(r))
if len(args) == 0:
@ -1369,10 +1370,20 @@ class ViewBox(GraphicsWidget):
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any():
if (
xr is None or
(xr[0] is None and xr[1] is None) or
not math.isfinite(xr[0]) or
not math.isfinite(xr[1])
):
useX = False
xr = (0,0)
if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any():
if (
yr is None or
(yr[0] is None and yr[1] is None) or
not math.isfinite(yr[0]) or
not math.isfinite(yr[1])
):
useY = False
yr = (0,0)

View File

@ -214,7 +214,7 @@ class RangeColorMapItem(ptree.types.SimpleParameter):
cmap = self.value()
colors = cmap.map(scaled, mode='float')
mask = np.isnan(data) | np.isinf(data)
mask = np.invert(np.isfinite(data))
nanColor = self['NaN']
nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.)
colors[mask] = nanColor