Skip to content

Commit 5bd5565

Browse files
committed
Fix: Handle single-element arrays in to_bins function to prevent IndexError
1 parent 82f4688 commit 5bd5565

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

doc/release_notes/release_2.08.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
🛠️ Bug fixes:
1212

13+
* Fixed `IndexError` when displaying images with a single row or column (e.g., SIF files with shape `(1, N)`):
14+
* The `to_bins()` function now handles single-element coordinate arrays by assuming a bin width of 1.0 centered on the point
15+
* Previously, loading such images in DataLab caused an `IndexError: index 1 is out of bounds for axis 0 with size 1`
1316
* Fixed circle/ellipse shape drawing with non-uniform aspect ratios:
1417
* Axes were not perpendicular and did not connect to the ellipse edge when plot aspect ratio differed from 1.0
1518
* Now uses parametric ellipse drawing that correctly handles non-perpendicular axes in pixel space

plotpy/items/image/filter.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,26 @@
5151
# Image with custom X, Y axes
5252
# ==============================================================================
5353
def to_bins(x):
54-
"""Convert point center to point bounds"""
54+
"""Convert point center to point bounds
55+
56+
Args:
57+
x: 1D array of point centers
58+
59+
Returns:
60+
1D array of bin edges (length = len(x) + 1)
61+
62+
Note:
63+
When x has only 1 element, we assume a bin width of 1.0 centered on the point.
64+
"""
5565
bx = np.zeros((x.shape[0] + 1,), float)
56-
bx[1:-1] = (x[:-1] + x[1:]) / 2
57-
bx[0] = x[0] - (x[1] - x[0]) / 2
58-
bx[-1] = x[-1] + (x[-1] - x[-2]) / 2
66+
if x.shape[0] == 1:
67+
# Single point: assume bin width of 1.0 centered on the point
68+
bx[0] = x[0] - 0.5
69+
bx[1] = x[0] + 0.5
70+
else:
71+
bx[1:-1] = (x[:-1] + x[1:]) / 2
72+
bx[0] = x[0] - (x[1] - x[0]) / 2
73+
bx[-1] = x[-1] + (x[-1] - x[-2]) / 2
5974
return bx
6075

6176

plotpy/tests/items/test_to_bins.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Licensed under the terms of the BSD 3-Clause
4+
# (see plotpy/LICENSE for details)
5+
6+
"""Test for to_bins function used in XYImageItem"""
7+
8+
import numpy as np
9+
10+
from plotpy.items.image.filter import to_bins
11+
12+
13+
def test_to_bins_single_element():
14+
"""Test to_bins with a single-element array.
15+
16+
This is a regression test for the bug where loading an image with only
17+
1 row (e.g., SIF files with shape (1, N)) caused an IndexError because
18+
to_bins assumed at least 2 elements to compute bin edges.
19+
"""
20+
x = np.array([5.0])
21+
result = to_bins(x)
22+
23+
# Should return 2 bin edges, centered around the single point
24+
assert len(result) == 2
25+
assert result[0] == 4.5 # x[0] - 0.5
26+
assert result[1] == 5.5 # x[0] + 0.5
27+
28+
29+
def test_to_bins_two_elements():
30+
"""Test to_bins with a two-element array."""
31+
x = np.array([1.0, 3.0])
32+
result = to_bins(x)
33+
34+
# Should return 3 bin edges
35+
assert len(result) == 3
36+
np.testing.assert_allclose(result, [0.0, 2.0, 4.0])
37+
38+
39+
def test_to_bins_multiple_elements():
40+
"""Test to_bins with multiple elements (uniform spacing)."""
41+
x = np.array([1.0, 2.0, 3.0, 4.0])
42+
result = to_bins(x)
43+
44+
# Should return 5 bin edges
45+
assert len(result) == 5
46+
np.testing.assert_allclose(result, [0.5, 1.5, 2.5, 3.5, 4.5])
47+
48+
49+
def test_to_bins_non_uniform_spacing():
50+
"""Test to_bins with non-uniform spacing."""
51+
x = np.array([0.0, 1.0, 4.0])
52+
result = to_bins(x)
53+
54+
# Should return 4 bin edges
55+
# First edge: 0.0 - (1.0 - 0.0) / 2 = -0.5
56+
# Middle: (0.0 + 1.0) / 2 = 0.5, (1.0 + 4.0) / 2 = 2.5
57+
# Last edge: 4.0 + (4.0 - 1.0) / 2 = 5.5
58+
assert len(result) == 4
59+
np.testing.assert_allclose(result, [-0.5, 0.5, 2.5, 5.5])
60+
61+
62+
if __name__ == "__main__":
63+
test_to_bins_single_element()
64+
test_to_bins_two_elements()
65+
test_to_bins_multiple_elements()
66+
test_to_bins_non_uniform_spacing()
67+
print("All tests passed!")

0 commit comments

Comments
 (0)