@@ -582,6 +582,28 @@ function createRoundHistogram(runs, canvasId) {
582582 } ) ;
583583}
584584
585+ // Two-tailed 95% CI critical values: t(0.025, df)
586+ // df = n - 1, where n = run_count
587+ const T_CRIT_95 = {
588+ 1 : 12.706 , 2 : 4.303 , 3 : 3.182 , 4 : 2.776 , 5 : 2.571 ,
589+ 6 : 2.447 , 7 : 2.365 , 8 : 2.306 , 9 : 2.262 , 10 : 2.228 ,
590+ 11 : 2.201 , 12 : 2.179 , 13 : 2.160 , 14 : 2.145 , 15 : 2.131 ,
591+ 16 : 2.120 , 17 : 2.110 , 18 : 2.101 , 19 : 2.093 , 20 : 2.086 ,
592+ 25 : 2.060 , 30 : 2.042 , 40 : 2.021 , 60 : 2.000 , 120 : 1.980
593+ } ;
594+
595+ function tCritical ( n ) {
596+ const df = n - 1 ;
597+ if ( df <= 0 ) return 0 ;
598+ if ( T_CRIT_95 [ df ] ) return T_CRIT_95 [ df ] ;
599+ // For intermediate df, find closest lower key
600+ const keys = Object . keys ( T_CRIT_95 ) . map ( Number ) . sort ( ( a , b ) => a - b ) ;
601+ for ( let i = keys . length - 1 ; i >= 0 ; i -- ) {
602+ if ( keys [ i ] <= df ) return T_CRIT_95 [ keys [ i ] ] ;
603+ }
604+ return 1.96 ; // z-value fallback for large n
605+ }
606+
585607// Create performance bar chart with error bars
586608function createPerformanceBarChart ( entries ) {
587609 const ctx = document . getElementById ( 'performance-chart' ) . getContext ( '2d' ) ;
@@ -612,8 +634,15 @@ function createPerformanceBarChart(entries) {
612634 fillColors . push ( base ) ;
613635 } ) ;
614636
637+ // Compute 95% CI half-widths: t(0.025, n-1) × SD / √n
638+ const ciHalfWidths = entries . map ( ( entry , i ) => {
639+ const n = entry . run_count ;
640+ const t = tCritical ( n ) ;
641+ return t * stdDevs [ i ] / Math . sqrt ( n ) ;
642+ } ) ;
643+
615644 // Calculate Y-axis max to include error bars
616- const maxWithError = Math . max ( ...avgRounds . map ( ( avg , i ) => avg + stdDevs [ i ] ) ) ;
645+ const maxWithError = Math . max ( ...avgRounds . map ( ( avg , i ) => avg + ciHalfWidths [ i ] ) ) ;
617646 // Add 0.5 padding above highest error bar, then round up to next integer for clean axis labels
618647 const yAxisMax = Math . ceil ( maxWithError + 0.5 ) ;
619648
@@ -634,8 +663,8 @@ function createPerformanceBarChart(entries) {
634663 borderWidth : 0 ,
635664 errorBars : {
636665 'Average Final Round' : {
637- plus : stdDevs ,
638- minus : stdDevs
666+ plus : ciHalfWidths ,
667+ minus : ciHalfWidths
639668 }
640669 }
641670 } ]
@@ -652,8 +681,11 @@ function createPerformanceBarChart(entries) {
652681 tooltip : {
653682 ...ChartConfig . getTooltipFonts ( ) ,
654683 callbacks : {
655- label : ( context ) =>
656- `${ context . parsed . y . toFixed ( 1 ) } ± ${ stdDevs [ context . dataIndex ] . toFixed ( 1 ) } `
684+ label : ( context ) => {
685+ const ci = ciHalfWidths [ context . dataIndex ] ;
686+ const avg = context . parsed . y ;
687+ return `${ avg . toFixed ( 1 ) } ± ${ ci . toFixed ( 1 ) } (95% CI)` ;
688+ }
657689 }
658690 }
659691 } ,
@@ -695,7 +727,7 @@ function createPerformanceBarChart(entries) {
695727 const x = bar . x ;
696728 const y = bar . y ;
697729 const value = dataset . data [ index ] ;
698- const stdDev = stdDevs [ index ] ;
730+ const stdDev = ciHalfWidths [ index ] ;
699731 const scale = chart . scales . y ;
700732
701733 // Calculate error bar positions
0 commit comments