Skip to content

Commit 06e97fb

Browse files
committed
filter out negative error values
1 parent e0b663c commit 06e97fb

File tree

2 files changed

+40
-26
lines changed

2 files changed

+40
-26
lines changed

src/silx/gui/plot/items/core.py

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from copy import deepcopy
3535
import logging
3636
import enum
37+
import numbers
3738
from typing import Optional, Tuple, Union
3839
import weakref
3940

@@ -1422,7 +1423,7 @@ def getCurrentVisualizationParameter(self, parameter):
14221423
class PointsBase(DataItem, SymbolMixIn, AlphaMixIn):
14231424
"""Base class for :class:`Curve` and :class:`Scatter`"""
14241425

1425-
# note: _logFilterData must be overloaded if you overload
1426+
# note: _filterData must be overloaded if you overload
14261427
# getData to change its signature
14271428

14281429
_DEFAULT_Z_LAYER = 1
@@ -1517,8 +1518,22 @@ def _getClippingBoolArray(self, xPositive, yPositive):
15171518
)
15181519
return self._clippedCache[(xPositive, yPositive)]
15191520

1520-
def _logFilterData(self, xPositive, yPositive):
1521-
"""Filter out values with x or y <= 0 on log axes
1521+
@staticmethod
1522+
def _filterNegativeValues(
1523+
data: numpy.ndarray | numbers.Number | None,
1524+
) -> numpy.ndarray | numbers.Number | None:
1525+
"""Returns data with negative values to 0"""
1526+
if data is None:
1527+
return None
1528+
1529+
# Convert data to array to avoid specific case for complex scalar
1530+
if numpy.all(numpy.array(data, copy=False) >= 0):
1531+
return data
1532+
1533+
return numpy.clip(data, 0, None) # Also works for scalars
1534+
1535+
def _filterData(self, xPositive, yPositive):
1536+
"""Filter out errors<0 and values with x or y <= 0 on log axes
15221537
15231538
:param bool xPositive: True to filter arrays according to X coords.
15241539
:param bool yPositive: True to filter arrays according to Y coords.
@@ -1527,8 +1542,8 @@ def _logFilterData(self, xPositive, yPositive):
15271542
"""
15281543
x = self.getXData(copy=False)
15291544
y = self.getYData(copy=False)
1530-
xerror = self.getXErrorData(copy=False)
1531-
yerror = self.getYErrorData(copy=False)
1545+
xerror = self._filterNegativeValues(self.getXErrorData(copy=False))
1546+
yerror = self._filterNegativeValues(self.getYErrorData(copy=False))
15321547

15331548
if xPositive or yPositive:
15341549
clipped = self._getClippingBoolArray(xPositive, yPositive)
@@ -1592,7 +1607,7 @@ def _getBounds(self):
15921607
if len(data) == 5:
15931608
# hack to avoid duplicating caching mechanism in Scatter
15941609
# (happens when cached data is used, caching done using
1595-
# Scatter._logFilterData)
1610+
# Scatter._filterData)
15961611
x, y, xerror, yerror = data[0], data[1], data[3], data[4]
15971612
else:
15981613
x, y, xerror, yerror = data
@@ -1609,31 +1624,30 @@ def _getBounds(self):
16091624
return self._boundsCache[(xPositive, yPositive)]
16101625

16111626
def _getCachedData(self):
1612-
"""Return cached filtered data if applicable,
1613-
i.e. if any axis is in log scale.
1627+
"""Return cached filtered data if applicable.
1628+
16141629
Return None if caching is not applicable."""
16151630
plot = self.getPlot()
1616-
if plot is not None:
1617-
xPositive = plot.getXAxis()._isLogarithmic()
1618-
yPositive = plot.getYAxis()._isLogarithmic()
1619-
if xPositive or yPositive:
1620-
# At least one axis has log scale, filter data
1621-
if (xPositive, yPositive) not in self._filteredCache:
1622-
self._filteredCache[(xPositive, yPositive)] = self._logFilterData(
1623-
xPositive, yPositive
1624-
)
1625-
return self._filteredCache[(xPositive, yPositive)]
1626-
return None
1631+
if plot is None:
1632+
return None
1633+
1634+
xPositive = plot.getXAxis()._isLogarithmic()
1635+
yPositive = plot.getYAxis()._isLogarithmic()
1636+
if (xPositive, yPositive) not in self._filteredCache:
1637+
self._filteredCache[(xPositive, yPositive)] = self._filterData(
1638+
xPositive, yPositive
1639+
)
1640+
return self._filteredCache[(xPositive, yPositive)]
16271641

16281642
def getData(self, copy=True, displayed=False):
16291643
"""Returns the x, y values of the curve points and xerror, yerror
16301644
16311645
:param bool copy: True (Default) to get a copy,
16321646
False to use internal representation (do not modify!)
1633-
:param bool displayed: True to only get curve points that are displayed
1634-
in the plot. Default: False
1635-
Note: If plot has log scale, negative points
1636-
are not displayed.
1647+
:param bool displayed:
1648+
True to only get curve points that are displayed in the plot.
1649+
Note: If plot has log scale, negative points are not displayed.
1650+
Negative errors are set to 0.
16371651
:returns: (x, y, xerror, yerror)
16381652
:rtype: 4-tuple of numpy.ndarray
16391653
"""

src/silx/gui/plot/items/scatter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,8 @@ def _getInterpolatorFuture(self):
930930
)
931931
return self.__interpolatorFuture
932932

933-
def _logFilterData(self, xPositive, yPositive):
934-
"""Filter out values with x or y <= 0 on log axes
933+
def _filterData(self, xPositive: bool, yPositive: bool):
934+
"""Filter out errors<0 and values with x or y <= 0 on log axes
935935
936936
:param bool xPositive: True to filter arrays according to X coords.
937937
:param bool yPositive: True to filter arrays according to Y coords.
@@ -949,7 +949,7 @@ def _logFilterData(self, xPositive, yPositive):
949949
value = numpy.array(value, copy=True, dtype=numpy.float64)
950950
value[clipped] = numpy.nan
951951

952-
x, y, xerror, yerror = PointsBase._logFilterData(self, xPositive, yPositive)
952+
x, y, xerror, yerror = PointsBase._filterData(self, xPositive, yPositive)
953953

954954
return x, y, value, xerror, yerror
955955

0 commit comments

Comments
 (0)