Skip to content

Commit 99cc068

Browse files
committed
Accept NumPy arrays for x and y in create_annotated_heatmap
create_annotated_heatmap tested its optional x and y arguments for truthiness rather than for None. A NumPy array has no unambiguous truth value, so passing arrays as axis labels raised ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() from validate_annotated_heatmap, before any heatmap was built. This is easy to hit because z itself is documented to accept an ndarray, so labels derived from the same data are naturally arrays too. Compare x and y against None in the three places that gate on them: the length validation, the choice of trace/layout with or without tick labels, and the default axis ranges in _AnnotatedHeatmap. Sequences that are merely falsy, such as an empty list, are now length-checked against z instead of being silently ignored. pandas Series and Index objects work for the same reason.
1 parent de4f21b commit 99cc068

3 files changed

Lines changed: 28 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77
### Fixed
88
- Fix `hex_to_rgb` parsing of 3-digit shorthand hexadecimal colors such as `#FFF` [[#5662](https://github.com/plotly/plotly.py/pull/5662)], with thanks to @genrichez for the contribution!
99
- Add `<!doctype html>` to the `to_html()` template to comply with modern web standards [[#5693](https://github.com/plotly/plotly.py/pull/5693)], with thanks to @mishrakushal for the contribution!
10+
- Accept NumPy arrays for the `x` and `y` arguments of `figure_factory.create_annotated_heatmap`, which previously raised `ValueError: The truth value of an array with more than one element is ambiguous` [[#4160](https://github.com/plotly/plotly.py/issues/4160)]
1011

1112

1213
## [6.9.0] - 2026-07-09

plotly/figure_factory/_annotated_heatmap.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ def validate_annotated_heatmap(z, x, y, annotation_text):
2828
"z and text should have the same dimensions"
2929
)
3030

31-
if x:
31+
if x is not None:
3232
if len(x) != len(z[0]):
3333
raise exceptions.PlotlyError(
3434
"oops, the x list that you "
3535
"provided does not match the "
3636
"width of your z matrix "
3737
)
3838

39-
if y:
39+
if y is not None:
4040
if len(y) != len(z):
4141
raise exceptions.PlotlyError(
4242
"oops, the y list that you "
@@ -65,8 +65,8 @@ def create_annotated_heatmap(
6565
This function adds annotations to each cell of the heatmap.
6666
6767
:param (list[list]|ndarray) z: z matrix to create heatmap.
68-
:param (list) x: x axis labels.
69-
:param (list) y: y axis labels.
68+
:param (list|ndarray) x: x axis labels.
69+
:param (list|ndarray) y: y axis labels.
7070
:param (list[list]|ndarray) annotation_text: Text strings for
7171
annotations. Should have the same dimensions as the z matrix. If no
7272
text is added, the values of the z matrix are annotated. Default =
@@ -109,7 +109,7 @@ def create_annotated_heatmap(
109109
z, x, y, annotation_text, colorscale, font_colors, reversescale, **kwargs
110110
).make_annotations()
111111

112-
if x or y:
112+
if x is not None or y is not None:
113113
trace = dict(
114114
type="heatmap",
115115
z=z,
@@ -174,11 +174,11 @@ def __init__(
174174
self, z, x, y, annotation_text, colorscale, font_colors, reversescale, **kwargs
175175
):
176176
self.z = z
177-
if x:
177+
if x is not None:
178178
self.x = x
179179
else:
180180
self.x = range(len(z[0]))
181-
if y:
181+
if y is not None:
182182
self.y = y
183183
else:
184184
self.y = range(len(z))

tests/test_optional/test_tools/test_figure_factory.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import math
22

33
import datetime
4+
import numpy as np
45
import plotly.figure_factory as ff
56

67
from plotly.exceptions import PlotlyError
@@ -781,6 +782,25 @@ def test_incorrect_y_size(self):
781782
kwargs = {"z": [[1, 2], [1, 2]], "y": [1, 2, 3]}
782783
self.assertRaises(PlotlyError, ff.create_annotated_heatmap, **kwargs)
783784

785+
def test_numpy_x_and_y(self):
786+
# check: numpy arrays are accepted as x and y axis labels
787+
788+
a_heat = ff.create_annotated_heatmap(
789+
[[1, 2], [3, 4]], x=np.array(["A", "B"]), y=np.array(["C", "D"])
790+
)
791+
792+
self.assertEqual(list(a_heat["data"][0]["x"]), ["A", "B"])
793+
self.assertEqual(list(a_heat["data"][0]["y"]), ["C", "D"])
794+
# tick labels are shown when x and y are supplied
795+
self.assertNotEqual(a_heat["layout"]["xaxis"]["showticklabels"], False)
796+
self.assertNotEqual(a_heat["layout"]["yaxis"]["showticklabels"], False)
797+
798+
def test_numpy_x_wrong_size(self):
799+
# check: PlotlyError if a numpy x is the wrong size
800+
801+
kwargs = {"z": [[1, 2], [1, 2]], "x": np.array(["A", "B", "C"])}
802+
self.assertRaises(PlotlyError, ff.create_annotated_heatmap, **kwargs)
803+
784804
def test_simple_annotated_heatmap(self):
785805
# we should be able to create a heatmap with annotated values with a
786806
# logical text color

0 commit comments

Comments
 (0)