import React, { useState } from "react"; const BoxPlotComparisonWidget: React.FC = () => { // Box Plot A is fixed const statsA = { min: 10, q1: 18, med: 24, q3: 30, max: 42 }; // Box Plot B is adjustable const [shift, setShift] = useState(0); // Shift median const [spread, setSpread] = useState(1); // Scale spread const statsB = { min: 10 + shift - 5 * (spread - 1), // Just approximating visual expansion q1: 16 + shift - 2 * (spread - 1), med: 26 + shift, q3: 34 + shift + 2 * (spread - 1), max: 38 + shift + 4 * (spread - 1), }; const scaleX = (val: number) => (val / 60) * 100; // 0 to 60 range mapping to % const BoxPlot = ({ stats, color, label, }: { stats: any; color: string; label: string; }) => { const leftW = scaleX(stats.min); const rightW = scaleX(stats.max); const boxL = scaleX(stats.q1); const boxR = scaleX(stats.q3); const med = scaleX(stats.med); return (
{label}
{/* Main Line (Whisker to Whisker) */}
{/* Whiskers */}
{/* Box */}
{/* Median Line */}
{/* Labels on Hover */}
Min:{stats.min.toFixed(0)} Q1:{stats.q1.toFixed(0)} Med: {stats.med.toFixed(0)} Q3:{stats.q3.toFixed(0)} Max: {stats.max.toFixed(0)}
); }; const iqrA = statsA.q3 - statsA.q1; const iqrB = statsB.q3 - statsB.q1; return (
{/* Axis */}
0 10 20 30 40 50 60
setShift(parseInt(e.target.value))} className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600" />
setSpread(parseFloat(e.target.value))} className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600" />
Median Comparison
{statsA.med} {statsA.med > statsB.med ? ">" : statsA.med < statsB.med ? "<" : "="} {statsB.med}
IQR Comparison
{iqrA.toFixed(0)} {iqrA > iqrB ? ">" : iqrA < iqrB ? "<" : "="} {iqrB.toFixed(0)}
The box length represents the IQR (Middle 50%). The whiskers represent the full Range.
); }; export default BoxPlotComparisonWidget;