diff --git a/CHANGES.md b/CHANGES.md index f979bf5ac..495065c76 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ Full changelog ============== +v1.4.0 (unreleased) +------------------- + +* Modify profile viewer so that when in 'Sum' mode, parts of profiles + with no valid values are NaN rather than zero. + v1.3.0 (2022-04-22) ------------------- diff --git a/glue/utils/array.py b/glue/utils/array.py index f0998852a..2981e3ed7 100644 --- a/glue/utils/array.py +++ b/glue/utils/array.py @@ -396,6 +396,23 @@ def format_minimal(values): return fmt, strings +def nansum_with_nan_for_empty(values, axis=None): + """ + Wrapper around nansum which returns NaN instead of zero when no non-NaN + values are summed. + """ + result = np.nansum(values, axis=axis) + reset = np.sum(~np.isnan(values), axis=axis) + if np.isscalar(result): + if reset == 0: + return np.nan + else: + return result + else: + result[reset == 0] = np.nan + return result + + PLAIN_FUNCTIONS = {'minimum': np.min, 'maximum': np.max, 'mean': np.mean, @@ -407,7 +424,7 @@ def format_minimal(values): 'maximum': np.nanmax, 'mean': np.nanmean, 'median': np.nanmedian, - 'sum': np.nansum, + 'sum': nansum_with_nan_for_empty, 'percentile': np.nanpercentile} diff --git a/glue/utils/tests/test_array.py b/glue/utils/tests/test_array.py index adfef7a29..38371bfab 100644 --- a/glue/utils/tests/test_array.py +++ b/glue/utils/tests/test_array.py @@ -10,7 +10,7 @@ shape_to_string, check_sorted, pretty_number, unbroadcast, iterate_chunks, combine_slices, nanmean, nanmedian, nansum, nanmin, nanmax, format_minimal, compute_statistic, categorical_ndarray, - index_lookup, broadcast_arrays_minimal) + index_lookup, broadcast_arrays_minimal, nansum_with_nan_for_empty) @pytest.mark.parametrize(('before', 'ref_after', 'ref_indices'), @@ -194,6 +194,14 @@ def test_nanfunctions(function, axis): assert_allclose(function(ARRAY, axis=axis), np_func(ARRAY, axis=axis)) +def test_custom_nansum(): + assert np.isnan(nansum_with_nan_for_empty([])) + assert_equal(nansum_with_nan_for_empty([np.nan]), np.nan) + assert_equal(nansum_with_nan_for_empty([[np.nan, np.nan], [np.nan, 1]]), 1) + assert_equal(nansum_with_nan_for_empty([[np.nan, np.nan], [1, np.nan]], axis=0), [1, np.nan]) + assert_equal(nansum_with_nan_for_empty([[np.nan, np.nan], [1, np.nan]], axis=1), [np.nan, 1]) + + SLICE_CASES = [ (slice(None), slice(5), 10), (slice(None), slice(1, 5), 10),