import%20marimo%0A%0A__generated_with%20%3D%20%220.24.0%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20from%20plotly.subplots%20import%20make_subplots%0A%20%20%20%20from%20scipy%20import%20stats%0A%0A%20%20%20%20return%20go%2C%20make_subplots%2C%20mo%2C%20np%2C%20pd%2C%20stats%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Note%2021%3A%20Kruskal-Wallis%20Test%2C%20Non-Parametric%20ANOVA%2C%20and%20Rank%20Sums%0A%0A%20%20%20%20%26larr%3B%20Previous%20Note%3A%20%5B20%20Spurious%20Correlation%5D(20_spurious_correlation.py)%20%7C%20Next%20Note%3A%20%5B22%20ACF%20and%20PACF%5D(22_acf_and_pacf.py)%20%26rarr%3B%0A%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%20%5Ba%5D%20Why%20do%20you%20need%20to%20know%20these%20concepts%3F%0A%0A%20%20%20%20When%20comparing%20numerical%20metrics%20across%20three%20or%20more%20experimental%20cohorts%20(%24k%20%5Cgeq%203%24)%2C%20classical%20One-Way%20Analysis%20of%20Variance%20(ANOVA)%20is%20the%20traditional%20benchmark.%20However%2C%20ANOVA%20relies%20on%20three%20strict%20parametric%20assumptions%3A%0A%20%20%20%201.%20**Normality%20of%20Residuals**%3A%20Observations%20within%20each%20group%20must%20follow%20a%20Gaussian%20distribution.%0A%20%20%20%202.%20**Homoscedasticity**%3A%20All%20groups%20must%20share%20identical%20population%20variances%20(%24%5Csigma_1%5E2%20%3D%20%5Csigma_2%5E2%20%3D%20%5Cdots%20%3D%20%5Csigma_k%5E2%24).%0A%20%20%20%203.%20**Independence**%3A%20Observations%20are%20independently%20sampled.%0A%0A%20%20%20%20In%20modern%20engineering%2C%20finance%2C%20and%20machine%20learning%20pipelines%2C%20raw%20metrics%20(such%20as%20server%20request%20latencies%2C%20web%20application%20load%20times%2C%20model%20inference%20latencies%2C%20transaction%20fraud%20amounts%2C%20and%20customer%20lifetime%20values)%20consistently%20violate%20normality.%20They%20exhibit%20severe%20positive%20skew%2C%20heavy%20tails%20(Laplace%2C%20Pareto%2C%20Cauchy)%2C%20or%20multimodal%20clusters.%20In%20these%20conditions%2C%20ANOVA's%20%24F%24-test%20loses%20statistical%20power%20and%20yields%20unreliable%20%24p%24-values.%0A%0A%20%20%20%20**The%20Kruskal-Wallis%20%24H%24-test**%20is%20the%20non-parametric%20generalization%20of%20ANOVA%3A%0A%20%20%20%201.%20**Distribution-Free%20Rank%20Test**%3A%20By%20replacing%20raw%20continuous%20values%20with%20their%20ranks%20across%20the%20pooled%20dataset%2C%20Kruskal-Wallis%20eliminates%20sensitivity%20to%20extreme%20outliers%20and%20asymmetric%20heavy%20tails.%0A%20%20%20%202.%20**Hypothesis%20of%20Stochastic%20Dominance**%3A%20Rather%20than%20comparing%20population%20means%20%24%5Cmu_j%24%2C%20Kruskal-Wallis%20tests%20whether%20the%20population%20distributions%20are%20identical%20against%20the%20alternative%20that%20at%20least%20one%20group%20tends%20to%20produce%20stochastically%20larger%20values%20than%20another.%0A%20%20%20%203.%20**Rigorous%20Diagnostic%20Workflow**%3A%20A%20principled%20data%20science%20workflow%20verifies%20homoscedasticity%20(Levene's%20test)%20and%20normality%20(Shapiro-Wilk%20test).%20If%20normality%20is%20rejected%20while%20homoscedasticity%20holds%2C%20Kruskal-Wallis%20is%20the%20appropriate%20test.%0A%20%20%20%204.%20**Post-Hoc%20Pairwise%20Localization**%3A%20If%20the%20omnibus%20Kruskal-Wallis%20test%20rejects%20the%20null%20hypothesis%2C%20post-hoc%20Dunn's%20rank%20sum%20tests%20with%20Bonferroni%20or%20FDR%20corrections%20determine%20precisely%20which%20group%20pairs%20exhibit%20statistically%20significant%20divergence.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%20%5Bb%5D%20Concept%20explanation%20with%20their%20role%20in%20ML%2FAI%2FStats%3F%0A%0A%20%20%20%20%23%23%23%201.%20Mathematical%20Formulation%20of%20the%20%24H%24-Statistic%0A%0A%20%20%20%20Consider%20%24k%24%20independent%20groups%20with%20sample%20sizes%20%24n_1%2C%20n_2%2C%20%5Cdots%2C%20n_k%24.%20The%20total%20number%20of%20pooled%20observations%20is%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20N%20%3D%20%5Csum_%7Bj%3D1%7D%5Ek%20n_j%0A%20%20%20%20%24%24%0A%0A%20%20%20%20All%20%24N%24%20observations%20across%20all%20groups%20are%20combined%20into%20a%20single%20sorted%20array%20and%20assigned%20ranks%20%24r_%7Bij%7D%20%5Cin%20%5C%7B1%2C%202%2C%20%5Cdots%2C%20N%5C%7D%24.%20For%20tied%20observations%2C%20each%20tied%20value%20receives%20the%20average%20(mid-rank)%20of%20the%20positions%20it%20spans.%0A%0A%20%20%20%20Let%20%24R_j%24%20denote%20the%20sum%20of%20ranks%20for%20the%20%24j%24-th%20group%2C%20and%20%24%5Cbar%7BR%7D_j%20%3D%20%5Cfrac%7BR_j%7D%7Bn_j%7D%24%20denote%20the%20mean%20rank%20of%20group%20%24j%24%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20R_j%20%3D%20%5Csum_%7Bi%3D1%7D%5E%7Bn_j%7D%20r_%7Bij%7D%2C%20%5Cquad%20%5Cbar%7BR%7D_j%20%3D%20%5Cfrac%7BR_j%7D%7Bn_j%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20Under%20the%20null%20hypothesis%20%24H_0%24%20that%20all%20%24k%24%20groups%20originate%20from%20identical%20continuous%20distributions%2C%20the%20expected%20average%20rank%20across%20all%20observations%20is%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbar%7BR%7D_%7B%5Ctext%7Boverall%7D%7D%20%3D%20%5Cfrac%7BN%20%2B%201%7D%7B2%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20Kruskal-Wallis%20test%20statistic%20%24H%24%20measures%20the%20weighted%20sum%20of%20squared%20deviations%20of%20group%20average%20ranks%20from%20the%20global%20expected%20mean%20rank%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20H%20%3D%20%5Cfrac%7B12%7D%7BN(N%20%2B%201)%7D%20%5Csum_%7Bj%3D1%7D%5Ek%20n_j%20%5Cleft(%5Cbar%7BR%7D_j%20-%20%5Cfrac%7BN%20%2B%201%7D%7B2%7D%5Cright)%5E2%0A%20%20%20%20%24%24%0A%0A%20%20%20%20Expanding%20the%20quadratic%20term%20yields%20the%20standard%20computational%20form%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20H%20%3D%20%5Cleft%5B%5Cfrac%7B12%7D%7BN(N%20%2B%201)%7D%20%5Csum_%7Bj%3D1%7D%5Ek%20%5Cfrac%7BR_j%5E2%7D%7Bn_j%7D%5Cright%5D%20-%203(N%20%2B%201)%0A%20%20%20%20%24%24%0A%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%23%202.%20Tie%20Correction%20Factor%0A%0A%20%20%20%20When%20tied%20values%20exist%20in%20the%20sample%2C%20the%20variance%20of%20the%20ranks%20decreases.%20The%20test%20statistic%20is%20adjusted%20by%20dividing%20by%20a%20tie%20correction%20factor%20%24C_%7B%5Ctext%7Btie%7D%7D%24%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20C_%7B%5Ctext%7Btie%7D%7D%20%3D%201%20-%20%5Cfrac%7B%5Csum_%7Bm%3D1%7D%5EG%20(t_m%5E3%20-%20t_m)%7D%7BN%5E3%20-%20N%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20where%20%24G%24%20is%20the%20number%20of%20distinct%20tie%20groups%2C%20and%20%24t_m%24%20is%20the%20count%20of%20observations%20tied%20at%20the%20%24m%24-th%20value.%20The%20adjusted%20statistic%20is%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20H_%7B%5Ctext%7Badj%7D%7D%20%3D%20%5Cfrac%7BH%7D%7BC_%7B%5Ctext%7Btie%7D%7D%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%23%203.%20Asymptotic%20Chi-Square%20Distribution%20and%20Inference%0A%0A%20%20%20%20Under%20%24H_0%24%2C%20as%20the%20individual%20group%20sample%20sizes%20satisfy%20%24n_j%20%5Cgeq%205%24%2C%20the%20sampling%20distribution%20of%20%24H%24%20converges%20asymptotically%20to%20a%20Chi-Square%20distribution%20with%20%24k%20-%201%24%20degrees%20of%20freedom%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20H%20%5Cxrightarrow%7Bd%7D%20%5Cchi%5E2(k%20-%201)%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20two-tailed%20%24p%24-value%20is%20evaluated%20via%20the%20Chi-Square%20survival%20function%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20p%20%3D%201%20-%20F_%7B%5Cchi%5E2%7D(H%3B%20k%20-%201)%20%3D%20%5Cint_H%5E%5Cinfty%20%5Cfrac%7Bx%5E%7B%5Cfrac%7Bk-1%7D%7B2%7D%20-%201%7D%20e%5E%7B-%5Cfrac%7Bx%7D%7B2%7D%7D%7D%7B2%5E%7B%5Cfrac%7Bk-1%7D%7B2%7D%7D%20%5CGamma%5Cleft(%5Cfrac%7Bk-1%7D%7B2%7D%5Cright)%7D%20%5C%2C%20dx%0A%20%20%20%20%24%24%0A%0A%20%20%20%20If%20%24p%20%3C%20%5Calpha%24%20(typically%20%240.05%24)%2C%20we%20reject%20the%20null%20hypothesis%20and%20conclude%20that%20at%20least%20one%20group%20stochastically%20dominates%20another.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%23%204.%20Post-Hoc%20Dunn's%20Test%20with%20Multiple%20Comparison%20Adjustments%0A%0A%20%20%20%20The%20Kruskal-Wallis%20test%20is%20an%20omnibus%20test%3A%20it%20establishes%20that%20a%20difference%20exists%2C%20but%20does%20not%20indicate%20which%20specific%20group%20pairs%20differ.%20To%20evaluate%20pairwise%20contrasts%20between%20group%20%24i%24%20and%20group%20%24j%24%2C%20**Dunn's%20test**%20evaluates%20the%20standardized%20difference%20in%20mean%20ranks%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20z_%7Bij%7D%20%3D%20%5Cfrac%7B%7C%5Cbar%7BR%7D_i%20-%20%5Cbar%7BR%7D_j%7C%7D%7B%5Csigma_%7Bij%7D%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20where%20the%20standard%20error%20of%20the%20rank%20difference%20under%20%24H_0%24%20is%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Csigma_%7Bij%7D%20%3D%20%5Csqrt%7B%5Cleft(%5Cfrac%7BN(N%20%2B%201)%7D%7B12%7D%20-%20%5Cfrac%7B%5Csum%20(t_m%5E3%20-%20t_m)%7D%7B12(N%20-%201)%7D%5Cright)%20%5Cleft(%5Cfrac%7B1%7D%7Bn_i%7D%20%2B%20%5Cfrac%7B1%7D%7Bn_j%7D%5Cright)%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20For%20%24m%20%3D%20%5Cbinom%7Bk%7D%7B2%7D%24%20simultaneous%20pairwise%20comparisons%2C%20the%20family-wise%20error%20rate%20is%20controlled%20using%20Bonferroni%20correction%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20p_%7B%5Ctext%7Badj%7D%7D%20%3D%20%5Cmin%5Cleft(1.0%2C%20%5C%2C%20m%20%5Ccdot%202%5Cleft(1%20-%20%5CPhi(%7Cz_%7Bij%7D%7C)%5Cright)%5Cright)%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pd)%3A%0A%20%20%20%20%23%20Data%20Generation%3A%20Heavy-Tailed%20Laplace%20Distributed%20Cohorts%20(Server%20Latency%20Benchmark)%0A%20%20%20%20%23%203%20Server%20Configurations%3A%20Standard%20(Loc%3D20ms)%2C%20Optimized%20(Loc%3D21ms)%2C%20Cache-Boosted%20(Loc%3D23.5ms)%0A%20%20%20%20np.random.seed(47)%0A%20%20%20%20_n%20%3D%20100%0A%0A%20%20%20%20group1%20%3D%20np.random.laplace(loc%3D20.0%2C%20scale%3D4.5%2C%20size%3D_n)%0A%20%20%20%20group2%20%3D%20np.random.laplace(loc%3D21.0%2C%20scale%3D4.5%2C%20size%3D_n)%0A%20%20%20%20group3%20%3D%20np.random.laplace(loc%3D23.5%2C%20scale%3D4.5%2C%20size%3D_n)%0A%0A%20%20%20%20df_kruskal%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Latency%22%3A%20np.concatenate(%5Bgroup1%2C%20group2%2C%20group3%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Config%22%3A%20np.repeat(%5B%22Cluster%20A%20(20ms)%22%2C%20%22Cluster%20B%20(21ms)%22%2C%20%22Cluster%20C%20(23.5ms)%22%5D%2C%20_n)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20return%20df_kruskal%2C%20group1%2C%20group2%2C%20group3%0A%0A%0A%40app.cell%0Adef%20_(df_kruskal%2C%20go%2C%20group1%2C%20group2%2C%20group3%2C%20make_subplots%2C%20np%2C%20stats)%3A%0A%20%20%20%20%23%20Interactive%20Visualizations%20Cell%3A%0A%20%20%20%20%23%20Subplot%201%3A%20Violin%20%26%20Box%20Plot%20showing%20non-Gaussian%20heavy%20tails%0A%20%20%20%20%23%20Subplot%202%3A%20Normal%20Q-Q%20Plots%20revealing%20systematic%20Laplace%20tail%20departure%0A%20%20%20%20%23%20Subplot%203%3A%20Pooled%20Rank%20Sum%20Distributions%20and%20Mean%20Rank%20Comparison%0A%0A%20%20%20%20_fig%20%3D%20make_subplots(%0A%20%20%20%20%20%20%20%20rows%3D1%2C%0A%20%20%20%20%20%20%20%20cols%3D3%2C%0A%20%20%20%20%20%20%20%20subplot_titles%3D(%0A%20%20%20%20%20%20%20%20%20%20%20%20%221.%20Group%20Distributions%20(Violin%20%2B%20Quartiles)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%222.%20Normal%20Q-Q%20Diagnostics%20(Heavy%20Tails)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%223.%20Pooled%20Mean%20Ranks%20vs.%20H0%20Expected%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20horizontal_spacing%3D0.09%2C%0A%20%20%20%20)%0A%0A%20%20%20%20_colors%20%3D%20%7B%22Cluster%20A%20(20ms)%22%3A%20%22%233b82f6%22%2C%20%22Cluster%20B%20(21ms)%22%3A%20%22%2310b981%22%2C%20%22Cluster%20C%20(23.5ms)%22%3A%20%22%238b5cf6%22%7D%0A%0A%20%20%20%20%23%20Subplot%201%3A%20Violins%0A%20%20%20%20for%20_grp%2C%20_col%20in%20_colors.items()%3A%0A%20%20%20%20%20%20%20%20_sub%20%3D%20df_kruskal%5Bdf_kruskal%5B%22Config%22%5D%20%3D%3D%20_grp%5D%5B%22Latency%22%5D%0A%20%20%20%20%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Violin(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3D_sub%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D_grp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20box_visible%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20meanline_visible%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20line_color%3D_col%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fillcolor%3D_col%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3D0.6%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20points%3D%22outliers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20row%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20col%3D1%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Subplot%202%3A%20Q-Q%20Plot%20of%20Cluster%20A%20against%20Standard%20Normal%0A%20%20%20%20_osm%2C%20_osr%20%3D%20stats.probplot(group1%2C%20dist%3D%22norm%22)%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_osm%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D_osm%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%3Ddict(size%3D5%2C%20color%3D%22%233b82f6%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Cluster%20A%20Quantiles%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20hovertemplate%3D%22Theoretical%3A%20%25%7Bx%3A.2f%7D%3Cbr%3ESample%3A%20%25%7By%3A.2f%7D%3Cextra%3E%3C%2Fextra%3E%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20row%3D1%2C%0A%20%20%20%20%20%20%20%20col%3D2%2C%0A%20%20%20%20)%0A%20%20%20%20%23%20Q-Q%20Reference%20Line%0A%20%20%20%20_line_x%20%3D%20np.array(%5B_osm%5B0%5D.min()%2C%20_osm%5B0%5D.max()%5D)%0A%20%20%20%20_line_y%20%3D%20_osr%5B1%5D%20%2B%20_osr%5B0%5D%20*%20_line_x%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_line_x%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D_line_y%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line%3Ddict(color%3D%22%23ef4444%22%2C%20dash%3D%22dash%22%2C%20width%3D2)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Gaussian%20Reference%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20row%3D1%2C%0A%20%20%20%20%20%20%20%20col%3D2%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Subplot%203%3A%20Pooled%20Mean%20Ranks%0A%20%20%20%20_pooled_data%20%3D%20np.concatenate(%5Bgroup1%2C%20group2%2C%20group3%5D)%0A%20%20%20%20_pooled_ranks%20%3D%20stats.rankdata(_pooled_data)%0A%20%20%20%20_n1%2C%20_n2%2C%20_n3%20%3D%20len(group1)%2C%20len(group2)%2C%20len(group3)%0A%20%20%20%20_mean_r1%20%3D%20np.mean(_pooled_ranks%5B%3A_n1%5D)%0A%20%20%20%20_mean_r2%20%3D%20np.mean(_pooled_ranks%5B_n1%20%3A%20_n1%20%2B%20_n2%5D)%0A%20%20%20%20_mean_r3%20%3D%20np.mean(_pooled_ranks%5B_n1%20%2B%20_n2%20%3A%5D)%0A%20%20%20%20_expected_mean%20%3D%20(len(_pooled_data)%20%2B%201)%20%2F%202.0%0A%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Bar(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D%5B%22Cluster%20A%22%2C%20%22Cluster%20B%22%2C%20%22Cluster%20C%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D%5B_mean_r1%2C%20_mean_r2%2C%20_mean_r3%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker_color%3D%5B%22%233b82f6%22%2C%20%22%2310b981%22%2C%20%22%238b5cf6%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Mean%20Rank%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3D%5Bf%22%7B_mean_r1%3A.1f%7D%22%2C%20f%22%7B_mean_r2%3A.1f%7D%22%2C%20f%22%7B_mean_r3%3A.1f%7D%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20textposition%3D%22auto%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20row%3D1%2C%0A%20%20%20%20%20%20%20%20col%3D3%2C%0A%20%20%20%20)%0A%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D%5B%22Cluster%20A%22%2C%20%22Cluster%20C%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D%5B_expected_mean%2C%20_expected_mean%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line%3Ddict(color%3D%22%23ef4444%22%2C%20width%3D2.5%2C%20dash%3D%22dash%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22H0%20Expected%20Rank%20(150.5)%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20row%3D1%2C%0A%20%20%20%20%20%20%20%20col%3D3%2C%0A%20%20%20%20)%0A%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D480%2C%0A%20%20%20%20%20%20%20%20title%3Ddict(%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3D%22Non-Parametric%20Group%20Diagnostics%3A%20Distributions%2C%20Normality%2C%20and%20Ranks%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D0.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xanchor%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20font%3Ddict(size%3D16%2C%20family%3D%22Inter%2C%20system-ui%2C%20sans-serif%22)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20legend%3Ddict(orientation%3D%22h%22%2C%20yanchor%3D%22bottom%22%2C%20y%3D-0.22%2C%20xanchor%3D%22center%22%2C%20x%3D0.5)%2C%0A%20%20%20%20%20%20%20%20margin%3Ddict(l%3D40%2C%20r%3D40%2C%20t%3D70%2C%20b%3D80)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20_fig.update_yaxes(title_text%3D%22Latency%20(ms)%22%2C%20row%3D1%2C%20col%3D1)%0A%20%20%20%20_fig.update_xaxes(title_text%3D%22Theoretical%20Normal%20Quantiles%22%2C%20row%3D1%2C%20col%3D2)%0A%20%20%20%20_fig.update_yaxes(title_text%3D%22Sample%20Latency%20Quantiles%22%2C%20row%3D1%2C%20col%3D2)%0A%20%20%20%20_fig.update_xaxes(title_text%3D%22Configuration%20Group%22%2C%20row%3D1%2C%20col%3D3)%0A%20%20%20%20_fig.update_yaxes(title_text%3D%22Average%20Group%20Rank%22%2C%20row%3D1%2C%20col%3D3)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%20%5Bd%5D%20Code%20Examples%0A%0A%20%20%20%20Below%20we%20implement%20two%20end-to-end%20production%20statistical%20workflows%3A%0A%20%20%20%201.%20**Diagnostic%20Testing%20Pipeline**%3A%20Formally%20running%20Levene's%20test%20for%20homoscedasticity%2C%20Shapiro-Wilk%20tests%20for%20Gaussian%20distribution%20conformance%2C%20and%20deciding%20whether%20ANOVA%20or%20Kruskal-Wallis%20is%20statistically%20valid.%0A%20%20%20%202.%20**Kruskal-Wallis%20%24H%24-test%20from%20Scratch%20%26%20Post-Hoc%20Dunn's%20Procedure**%3A%20Computing%20rank%20sums%2C%20tie%20corrections%2C%20asymptotic%20%24%5Cchi%5E2%24%20%24p%24-value%20against%20%60scipy.stats.kruskal%60%2C%20followed%20by%20Dunn's%20pairwise%20contrasts%20with%20Bonferroni%20multiple%20testing%20adjustment.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(group1%2C%20group2%2C%20group3%2C%20pd%2C%20stats)%3A%0A%20%20%20%20%23%20Example%201%3A%20Formal%20Diagnostic%20Pipeline%20(Homoscedasticity%20%26%20Normality)%0A%20%20%20%20_lev_stat%2C%20_lev_p%20%3D%20stats.levene(group1%2C%20group2%2C%20group3%2C%20center%3D%22median%22)%0A%0A%20%20%20%20_shapiro_results%20%3D%20%5B%5D%0A%20%20%20%20_shapiro_results.append(%7B%0A%20%20%20%20%20%20%20%20%22Diagnostic%20Test%22%3A%20%22Levene's%20Homoscedasticity%20Test%22%2C%0A%20%20%20%20%20%20%20%20%22Target%22%3A%20%22All%203%20Groups%20(Joint)%22%2C%0A%20%20%20%20%20%20%20%20%22Test%20Statistic%22%3A%20f%22W%20%3D%20%7B_lev_stat%3A.4f%7D%22%2C%0A%20%20%20%20%20%20%20%20%22p-Value%22%3A%20f%22%7B_lev_p%3A.4f%7D%22%2C%0A%20%20%20%20%20%20%20%20%22Decision%20(alpha%3D0.05)%22%3A%20%22Equal%20Variances%20Upheld%22%20if%20_lev_p%20%3E%3D%200.05%20else%20%22Heteroscedasticity%20Detected%22%2C%0A%20%20%20%20%7D)%0A%0A%20%20%20%20for%20_idx%2C%20(_name%2C%20_grp)%20in%20enumerate(%5B(%22Cluster%20A%22%2C%20group1)%2C%20(%22Cluster%20B%22%2C%20group2)%2C%20(%22Cluster%20C%22%2C%20group3)%5D)%3A%0A%20%20%20%20%20%20%20%20_w_stat%2C%20_p_val%20%3D%20stats.shapiro(_grp)%0A%20%20%20%20%20%20%20%20_shapiro_results.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Diagnostic%20Test%22%3A%20%22Shapiro-Wilk%20Normality%20Test%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Target%22%3A%20_name%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Test%20Statistic%22%3A%20f%22W%20%3D%20%7B_w_stat%3A.4f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p-Value%22%3A%20f%22%7B_p_val%3A.2e%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Decision%20(alpha%3D0.05)%22%3A%20%22Normal%22%20if%20_p_val%20%3E%3D%200.05%20else%20%22Non-Gaussian%20(Reject%20Normality)%22%2C%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20_df_diagnostics%20%3D%20pd.DataFrame(_shapiro_results)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(group1%2C%20group2%2C%20group3%2C%20np%2C%20pd%2C%20stats)%3A%0A%20%20%20%20%23%20Example%202%3A%20Pure%20NumPy%20Kruskal-Wallis%20from%20Scratch%20%2B%20Post-Hoc%20Dunn's%20Test%0A%20%20%20%20_groups%20%3D%20%5Bgroup1%2C%20group2%2C%20group3%5D%0A%20%20%20%20_k%20%3D%20len(_groups)%0A%20%20%20%20_sizes%20%3D%20%5Blen(_g)%20for%20_g%20in%20_groups%5D%0A%20%20%20%20_N%20%3D%20sum(_sizes)%0A%0A%20%20%20%20%23%201.%20Pool%20data%20and%20compute%20mid-ranks%0A%20%20%20%20_pooled_vals%20%3D%20np.concatenate(_groups)%0A%20%20%20%20_ranks%20%3D%20stats.rankdata(_pooled_vals)%0A%0A%20%20%20%20%23%202.%20Group%20Rank%20Sums%0A%20%20%20%20_offset%20%3D%200%0A%20%20%20%20_rank_sums%20%3D%20%5B%5D%0A%20%20%20%20_mean_ranks%20%3D%20%5B%5D%0A%20%20%20%20for%20_n%20in%20_sizes%3A%0A%20%20%20%20%20%20%20%20_r_g%20%3D%20_ranks%5B_offset%20%3A%20_offset%20%2B%20_n%5D%0A%20%20%20%20%20%20%20%20_rank_sums.append(np.sum(_r_g))%0A%20%20%20%20%20%20%20%20_mean_ranks.append(np.mean(_r_g))%0A%20%20%20%20%20%20%20%20_offset%20%2B%3D%20_n%0A%0A%20%20%20%20%23%203.%20Unadjusted%20H%20statistic%0A%20%20%20%20_sum_sq_term%20%3D%20sum((_R**2)%20%2F%20_n%20for%20_R%2C%20_n%20in%20zip(_rank_sums%2C%20_sizes))%0A%20%20%20%20_H_raw%20%3D%20(12.0%20%2F%20(_N%20*%20(_N%20%2B%201)))%20*%20_sum_sq_term%20-%203.0%20*%20(_N%20%2B%201)%0A%0A%20%20%20%20%23%204.%20Tie%20correction%20factor%0A%20%20%20%20_%2C%20_counts%20%3D%20np.unique(_pooled_vals%2C%20return_counts%3DTrue)%0A%20%20%20%20_tie_groups%20%3D%20_counts%5B_counts%20%3E%201%5D%0A%20%20%20%20if%20len(_tie_groups)%20%3E%200%3A%0A%20%20%20%20%20%20%20%20_c_tie%20%3D%201.0%20-%20np.sum(_tie_groups**3%20-%20_tie_groups)%20%2F%20(_N**3%20-%20_N)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20_c_tie%20%3D%201.0%0A%0A%20%20%20%20_H_adj%20%3D%20_H_raw%20%2F%20_c_tie%0A%20%20%20%20_df%20%3D%20_k%20-%201%0A%20%20%20%20_p_scratch%20%3D%201.0%20-%20stats.chi2.cdf(_H_adj%2C%20df%3D_df)%0A%0A%20%20%20%20%23%20SciPy%20verification%0A%20%20%20%20_scipy_h%2C%20_scipy_p%20%3D%20stats.kruskal(group1%2C%20group2%2C%20group3)%0A%0A%20%20%20%20%23%205.%20Post-Hoc%20Dunn's%20Test%20(Pairwise%20Differences)%0A%20%20%20%20_pairs%20%3D%20%5B(%22Cluster%20A%22%2C%20%22Cluster%20B%22%2C%200%2C%201)%2C%20(%22Cluster%20A%22%2C%20%22Cluster%20C%22%2C%200%2C%202)%2C%20(%22Cluster%20B%22%2C%20%22Cluster%20C%22%2C%201%2C%202)%5D%0A%20%20%20%20_num_comparisons%20%3D%20len(_pairs)%0A%20%20%20%20_dunn_records%20%3D%20%5B%5D%0A%0A%20%20%20%20for%20_name_a%2C%20_name_b%2C%20_i%2C%20_j%20in%20_pairs%3A%0A%20%20%20%20%20%20%20%20_diff%20%3D%20np.abs(_mean_ranks%5B_i%5D%20-%20_mean_ranks%5B_j%5D)%0A%20%20%20%20%20%20%20%20_se%20%3D%20np.sqrt((_N%20*%20(_N%20%2B%201)%20%2F%2012.0)%20*%20(1.0%20%2F%20_sizes%5B_i%5D%20%2B%201.0%20%2F%20_sizes%5B_j%5D))%0A%20%20%20%20%20%20%20%20_z_pair%20%3D%20_diff%20%2F%20_se%0A%20%20%20%20%20%20%20%20_p_unadj%20%3D%202.0%20*%20(1.0%20-%20stats.norm.cdf(_z_pair))%0A%20%20%20%20%20%20%20%20_p_bonf%20%3D%20min(1.0%2C%20_p_unadj%20*%20_num_comparisons)%0A%20%20%20%20%20%20%20%20_dunn_records.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Pairwise%20Contrast%22%3A%20f%22%7B_name_a%7D%20vs%20%7B_name_b%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Mean%20Rank%20Diff%22%3A%20f%22%7B_diff%3A.2f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Standard%20Error%22%3A%20f%22%7B_se%3A.2f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22z-Score%22%3A%20f%22%7B_z_pair%3A.3f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Unadjusted%20p-Value%22%3A%20f%22%7B_p_unadj%3A.4f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Bonferroni%20Adjusted%20p-Value%22%3A%20f%22%7B_p_bonf%3A.4f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Significant%20(alpha%3D0.05)%22%3A%20%22Yes%20(p%20%3C%200.05)%22%20if%20_p_bonf%20%3C%200.05%20else%20%22No%20(p%20%3E%3D%200.05)%22%2C%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20_df_kruskal_summary%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22Metric%22%3A%20%22Omnibus%20H-Statistic%20(Scratch)%22%2C%20%22Value%22%3A%20f%22%7B_H_adj%3A.4f%7D%22%2C%20%22Details%22%3A%20%22Adjusted%20for%20tied%20mid-ranks%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22Metric%22%3A%20%22SciPy%20Reference%20H-Statistic%22%2C%20%22Value%22%3A%20f%22%7B_scipy_h%3A.4f%7D%22%2C%20%22Details%22%3A%20%22scipy.stats.kruskal()%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22Metric%22%3A%20%22Degrees%20of%20Freedom%20(k%20-%201)%22%2C%20%22Value%22%3A%20str(_df)%2C%20%22Details%22%3A%20%22k%20%3D%203%20groups%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22Metric%22%3A%20%22Chi-Square%20Asymptotic%20p-Value%22%2C%20%22Value%22%3A%20f%22%7B_p_scratch%3A.4e%7D%22%2C%20%22Details%22%3A%20%22Reject%20H0%20(medians%20differ)%22%7D%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%0A%20%20%20%20_df_dunn%20%3D%20pd.DataFrame(_dunn_records)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
9b2be56a20b3a07622b70491ac1e006a