From d3ca5baad577446c933391fd26a22654e7f5f781 Mon Sep 17 00:00:00 2001 From: Tim Treis Date: Tue, 31 Mar 2026 00:26:35 +0200 Subject: [PATCH] Fix categorical colors wrongly assigned to points with non-sequential index (#358) When points have a shuffled or non-sequential index (e.g. from .sample() or .subset()), _reparse_points sorts rows by index while adata.X retains the original order. This causes get_values to return colors in sorted order, misaligned with coordinates. Resetting the index to sequential before adata construction and reparsing ensures both share the same positional order. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/spatialdata_plot/pl/render.py | 5 ++++- ...ed_points_categorical_color_datashader.png | Bin 0 -> 15631 bytes ...ed_points_categorical_color_matplotlib.png | Bin 0 -> 15593 bytes tests/pl/test_render_points.py | 21 ++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/_images/Points_sampled_points_categorical_color_datashader.png create mode 100644 tests/_images/Points_sampled_points_categorical_color_matplotlib.png diff --git a/src/spatialdata_plot/pl/render.py b/src/spatialdata_plot/pl/render.py index cfd7dafa..4c3fcb0e 100644 --- a/src/spatialdata_plot/pl/render.py +++ b/src/spatialdata_plot/pl/render.py @@ -743,6 +743,9 @@ def _render_points( ) added_color_from_table = True + # Reset to sequential index so row order matches after _reparse_points round-trip (#358). + points = points.reset_index(drop=True) + n_points = len(points) points_pd_with_color = points # When we pull colors from a table, keep the raw points (with color) for later, @@ -758,7 +761,7 @@ def _render_points( if table_name is None: adata = AnnData( X=points[["x", "y"]].values, - obs=points[coords].reset_index(), + obs=points[coords], dtype=points[["x", "y"]].values.dtype, ) else: diff --git a/tests/_images/Points_sampled_points_categorical_color_datashader.png b/tests/_images/Points_sampled_points_categorical_color_datashader.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2f9ac806d341f39f07027fb131ac2c282bb40b GIT binary patch literal 15631 zcmdU$cQ}@B{P!a1EN5^p=rR%<~^E%JZ`FX$I?=wbIl zN2{@x?YJqZ(f5sOd3^hW!zQCAA1swhc1o_2kl@}TOT0q_>a zab`2AcWPlf@ZrOcF8_Lav-~iUUbd0$7#tS#M;y^c1Wj>p-@-h1MDcZFY3W*$D&I>H z2P))Tczej!0sQRj><}_P6xjK3dd`Xr5p$iguO1&MGuoS>c_KwPzJ{ClPhy$Xo`?l$ zqkEm~i3x8v?p|_gzP+5_j8Cz|Re`hYh^^r6;gcR>>^?YWX z3bS%j=Y`|}&5g`sp{;wrULZG#h@0yctIK{6@m{u^+tTf4-BGPn9Sex@Rd;b&w@Q#r zNlcvG^3M)jEoN6uQB+hcHLAn>bpO@B6e!4I{rTyYR1;r=2M-=7D13COo7Oco{ZZ2X zclB+Bt*vdKOm@sgP9|wzo}D%6%_d>X&S*3m-Pzfx%HO<$t!R~#{s=>1PWtxwT&9kS zsQTeN^XdN4cE9}2x7&{ncE&3$I%*sSsg)dDU1`Et74D*j-CF+Hc1BbF`}eP+d3WNS z)#oKlR^y|?U0$PFi7X9;XbU>k6rPy)_=l^-g^o^6IgfV7;g>eX-rY{ILB`%YjV`8| z$vryo?i#DI>EUbM8TgAGy>MCN%K|F&Bjw$!Bs$LnxOGb_tIg)UALasIB;|vT_C-gH z=iMcA%Z&q8hl*Ps9X@52&-OAkFpzW@cmvOKa1dxRDynYO=*x@r=E&!Gnd<2+{|%I7 z>VJ0auD8jkkBQ9T$>FAOm&CGMPQ_xy#$aK8S=|pqGqZk4+KzRnky3|KQTTdaVrg!S zBtlI~D=s3UmMNj7McpBgl$2E7azV=3uElVa~V8y%Z(C-vD{#k-7GDW%w)Y&#IbLrY3ZGKjk-+Zd$Ss5m@&biBJhNlHS3 zgld;y54xT(D1{_y8yXr0{`!{tqd7=N<$ksdx3YF{a7#%^Y+iC|D($69D-BQY5`RC% zA<*SyZ$RS_kkJX-_I6y5Kic}t%q-zPn-Fp||F)v#GA%79hX_|JEBQ7%JNvX(zbp)P z!`~63kb{*%QKu2(p!N49C74XfiwF@5dDQK}nzIQyRlnWC-LWdgKM>>=!S57GLN+r4#(Pyv=J^^9B1C_a;oSlcQz%3gh~!SFdK5GTgES zt?szGy4u*}e^G3mVJ0Iu4sNY?!iymNxaD|0%T$0)#(fsC;<RF*SSQ&L8Thn16H!qYM{^Z&rm-Qecl@|S@DfANAolJerk=b4#| z2M^T;zkdCC`SN9FF(Y5!U3f;^u$zvKC2Y#->gsnBnABBOy`-`@cx6dA>GZQwqjNV~ z4qZpfO)he34EY?x$TXjx9K!D%kK5%4b}5EXuwdKTR+~D*KBC_0PJ8z72Di2r7k9hX zB{0he!+VK|iOD4~)TcRF`m(IQ z2|q`pP$W!rXiiQLUQ!~rwQw;*mro~aW9gMEU1mqUP8vlX_Rgb3Nowv9ZbI*kQJ)y? zu7K*Uqgx!xon_ieDHzqKQC&_v?B4VQZ(rEG4>Wvd9^l*946AT=ZG5mmeNaSQi@N%T zqn8DZs%QFfpQ!FWC>_%axSfNhlpQQu zjjnv_oA)9`PyF2ZgvmvBiPOUhj^(3oxwl~foh~kCn$0z}uCA^Y6ch{%4SmB`Q0FAM z!N;eersgfdXq!5iUf=+$aD04xgnoKZq|ddk|D~|qGA0Xds@gEE_F3fx9tC2=Ua@8K zhuQs>f0%M6CacrEY72%&Ml;Pu=@zr44>$Ob(2BUXCx8BKy^{@`nUda;8PPQ~OwY~^ zW|IH?@VifQON*)6T21o&=Y+bLBp(THN875t@6W2(>lJ@d?122xKA^G z`!{E@m#K*f@6uzVDEq6KQa&ZQx$W9_@s!wyhlgG3CKo+lnrJwM-F(&1ASEd|F6G2S zM>gRsmeW64yfhX0$eg%@N7QM`Jd@s7iw&90R7KaU4ie&>H7Smqb$~305v^H11g{DBIumuL=#%WLdK{78zAFCUenk2Dd*MiGPgc~zQI+dqW z46X9m;;08*rS6$~V2E*Is4xgZH=mq@_xZZskvnzK)UQ##mCNF9f{1_@`F0z&S7 zA|2h!Fi!L?3EqpGF1)k!NxgyXRA6omm>GK8A-x zxn8RhVeI5sDd*Ld5UROKZo!h%?4Q$o-KxJG&|c-&RpKXgy-NRN91l}juotG&Z1N>k zJ(rGJ-fe9AB#|mmNl{fvPNg63rIZp*aA@vjfqEW%m>;mhlwl^E{&HufT;e!bA0yu+IjXKwcbyGqmyNRHZ)}Kx+0LW-cgxn# zRbo^elCxTxS{_)O{G{!~WkIg0iVWOo_GQX_tfu@R@y*Lkt~Vpyi}PogVD z?+x@v1wvvTm6&p@uTr{)L5q6c?Onzoi@_~*_Lg_e+omhe1^tHGSD24?_Vojt&HR!l z=@;c>ziT8}*98{6{QhmlM`Gh?;tQWdr-qA&$76+3agKrcE0e;xW{+!@%@>thlhPMl z$j{Z$4l}+TMRxL~$uS1CL6c$63FdvC^hMHKZO9SCLyFie3|XvguDprBN0N9L;u#tW zxMJtx8yx%-R0FNazy1}v-Qc6Rry68SWBpltX!z8sr#+!I<I5SGlPx60TfTc2&Ii>fTN@em(b`U4dxA z$_sP7{9p$yB$48Zpp=wC(~`Z-x)H;!wsQ6qzbOATl(F1cX2dO`-PZ(^>P((6)69@V{NkCbUr_} zgyv2Cwve_#)C%vlp)m9#^O$ZO-H)jT|TU#f*;i`-C;d7FDZ`zJnz(?vQE1ovQWGhymzsZLr28|1Cr zucBTv?wm}a#QOPqiQMthw*;F)SdNBHEtHdcB=Zc&m{{AYhwx6+bJr$^?Suv`NpKib_pxvJhV3Jox#S= z`Ve7(vil{61EvP#dgqpnwT6Z|(cg^Rd#|uK!A*RUe163>yr-)9FYh{=ibARbt^7`R z>|u~D4?;P}pr{%zhzZkC_)Wp0kTQG=mlTl@<_g8o9W@4(3y>3BLx(D|h*XDZKMvO; zGBEy3%=2CB;QqwjWaRzSqc;=^r%X@zuAx5?5YQ-aIh&hjDc~8omChRE$6&g+f^xjebKp=noMwvDJq15=ZFTJGQFdGU%aD{~pSz6pvzk z>WyuOV<@V~2fHeN1Rt(ylri~8u}=55F?2GJIha(^o+lQ>dKZ4 z!lVh_mie@GQb6S6%5g5`OV)k4e{8a{$Iu^+K?j^E=_Pl(Gl(s3EH5aC)NX+O6fRtt zpATL9;(UU(4UeE*6=)I!A zT~<_tgq~**i@z-T^{2Z#=6uJ8x!4;`rkkl|!QGOD$(Gkhb2WNu+VYZBlGxH!(TVVA zxHtDUrWr0@Ud3X)JUto730XP?$SOt|=;=We4~>oa?=F9-BI70JHY_~0$x<F&mkul41zv=i0ipOoN^5^%v%(4&n-Mzhc!te<1 z#M5~k?Qg}#lB~>3-+Mo8pI+;?X{P^_8`+p{4Enoj<9lgY)4;&M%q%A> z%f*+aY<;qh|Jt>V$AcS>+h^~~9}iXA-LEG7$EgmyK>6OiZz6qbh7$=dm#KuhI5aZ% zL8$SVwKO@6mLspQ4Wrn<{xXMqn|p`H$SW=tB_$WS`-?W~loYvryrVJ(F_c*=hmL~SXRV`g~7%V6=s&oCGrv!TZQbOGB&gwu}JNn&`6LRv8w{GfB8{P#NrlZ}u*5<7)hVAX`+-fHJTSUYKKE$cKMg)l0 zQZGHo&QG*vptag=g%gySwPp*B4|!acVjXQ^czPA4yh~hPs(6?>YZQu}3$Jro{7RhRe`aIAcwUi+1*bwH zmV$(VPM;{ZGAu0O|HF?}&ahM}kAb$fw$Q`<-=@uhlnN5d|1EphnU*XHA?q)R z?cs!Rv{urjxtQLCHp}=c0E`MUTuX|JLHKASFv(gQaG-$-g`6DybFDKQ!t^ymzvHz=R>|#WwI6n&uMn3eB>At-L45!i#-}X?RQ%VNg<%_-&*{!v&_k>`NN` zGkhpv)fIo2DS(Zi|AOH|h88HPXV3f^9d`37h(bsb@FNhIbzv)JXEYhOiWB36|SwqZ4>|L(zOqH6j`Edv7w-%Dkb3Q$0ggHHh+@ccF5MBAtP zt`2c$^x69ImaR0DV;k~o3kn=eD@K>M;X7WQRW8m-B9^EN%)z_kcJfERZ>RPQkBxEV z#d`4tML^M^VUHt%C5s69+((&>0n*rn5n*aPdFAvyYxr3pXmzRl2mJe>6pst>t z-G8@$8x~sN;N#F zw`RwVQy)B#S0&e3Iu||1*L@`hjhrQmD9H$v`0vQDvKGHO`?)D2Wb-xo%a<=7K1iq@ zzaNJ(sG;$69F=(+zRbkLL{~RuJfdb7wom}FhLsVYB>8!GYGy~`pFA0c8hL#pK{5=6 zwZ{GD#~^^7z(+aI`^Uj1qoZX;v_$7@F`4w2E}>AUe1~+X7o8uV@Uc zER|LoT-%3-eS632;I+KE6)tzUCj7nU;;m|!=KQTke|9I`Hb*OFFj>-{#5-K;#Kpyd z5hYxa9$ELTJEBl2E-A_M%IM>`Y^KXV6CrHXokC@(#Cz(7pBq@JqaBSunug!HzTTGk z!wuk>{NY-4mbBl-WL-sNrTb*93whLhJ0bJbd#BVs+YIO!paM7gZ&?Zs3Hfapx6Xw8 zTTG3+7MkTk%7Rwtb+F)$O-f7S3Z}VOm7boCT$Ph!LO9gZMFRdTL@@>a0^+Aly%>H$ zE)CdT1g$AogB}!0A46MA2bFwu+hy56Z(rZqw2C&mpyez-*aYjmSF}}D;B^F#n)v+; zkR)Om#C^N?ZVcWa@Tm6>zuH#I9{hTpAa_vYuNY5ZWymrlr|>zhRLt%p72y!lwz+pB zmMUH0l2j5tlC14fZP6U#i4v=* zxcKJ8`x_h_1+W9AtWN2_ZH}UFBxMXmD3Ct$;64OVm`zz46en9#7{Qb-vA``T`R#a5< zghBjkm2=j=e%%!+R`kSUd!v7%^KKazWUJTiHmY&6`}9rNOwXCU9N_G>vWu^|tk2KK z_X9vb49&XlAwWX6-5?A~f%I(GiCJJ-m@^(jw3I(N&L4DQ}B%*Ch&I z#y=)Z`z+6nrNTV{I^!mP4}gBK@a{fktaqK361+RW3V2|(Qi3ohDHIdT_4YNRNXIvN zak^1@J4TJ6!-NqLZYmpIJ~0Db-CEDZuY^qg2tqdTpllbhbS_ARxf4^|&^T z$YDgN25kkC!TcPOm+9yjKe#+PUEt^JFkNgb`#=&_3=qhX87TS8e?q>ev?H4O}mS@Vz$jw%@%_B3D zwAFW=qHu+I-rd3UV@lL~D|hiO{toG&jE8GR`-f=Z+`K$Xu!Wv;-nWf^-()`l zjS)R9EzJ7?Zk>X(A2&MN6pH3wpOYZ$vbdQ_hs-h>7QFcK3VI#&46&@>M{&gl#l^+I zta;5GSl6gnhqx(lsn|2|mXULYwPgyO&3_GLx%h~jBTlZGlR`hfXjYeqk<2ogLTbuL z4$MB%{9j~JB|eQ)!h}9@`njqXDtz?~ z>Vj4hTf*n(X=!u}3>g_2Z?Oj-Jm%pt(0}}PJP(|%lk62$@2b6Q7@(Ycj3qSv0KoT>P`x!>*h+bo4=>6&J0U z@e@JR-)rg-lpN=@cj8#%?_7OAo5K_?4~-P}6-m#9)#11Dr$^ftjjmOu5H z>m;sh`ylO0Ou5=dgoMiCW(u0yziL3@2QLT{RN+!8pFT7o?e6ZTHSvB%Q~|yVEj4x1 zpRYIiW%n{a&^a9Pee6Gb!%tNiNs=GjTPIY+EkT}zX+6XmKp&}#mnRncAKl4iQ--mCpLK(w5X@OG8R0r~ z3v4c>&ZZAqQU1nBY#u$%UaCq(R{uU-A*mQ~ca5|-8gLfD1oUv&bA6wvkU&e_xUp$p zaD|S?9*j&wpQ4^>wnwOCcLjsX7H};J)35HWoB` zPT(qc-K0yzJT(zZb=nBKv%UR?`KQunWi(3uh(%QN5&~-|FE5XX1u9EB_^xL{A0)rF z3W$>Ihkv?3liGesWU)oXQB>i2HK5Unzy=mT5pOT`_4M`mNHCH|T?1Qj#hXgu$D_Sz z5U5X?WDR>wGen(I23L&VSw=`;Vz1mrOYvSb)Xy`ja~`iE<@+!TT%p>w4{WW~@2@}K zz95^NlXC%qr_L6zz|BwgVj?=X<{PA|I|6j#pn#|o1d7eQwa3}WC4QLE%th%9xwA8xo7}QgR!pq6rRB%wUft2Tv({zqqbwMb$=ZT~AK&0RKFPH3Ws$$O3h%j; z51Y(+odsP~;+R#U;4GFgELPNKi-#DbNTY0_8BZ?&kM>V?&_)1wj;~+8F1(*(s;T*V zS^o4NsK5u*N-3uXY5P>-Vq%fy->xs+b_UG=vI9yfumq!8XKpSoE`I(BC}{soFM41^ z)o)zCF4UNqL)kZJE>MIG)Y5w9Lv9(ZlAe(f7Z<1AsxmcBXVq^0P5aE=0LKTEy{)zN zf6Crv24FVw8#F4o`N&N${r9)##a$*kB~xw2m$xJ4VgyINeY;v7%g;oqQzs-W?AEl^ zH8EjWI0$`{GPG_J1|!6!;A%tHdL|IwwSut{Gf`T77^4E6^5^+?lk5LuCxzW?`|tau z7|9C<9U@fA>KF!h*THjxO;$`uNW0g5Ig^u}9Tt}~K3xicHy^mS zvF=&l8T3iKV3GJ7sP4+ib&bNo74P)-igq_{+=zSnRID-9i^7gpzp@r~tUwv2s1A4` zWgs53v@D86IegEPm5OoBouv+#AMI#)cx)-rny@iJ6TK7IoIG%*yA9u~WIlt008Gj= z0~v&0Y#Yh(qxnqXE_jwc+wH?+%W8*o+bkLyea~-M6s#u-L^KZk(JB(lkrw(c`8c2q zbd9Xi3&x#mHD2hNkJJ!g{=HEKT#~c_OKG$9w57Iow@-ZLXfBT5wl|GMd7$^Du&Sym zbKtLBXg^-0rq1|Ix@>K2K@{Vqge%- zOHWFLOi?Hl2$b!0(ls9a>Cw^Fu63J%|C;vfnV{nkm*B)#$s90-gM&|C1rW0w|EEoJ z418G8r5!bGRDKt~_V?31SHk}uN!3>bBJu*0%!kGKCF)p;Dl*~Eh1~g;Xw-sMPcE0> zD?S2h?tGfev~UW?5DsR|!+bVYJA$FfdM0$@i=QT56bcr2c%&@UV$kCK|M&5>*p{Cc zhJk@0y!|SRA_@iaauxr{lP6&}Ln-FGrCh`^xRw4JGa4GJHE^)6$UXEA3=B-=GdYW_ zT)Lqv(ABAfR04xK>z4%u1#jE{aJo?f_!7a?8{l6S90xr=1z*3l7 zUpEs|_~LC#-Z1H0Yc4>9WO^B-NG>O9e`hvs4*UziPlo9N;9H1b&|bOH9I$PRyh^Th zw$@U?L9UVIV%pF&DMYC=M@!ra1w|R{aEoHf!1+&cyVb7 zN(A!FN0x(w14=${ExDZjb>F!DpRf)))0(3tw_Yh7#OIO4QC}RWV#1c*u-8h#!PzE-P8=H>1E2<5U<=EJ~Qs}#%-DQ&eAAAb)I_f-w@7mZqz^BSdy@o})BI=XXb_rlo-ysqN@^r>h zRUfSovp~4!!-uiQ>o)%fKYj3E;eYc}-b%>n_;j2XH}?2+3AS)Nq&Gv{4ZtiAm!AIq z)4CZwgA^Qd;HKB^J|#PcE0(bi>7`wN8F&)_PUZ{-JurKa2zaZs2r3)2TZ6ewdKOCP`usVrbuJ6-5WWG-={_*-Qy`I& zSsEPzcM_O@S^fJ+%750Z0bZ}-866oBI^(&A->0S+uUskPQt3%xMWanaDOP{0F?gT; zjAB06U4L7$x+2B34vn6&va*iO^U3w6@$mpJZ+dwNBVVeFQ7HC1?=dHnPQ@DM6^hhh z$6Hxhf&H3BK8|Amh7}5;BKipw+4YI{@B{G31i~voZWCEetayoELFbJNvTy ze$>OzL8tBn$paX(Y!Qb6=KRfiw42M@2?9^_crG$m; z04PB(bn=PRm~V7E)%_*_1bLk82Y51>78Q!?Rgx0 zze0et8SFjIqk5LCk*ymV7LjQ1fv#qbG1P%hHx*~L*PLdVjEuKv>~9jk$&KLU z#{FG$?Us@W%=dFxMyT(OZh$GkkXmW5tt%!+4c|(7>ipvA@fWLs0Bc4Zj3nXhYgK)t znxxV{+%E-F@7Q9Wz4TQGr8l?F^y%t7ld9{OZ=qfdTauHHu^A+K!hOu&aKEN{x#XPm zNf_88p_FWBs__v`gON(RmyfVAp62|_zHXhN4C`VHn#9zN&VI;EJ10jAP$Ii`B=F34 zALZN&A{XH>bKYs5z?p5`b%&SUW zxbCOVHs2@pg^iiMn~waZ;X5x4?{p%AWn2XJ0A06sd+)6Yr^veDD&OQ(rpTD@%A*nm zy!2bOtt{)O3Ua&6uZz`4Z+5V^qN1j65ru9Lj%$P%F6sYK91LAalvhY)^mR>ZzPU~a zs%Q6_tH}>{>r|IL`DodO+?nTCcY~_NdFxUPu+SD<2F*ijZMvxr*V`G99~d& zUm%P9!dQxX?WW{2xU>d|Fa4e4_rDi6&1XF6+{4G?hJ(-E!F|S15#~wS`V;&sujCr^ z7SdA%;gHc%iuWCTBP}cL_;?Uvn%;W{F)@dEJ`+V$;syeI|Y8N}FWvx#+a*wlxj$ zg{MEqvAOkKYY~p}0Px<6le~&06{aW=xQ)}Sp<{y0e&(C}i~Es-W%kD~ywt;^iF*um zl!Kp`E^Sv^g`p`}DFs4TNJu^BMC=56&sGLKk;_)Nb84HJz~H^>59U2>3h-VKQy9(R zC;ErE{_c7m)MobCS?lS(^QR3N;ce>BuT9TQW^tgUdG6jNNudyWPwXW__@=F5-gVjM zciPUMm#C0#$Z_`X3jWi$)EPGL;{KNy$u=@pp8?sr=X_!cOV>^_|8`6IO;Df4uzK3R z&nc-TUtYbF-?C&awZz(1QRU11lms{5cCmh{&7GK8r@ zaS|Ky`rgHDkaLw}3u<}>gne6zyd3gMt6|^Oq0Oa4nB4eXOS+1!J?0+&GwSNpj&o-N zC2fnmc+bnfeNB2b@>r~Pl7bi^dY8ZBc-go0&J|xjM#L)l@=UW6|I|&s#{S@Td-_)` zbJ4YnqD6cod_niR*X(vJ(~z^ffwdJHea*@KyKc_(NOH<%Y&1t^z0kqF&S4Rfq!P9g zUs9X&cVJW%+pu%Nm!7p-aU0dBqRai+Z-sWIEmxR;<_ySq35do9SWXe9CbCzzo82Ws zQA>X0Yg`w~r;g^qNxUWe&n??yApvC*qZy+@;??=9oGx+AHr0*X*@9*0L;|6hAvQ$e zf9Km1-xS%+{4z+=MubZ-tEPQLtS(CB0oL!OrAoXhbH<(yW)UajOFm=y^>#B*eeKh86iL{Vkdfg1kMajveih%v z5K$qGeg?bFoi zHydKZA~YMXokMU<_(pFH%bKw1@o0XV3!G>?{|q_uvC$0bHEE#o4Q5iqmY50LDv@_| z;(9hMsHh!w5#vzUyyyryhORUVx=0HOC+{`M%9<5U4ZT{vv;Q1#euB zKR{fjWKf%kfUGO@ou*Q}Pr0uy=IFe4BRE-p6*g#qX_t4Ffnxq$y#! zVZ}8jp+>xWSCx@jcM0UGkWiJ5jqVsB%`zhb9u6&^@jasNB7IVa7T}Pm#b2r*7>5Rk zj6jy@LfzOud*~6t^V8z-8vs)tI+kTWCr=EUvSoSuoK)V9`L&sC`sH|pP>(#}m$Kfb zWjk-?y`YmK9||NV7@$){>q$d_ zgRP$TWh-ZBb5czMi^n65$d)s;4ULVVn;LEn3F#2%BoVn{rIz#tz&xZwg*iAP1#OF4 z9CdUC#b+L#Me?_e=$13-ZVB?nU5wGBC;G`qM2c&5)Z4rwDNHmdNs(x+a8lpwEXI)k z;9QDQt(DO=-9v`SXJA+}Nw~MIUB~Jbe1^pGY6sF_dXWW>#@nnp$pMCW?>7VNxDQf=x>OH098qJTye=ggVYLXdWI zP1G=CO+f}PELeA?DW#L;MWD2`S!)aQ!)$5)-ape4yeRFA)vGc=AQ=F(>!lk0Eoiw=)un*j~!a?r;xxjG1&l0yARXk?~k*(hMbO}~L z$WVg=g%j$tE2}jIk%7#ttn+pod{+2*R28F#AUZz2lzL|1PKK4Cz-k_uJ#01Fa4X9e zb<$hNjO$W(i`#i?tGdUvzht&lbXzr{&Q7^?XY%$Sk=j&x77c-J29bKW%;ANqOG}PI zV3#e8_+n<*pBkpO1Y^3ZjH-3o+oUdl$Mm(Q2fMl|qVCl=4gD`)>W!UJ3%V%sD8m=COf<;^3%#C`L!d zB;)^Wc{}E@k*oa>lf=O$e7OcH@IJ)2qoaQf;(wGi8JqE*d-4_x*vLG*@Uy<@I0@V;3d!37GtSO*cf-qlQl`1D>t}~`_ zvqc&nMoLObOw1RgXNH&yiR|Rq*#6Go{kUs*pg8uX{mdY^EnUh-{W$Dz2xm+2ix)3o zXhPtuNXy|yBg~gkvykaU&nNW4k%C=xMm6@7h|E^!MKFb-(}#ew-;_sGId)z5&AtiT zOfw)50Opb8S75pIN?srcKQmfzW$DPX%($Hg#7;+h*|%R!*QX@OI4 z=2cWwOyMK|upQIUzY`M^aOejIkHC4db{&Q&oZ$S|?rv@#9;DnBS`bcZY>G#)nqq5UM60s?ROXHL?(doLhcUY*A6$lpszf}6hjkj;!puJka zf&$D2w%ARc$1|I!e=9s3k1G+^;Xwb}1p;eP*SSmt}NAsaKT z@~o_L|CV!TLy(E`>zDu!4-dF7l&e5Y{mkZaX2{`s9a8B$E`$L%5dgLweEa3VQCM(m zzKwWc2!U?nyUquxl^g`4pEUS(yI}dNIKULSeY*punmG7d-}{BgtUzKX5ALrOaO6)!&DX>k#LlN=p3~YJs?_mcE61&IN33ob-xD!T`k)f z=_(#GP}gAg^YQTTaC192I)Vp=R0ba(fIJ2sNckG>5lHG}QvjIyS6Ey`7p}|cqK3ii z1g;IIs|pHX9#JZsfs6;bx-^$Asiz6t8ui&qH3$11s1A_Vc8@*Z@Gu{y=x%f}WErfS zx8^>XR)|YWAA(_+xy@(N00&00*9Y=R^LOBH*4^nl^ z3ejxJ5J8wTo)IfUdtvB;en!xOF$Mv_+pSvX!uprt)P_c(@J=8KweA}PBU|&xe0XRm zIVGh9tVbv$1F#anX8C)q6SV2d@Aq+3rg5$=dz>$yy zbozHvW4}2A-Uf-(dkvS>$;n}&d^WswiFpmVq*l_b~ z_Ee78-J+grC~mI;P%{SeYiDJ!y1M#PJwKYlgZz0K>f5Txy01b!n{vDPZy2Ss#c;+z z#;g)de1=iL2OLqT7t^k8!$;IUwGI}nW!@$^kU0?Um)V8@7C6Hp<5f0cH_i8d|D4l& z#WlP3!uFX?v=p)N=yD%Sv#xl0ukT8la8WlE-O&4;p)i|yRq zoN3L=d#t_cEd^((I{j1N7}>hJgrOKgzx4NwNgymar$@^(#~XfiQ=Y28`T*Y1@aStA z8XkwF<#9HMH8nv`5E)4TZs$v{U>H+q z9)A7$CB{pJJd`toZ-bKZQ?0Rk#hZhT4JH)sO$`llmN%7^0uQ?2z!ri)DX?!$(!2o$ zk{9z2oN|Z=-Xk(-@OH4awze;<8h6%%y7m~K^r0Lx1jkM`8aMCUy?cRCq7t?n>5Pt_ z{(ybNoX38{gj=7guP!RG2OkJd4#DX|%55c>I`V)Ip;U5xg}HV{Rv|m)gB?*H{afpV z#NKaNEqUuKHVNrf+%SZtb91l4pqfKo-V8Mi!3zLnhf&PI=JDNCO^{1~CHRz4a$wPO zODsELbIae-(h|;u7CBsAo)UH$nM^|cP~0RT9aByu4Ba;`Z!dRGt5!gOg-z3p=@n0_q(04-mcRN0NVrd!dkkz z9N7aRXBXzAg2$k00a&TGR2IxSe<^ddH7#RkPwrOs{L1#o+Nkg$* I!Tj<60xE$i1ONa4 literal 0 HcmV?d00001 diff --git a/tests/_images/Points_sampled_points_categorical_color_matplotlib.png b/tests/_images/Points_sampled_points_categorical_color_matplotlib.png new file mode 100644 index 0000000000000000000000000000000000000000..0511ea807c239534cf8714c9018fd7342fa6fb49 GIT binary patch literal 15593 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 literal 0 HcmV?d00001 diff --git a/tests/pl/test_render_points.py b/tests/pl/test_render_points.py index eba38fbd..107c1efb 100644 --- a/tests/pl/test_render_points.py +++ b/tests/pl/test_render_points.py @@ -606,6 +606,27 @@ def test_plot_groups_na_color_none_filters_points_datashader(self, sdata_blobs: "blobs_points", color="cat_color", groups=["a"], size=30, method="datashader" ).pl.show(ax=axs[1], title="default (filtered)") + @staticmethod + def _make_sampled_sdata() -> SpatialData: + """Points with two spatially separated clusters, shuffled via .sample() (#358).""" + rng = get_standard_RNG() + n = 100 + x = np.concatenate([rng.uniform(0, 10, n // 2), rng.uniform(90, 100, n // 2)]) + y = np.concatenate([rng.uniform(0, 10, n // 2), rng.uniform(90, 100, n // 2)]) + df = pd.DataFrame({"x": x, "y": y, "cluster": pd.Categorical(["A"] * (n // 2) + ["B"] * (n // 2))}) + sdata = SpatialData(points={"pts": PointsModel.parse(df)}) + sampled = sdata.points["pts"].compute().sample(frac=0.8, random_state=42) + sdata.points["pts"] = PointsModel.parse(sampled) + return sdata + + def test_plot_sampled_points_categorical_color_matplotlib(self): + """Regression test for #358: .sample() must not shuffle categorical colors.""" + self._make_sampled_sdata().pl.render_points("pts", color="cluster", method="matplotlib").pl.show() + + def test_plot_sampled_points_categorical_color_datashader(self): + """Regression test for #358: .sample() must not shuffle categorical colors.""" + self._make_sampled_sdata().pl.render_points("pts", color="cluster", method="datashader").pl.show() + def test_groups_na_color_none_no_match_points(sdata_blobs: SpatialData): """When no elements match the groups, the plot should render without error."""