From 99cc0685f78016d9e8aaf4c5ea4be92efa40916c Mon Sep 17 00:00:00 2001 From: nyxst4ck <289980115+nyxst4ck@users.noreply.github.com> Date: Sun, 2 Aug 2026 14:46:10 -0300 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + plotly/figure_factory/_annotated_heatmap.py | 14 ++++++------- .../test_tools/test_figure_factory.py | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f4cc3ac0ed..327e04ca5fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - 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! - Add `` 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! +- 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)] ## [6.9.0] - 2026-07-09 diff --git a/plotly/figure_factory/_annotated_heatmap.py b/plotly/figure_factory/_annotated_heatmap.py index 5da24ae5bc3..30a088cb42d 100644 --- a/plotly/figure_factory/_annotated_heatmap.py +++ b/plotly/figure_factory/_annotated_heatmap.py @@ -28,7 +28,7 @@ def validate_annotated_heatmap(z, x, y, annotation_text): "z and text should have the same dimensions" ) - if x: + if x is not None: if len(x) != len(z[0]): raise exceptions.PlotlyError( "oops, the x list that you " @@ -36,7 +36,7 @@ def validate_annotated_heatmap(z, x, y, annotation_text): "width of your z matrix " ) - if y: + if y is not None: if len(y) != len(z): raise exceptions.PlotlyError( "oops, the y list that you " @@ -65,8 +65,8 @@ def create_annotated_heatmap( This function adds annotations to each cell of the heatmap. :param (list[list]|ndarray) z: z matrix to create heatmap. - :param (list) x: x axis labels. - :param (list) y: y axis labels. + :param (list|ndarray) x: x axis labels. + :param (list|ndarray) y: y axis labels. :param (list[list]|ndarray) annotation_text: Text strings for annotations. Should have the same dimensions as the z matrix. If no text is added, the values of the z matrix are annotated. Default = @@ -109,7 +109,7 @@ def create_annotated_heatmap( z, x, y, annotation_text, colorscale, font_colors, reversescale, **kwargs ).make_annotations() - if x or y: + if x is not None or y is not None: trace = dict( type="heatmap", z=z, @@ -174,11 +174,11 @@ def __init__( self, z, x, y, annotation_text, colorscale, font_colors, reversescale, **kwargs ): self.z = z - if x: + if x is not None: self.x = x else: self.x = range(len(z[0])) - if y: + if y is not None: self.y = y else: self.y = range(len(z)) diff --git a/tests/test_optional/test_tools/test_figure_factory.py b/tests/test_optional/test_tools/test_figure_factory.py index 209ae438235..eb4163dee87 100644 --- a/tests/test_optional/test_tools/test_figure_factory.py +++ b/tests/test_optional/test_tools/test_figure_factory.py @@ -1,6 +1,7 @@ import math import datetime +import numpy as np import plotly.figure_factory as ff from plotly.exceptions import PlotlyError @@ -781,6 +782,25 @@ def test_incorrect_y_size(self): kwargs = {"z": [[1, 2], [1, 2]], "y": [1, 2, 3]} self.assertRaises(PlotlyError, ff.create_annotated_heatmap, **kwargs) + def test_numpy_x_and_y(self): + # check: numpy arrays are accepted as x and y axis labels + + a_heat = ff.create_annotated_heatmap( + [[1, 2], [3, 4]], x=np.array(["A", "B"]), y=np.array(["C", "D"]) + ) + + self.assertEqual(list(a_heat["data"][0]["x"]), ["A", "B"]) + self.assertEqual(list(a_heat["data"][0]["y"]), ["C", "D"]) + # tick labels are shown when x and y are supplied + self.assertNotEqual(a_heat["layout"]["xaxis"]["showticklabels"], False) + self.assertNotEqual(a_heat["layout"]["yaxis"]["showticklabels"], False) + + def test_numpy_x_wrong_size(self): + # check: PlotlyError if a numpy x is the wrong size + + kwargs = {"z": [[1, 2], [1, 2]], "x": np.array(["A", "B", "C"])} + self.assertRaises(PlotlyError, ff.create_annotated_heatmap, **kwargs) + def test_simple_annotated_heatmap(self): # we should be able to create a heatmap with annotated values with a # logical text color