|
5 | 5 | from qtpy.QtWidgets import (QWidget, QVBoxLayout, QListWidget, QSplitter, QHBoxLayout, |
6 | 6 | QLabel, QCheckBox, QLineEdit, QComboBox, QMessageBox) |
7 | 7 |
|
8 | | -from larray_editor.utils import replace_inf, _ |
| 8 | +from larray_editor.utils import _ |
9 | 9 | from larray_editor.arraywidget import ArrayEditorWidget |
10 | 10 | from larray_editor.editor import AbstractEditorWindow, DISPLAY_IN_GRID |
11 | 11 |
|
@@ -112,7 +112,8 @@ def _get_atol_rtol(self): |
112 | 112 | self.tolerance_line_edit.setText('') |
113 | 113 | tol = 0 |
114 | 114 | QMessageBox.critical(self, "Error", str(e)) |
115 | | - return (tol, 0) if self.tolerance_combobox.currentText() == "absolute" else (0, tol) |
| 115 | + is_absolute = self.tolerance_combobox.currentText() == "absolute" |
| 116 | + return (tol, 0) if is_absolute else (0, tol) |
116 | 117 |
|
117 | 118 | # override keyPressEvent to prevent pressing Enter after changing the tolerance value |
118 | 119 | # in associated QLineEdit to close the parent dialog box |
@@ -160,31 +161,69 @@ def _update_from_combined_array(self): |
160 | 161 |
|
161 | 162 | atol, rtol = self._get_atol_rtol() |
162 | 163 | try: |
163 | | - self._diff_below_tolerance = self._combined_array.eq(self._array0, rtol=rtol, atol=atol, nans_equal=self.nans_equal) |
| 164 | + # eq does not take atol and rtol into account |
| 165 | + eq = self._combined_array.eq(self._array0, |
| 166 | + nans_equal=self.nans_equal) |
| 167 | + isclose = self._combined_array.eq(self._array0, |
| 168 | + rtol=rtol, atol=atol, |
| 169 | + nans_equal=self.nans_equal) |
164 | 170 | except TypeError: |
165 | | - self._diff_below_tolerance = self._combined_array == self._array0 |
| 171 | + # object arrays |
| 172 | + eq = self._combined_array == self._array0 |
| 173 | + isclose = eq |
| 174 | + self._diff_below_tolerance = isclose |
166 | 175 |
|
167 | 176 | try: |
168 | 177 | with np.errstate(divide='ignore', invalid='ignore'): |
169 | 178 | diff = self._combined_array - self._array0 |
170 | 179 | reldiff = diff / self._array0 |
171 | | - |
| 180 | + # make reldiff 0 where the values are the same than array0 even for |
| 181 | + # special values (0, nan, inf, -inf) |
| 182 | + # at this point reldiff can still contain nan and infs |
| 183 | + reldiff = la.where(eq, 0, reldiff) |
| 184 | + |
| 185 | + # 1) compute maxabsreldiff for the label |
| 186 | + # this should NOT exclude nans or infs |
| 187 | + relmin = reldiff.min(skipna=False) |
| 188 | + relmax = reldiff.max(skipna=False) |
| 189 | + maxabsreldiff = max(abs(relmin), abs(relmax)) |
| 190 | + |
| 191 | + # 2) compute bg_value |
172 | 192 | # replace -inf by min(reldiff), +inf by max(reldiff) |
173 | | - finite_reldiff, finite_relmin, finite_relmax = replace_inf(reldiff) |
174 | | - maxabsreldiff = max(abs(finite_relmin), abs(finite_relmax)) |
175 | | - |
176 | | - # We need a separate version for bg and the label, so that when we modify atol/rtol, the background |
177 | | - # color is updated but not the maxreldiff label |
178 | | - # this is necessary for nan, inf and -inf (because inf - inf = nan, not 0) |
179 | | - # this is more precise than divnot0, it only ignore 0 / 0, not x / 0 |
180 | | - reldiff_for_bg = la.where(self._diff_below_tolerance, 0, finite_reldiff) |
181 | | - maxabsreldiff_for_bg = max(abs(np.nanmin(reldiff_for_bg)), abs(np.nanmax(reldiff_for_bg))) |
| 193 | + reldiff_for_bg = reldiff.copy() |
| 194 | + isneginf = reldiff == -np.inf |
| 195 | + isposinf = reldiff == np.inf |
| 196 | + isinf = isneginf | isposinf |
| 197 | + |
| 198 | + # given the way reldiff is constructed, it cannot contain only infs |
| 199 | + # (because inf/inf is nan) it can contain only infs and nans though, |
| 200 | + # in which case finite_relXXX will be nan, so unless the array |
| 201 | + # is empty, finite_relXXX should never be inf |
| 202 | + finite_relmin = np.nanmin(reldiff, where=~isinf, initial=np.inf) |
| 203 | + finite_relmax = np.nanmax(reldiff, where=~isinf, initial=-np.inf) |
| 204 | + # special case when reldiff contains only 0 and infs (to avoid |
| 205 | + # coloring the inf cells white in that case) |
| 206 | + if finite_relmin == 0 and finite_relmax == 0 and isinf.any(): |
| 207 | + finite_relmin = -1 |
| 208 | + finite_relmax = 1 |
| 209 | + reldiff_for_bg[isneginf] = finite_relmin |
| 210 | + reldiff_for_bg[isposinf] = finite_relmax |
| 211 | + |
| 212 | + # make sure that "acceptable" differences show as white |
| 213 | + reldiff_for_bg = la.where(isclose, 0, reldiff_for_bg) |
| 214 | + |
| 215 | + # We need a separate version for bg and the label, so that when we |
| 216 | + # modify atol/rtol, the background color is updated but not the |
| 217 | + # maxreldiff label |
| 218 | + maxabsreldiff_for_bg = max(abs(np.nanmin(reldiff_for_bg)), |
| 219 | + abs(np.nanmax(reldiff_for_bg))) |
182 | 220 | if maxabsreldiff_for_bg: |
183 | 221 | # scale reldiff to range 0-1 with 0.5 for reldiff = 0 |
184 | 222 | self._bg_value = (reldiff_for_bg / maxabsreldiff_for_bg) / 2 + 0.5 |
185 | 223 | # if the only differences are nans on either side |
186 | | - elif not self._diff_below_tolerance.all(): |
187 | | - # use white (0.5) everywhere except where reldiff is nan, so that nans are grey |
| 224 | + elif not isclose.all(): |
| 225 | + # use white (0.5) everywhere except where reldiff is nan, so |
| 226 | + # that nans are grey |
188 | 227 | self._bg_value = reldiff_for_bg + 0.5 |
189 | 228 | else: |
190 | 229 | # do NOT use full_like as we don't want to inherit array dtype |
|
0 commit comments