@@ -1875,6 +1875,38 @@ def test_umap_mask_no_modification():
18751875 pd .testing .assert_series_equal (pbmc .obs ["louvain" ], data_copy )
18761876
18771877
1878+ def test_scatter_size_not_mutated_across_panels ():
1879+ """Per-point size array must not be cumulatively reordered across subplots.
1880+
1881+ Regression test for https://github.com/scverse/scanpy/issues/4024
1882+ Uses 3 panels (categorical + two continuous) to exercise cumulative reorder.
1883+ """
1884+ pbmc = pbmc3k_processed ()
1885+ rng = np .random .default_rng (0 )
1886+ sizes = rng .uniform (10 , 200 , size = pbmc .n_obs )
1887+ sizes_original = sizes .copy ()
1888+
1889+ axes = sc .pl .umap (
1890+ pbmc ,
1891+ color = ["louvain" , "n_genes" , "n_counts" ],
1892+ size = sizes ,
1893+ show = False ,
1894+ )
1895+
1896+ # The input array must not be modified
1897+ np .testing .assert_array_equal (sizes , sizes_original )
1898+
1899+ # Each panel must plot the correct per-point sizes (just reordered by
1900+ # z-order, not cumulatively scrambled). Sorting makes the comparison
1901+ # independent of z-ordering.
1902+ expected_sorted = np .sort (sizes )
1903+ for ax in axes :
1904+ plotted = ax .collections [0 ].get_sizes ()
1905+ np .testing .assert_allclose (np .sort (plotted ), expected_sorted )
1906+
1907+ plt .close ()
1908+
1909+
18781910def test_string_mask (tmp_path , check_same_image ):
18791911 """Check that the same mask given as string or bool array provides the same result."""
18801912 pbmc = pbmc3k_processed ()
0 commit comments