From aa599a022f1a57d103d0dacfe042f98c2102af5e Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:04:02 +0200 Subject: [PATCH 1/9] add logic to handle non-hommogeneous arrays --- ultraplot/internals/inputs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ultraplot/internals/inputs.py b/ultraplot/internals/inputs.py index 6b61dbc2b..8163d8136 100644 --- a/ultraplot/internals/inputs.py +++ b/ultraplot/internals/inputs.py @@ -150,13 +150,17 @@ def _to_numpy_array(data, strip_units=False): data = data.data # support pint quantities that get unit-stripped later elif isinstance(data, (DataFrame, Series, Index)): data = data.values + if Quantity is not ndarray and isinstance(data, Quantity): if strip_units: return np.atleast_1d(data.magnitude) else: return np.atleast_1d(data.magnitude) * data.units - else: + try: return np.atleast_1d(data) # natively preserves masked arrays + except: + # handle non-homogeneous data + return np.array(data, dtype=object) def _to_masked_array(data, *, copy=False): From f7a93ecc95f64ad65e74577103fbcafaec7693f3 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:13:36 +0200 Subject: [PATCH 2/9] add unittest --- ultraplot/tests/test_inputs.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ultraplot/tests/test_inputs.py diff --git a/ultraplot/tests/test_inputs.py b/ultraplot/tests/test_inputs.py new file mode 100644 index 000000000..95a6de341 --- /dev/null +++ b/ultraplot/tests/test_inputs.py @@ -0,0 +1,23 @@ +import ultraplot as uplt, pytest, numpy as np + + +class BrokenObject: + def __str__(self): + raise Exception("I am broken.") + + +@pytest.mark.parametrize( + "data, dtype", + [ + ([1, 2, 3], int), + ([[1, 2], [1, 2, 3]], object), + (["hello", 1], np.dtype(" Date: Sun, 10 Aug 2025 18:16:19 +0200 Subject: [PATCH 3/9] add violin plot with inhomogeneous inputs --- ultraplot/tests/test_plot.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ultraplot/tests/test_plot.py b/ultraplot/tests/test_plot.py index 6f09e1834..35bf9ecec 100644 --- a/ultraplot/tests/test_plot.py +++ b/ultraplot/tests/test_plot.py @@ -433,3 +433,17 @@ def test_color_parsing_for_none(): for artist in ax[0].collections: assert artist.get_facecolor().shape[0] == 0 uplt.close(fig) + + +@pytest.mark.mpl_image_compare +def test_inhomogeneous_violin(): + """ + Test that inhomogeneous violin plots work correctly. + """ + fig, ax = uplt.subplots() + data = [np.random.normal(size=100), np.random.normal(size=200)] + violins = ax.violinplot(data, vert=True, labels=["A", "B"]) + assert len(violins) == 2 + for violin in violins: + assert violin.get_paths() # Ensure paths are created + uplt.close(fig) From 8b3398efb0f6648292975e0366b63d1c1d926b3a Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:17:11 +0200 Subject: [PATCH 4/9] rm broken object --- ultraplot/tests/test_inputs.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ultraplot/tests/test_inputs.py b/ultraplot/tests/test_inputs.py index 95a6de341..8e257f503 100644 --- a/ultraplot/tests/test_inputs.py +++ b/ultraplot/tests/test_inputs.py @@ -1,11 +1,6 @@ import ultraplot as uplt, pytest, numpy as np -class BrokenObject: - def __str__(self): - raise Exception("I am broken.") - - @pytest.mark.parametrize( "data, dtype", [ From 33c4e7d614ed3d7a3d46eb2d8eaae9d09610e3bc Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:19:03 +0200 Subject: [PATCH 5/9] swap random with rng --- ultraplot/tests/test_plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ultraplot/tests/test_plot.py b/ultraplot/tests/test_plot.py index 35bf9ecec..6db947c7f 100644 --- a/ultraplot/tests/test_plot.py +++ b/ultraplot/tests/test_plot.py @@ -436,12 +436,12 @@ def test_color_parsing_for_none(): @pytest.mark.mpl_image_compare -def test_inhomogeneous_violin(): +def test_inhomogeneous_violin(rng): """ Test that inhomogeneous violin plots work correctly. """ fig, ax = uplt.subplots() - data = [np.random.normal(size=100), np.random.normal(size=200)] + data = [rng.normal(size=100), np.random.normal(size=200)] violins = ax.violinplot(data, vert=True, labels=["A", "B"]) assert len(violins) == 2 for violin in violins: From 1ca3e2e2655896614c741d44760d7d783c34dffa Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:19:20 +0200 Subject: [PATCH 6/9] add return --- ultraplot/tests/test_plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/tests/test_plot.py b/ultraplot/tests/test_plot.py index 6db947c7f..26e766c2d 100644 --- a/ultraplot/tests/test_plot.py +++ b/ultraplot/tests/test_plot.py @@ -446,4 +446,4 @@ def test_inhomogeneous_violin(rng): assert len(violins) == 2 for violin in violins: assert violin.get_paths() # Ensure paths are created - uplt.close(fig) + return fig From bbf79d8bb90b02561277ec822e6523a58049bb92 Mon Sep 17 00:00:00 2001 From: Casper van Elteren Date: Sun, 10 Aug 2025 18:21:40 +0200 Subject: [PATCH 7/9] Update ultraplot/tests/test_inputs.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ultraplot/tests/test_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/tests/test_inputs.py b/ultraplot/tests/test_inputs.py index 8e257f503..0da7baa94 100644 --- a/ultraplot/tests/test_inputs.py +++ b/ultraplot/tests/test_inputs.py @@ -6,7 +6,7 @@ [ ([1, 2, 3], int), ([[1, 2], [1, 2, 3]], object), - (["hello", 1], np.dtype(" Date: Sun, 10 Aug 2025 18:22:01 +0200 Subject: [PATCH 8/9] Update ultraplot/internals/inputs.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ultraplot/internals/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/internals/inputs.py b/ultraplot/internals/inputs.py index 8163d8136..f35df5c34 100644 --- a/ultraplot/internals/inputs.py +++ b/ultraplot/internals/inputs.py @@ -158,7 +158,7 @@ def _to_numpy_array(data, strip_units=False): return np.atleast_1d(data.magnitude) * data.units try: return np.atleast_1d(data) # natively preserves masked arrays - except: + except (TypeError, ValueError): # handle non-homogeneous data return np.array(data, dtype=object) From e52d5bdaaff78f79aa5beb6e9377661eb7787433 Mon Sep 17 00:00:00 2001 From: cvanelteren Date: Sun, 10 Aug 2025 18:29:33 +0200 Subject: [PATCH 9/9] restore co-pilot shenanigans --- ultraplot/tests/test_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultraplot/tests/test_inputs.py b/ultraplot/tests/test_inputs.py index 0da7baa94..8e257f503 100644 --- a/ultraplot/tests/test_inputs.py +++ b/ultraplot/tests/test_inputs.py @@ -6,7 +6,7 @@ [ ([1, 2, 3], int), ([[1, 2], [1, 2, 3]], object), - (["hello", 1], "unicode"), # will convert 1 to string + (["hello", 1], np.dtype("