1
+ <!DOCTYPE html>
2
+ < html >
3
+
4
+ < head >
5
+ < meta charset ="utf-8 ">
6
+ < title > Normal vs t Distribution</ title >
7
+ < script src ="https://cdn.plot.ly/plotly-2.24.1.min.js "> </ script >
8
+ < script src ="https://cdn.jsdelivr.net/npm/jstat@latest/dist/jstat.min.js "> </ script >
9
+ < style >
10
+ body {
11
+ font-family : system-ui, sans-serif;
12
+ max-width : 1100px ;
13
+ margin : 0 auto;
14
+ padding : 10px ;
15
+ font-size : 14px ;
16
+ }
17
+
18
+ .controls {
19
+ margin : 1rem 0 ;
20
+ display : flex;
21
+ gap : 1rem ;
22
+ align-items : center;
23
+ }
24
+
25
+ .control-group {
26
+ display : flex;
27
+ flex-direction : column;
28
+ gap : 0.5rem ;
29
+ }
30
+
31
+ label {
32
+ font-size : 13px ;
33
+ }
34
+
35
+ input [type = "range" ] {
36
+ width : 200px ;
37
+ }
38
+
39
+ # stats {
40
+ font-size : 13px ;
41
+ margin-top : 1rem ;
42
+ color : # 666 ;
43
+ }
44
+ </ style >
45
+ </ head >
46
+
47
+ < body >
48
+ < div id ="app ">
49
+ < div class ="controls ">
50
+ < div class ="control-group ">
51
+ < label > Mean: < span id ="mean-value "> 0</ span > </ label >
52
+ < input type ="range " id ="mean " min ="-3 " max ="3 " step ="0.1 " value ="0 ">
53
+ </ div >
54
+ < div class ="control-group ">
55
+ < label > Standard Deviation: < span id ="std-value "> 1</ span > </ label >
56
+ < input type ="range " id ="std " min ="0.1 " max ="3 " step ="0.1 " value ="1 ">
57
+ </ div >
58
+ < div class ="control-group ">
59
+ < label > Sample Size: < span id ="size-value "> 30</ span > </ label >
60
+ < input type ="range " id ="size " min ="2 " max ="100 " step ="1 " value ="30 ">
61
+ </ div >
62
+ </ div >
63
+ < div id ="plot "> </ div >
64
+ < div id ="stats "> </ div >
65
+ </ div >
66
+
67
+ < script >
68
+ // State
69
+ const state = {
70
+ mean : 0 ,
71
+ stdDev : 1 ,
72
+ sampleSize : 30
73
+ } ;
74
+
75
+ // Normal distribution PDF
76
+ function normalPDF ( x , mu , sigma ) {
77
+ return ( 1 / ( sigma * Math . sqrt ( 2 * Math . PI ) ) ) *
78
+ Math . exp ( - 0.5 * Math . pow ( ( x - mu ) / sigma , 2 ) ) ;
79
+ }
80
+
81
+ // T distribution PDF
82
+ function tPDF ( x , df ) {
83
+ function gamma ( n ) {
84
+ if ( n === 1 ) return 1 ;
85
+ if ( n === 0.5 ) return Math . sqrt ( Math . PI ) ;
86
+ return ( n - 1 ) * gamma ( n - 1 ) ;
87
+ }
88
+
89
+ const coefficient = gamma ( ( df + 1 ) / 2 ) / ( Math . sqrt ( df * Math . PI ) * gamma ( df / 2 ) ) ;
90
+ return coefficient * Math . pow ( 1 + ( x * x ) / df , - ( df + 1 ) / 2 ) ;
91
+ }
92
+
93
+ // Calculate confidence intervals
94
+ function calculateCriticalValues ( ) {
95
+ // Normal distribution (z-score for 95% CI)
96
+ const zCritical = 1.96 ;
97
+ const normalCI = {
98
+ lower : state . mean - ( zCritical * state . stdDev ) ,
99
+ upper : state . mean + ( zCritical * state . stdDev )
100
+ } ;
101
+
102
+ // T distribution
103
+ const df = state . sampleSize - 1 ;
104
+ const alpha = 0.025 ; // Two-tailed 95% CI
105
+ const tCritical = Math . abs ( jStat . studentt . inv ( alpha , df ) ) ;
106
+ const tCI = {
107
+ lower : state . mean - ( tCritical * state . stdDev ) ,
108
+ upper : state . mean + ( tCritical * state . stdDev )
109
+ } ;
110
+
111
+ return { normalCI, tCI, tCritical } ;
112
+ }
113
+
114
+ function generateData ( ) {
115
+ const x = [ ] ;
116
+ const normalY = [ ] ;
117
+ const tY = [ ] ;
118
+ const range = state . stdDev * 4 ;
119
+ const step = range / 100 ;
120
+
121
+ for ( let i = state . mean - range ; i <= state . mean + range ; i += step ) {
122
+ x . push ( i ) ;
123
+ normalY . push ( normalPDF ( i , state . mean , state . stdDev ) ) ;
124
+ tY . push ( tPDF ( ( i - state . mean ) / state . stdDev , state . sampleSize - 1 ) ) ;
125
+ }
126
+
127
+ return { x, normalY, tY } ;
128
+ }
129
+
130
+ function updatePlot ( ) {
131
+ const data = generateData ( ) ;
132
+ const criticalValues = calculateCriticalValues ( ) ;
133
+
134
+ const traces = [
135
+ {
136
+ x : data . x ,
137
+ y : data . normalY ,
138
+ name : 'Normal Distribution' ,
139
+ line : { color : '#2563eb' }
140
+ } ,
141
+ {
142
+ x : data . x ,
143
+ y : data . tY ,
144
+ name : 't Distribution' ,
145
+ line : { color : '#dc2626' }
146
+ }
147
+ ] ;
148
+
149
+ const layout = {
150
+ title : {
151
+ text : 'Normal vs t Distribution Comparison' ,
152
+ font : { size : 16 }
153
+ } ,
154
+ width : 800 ,
155
+ height : 400 ,
156
+ showlegend : true ,
157
+ legend : {
158
+ orientation : 'h' ,
159
+ y : - 0.15 ,
160
+ x : 0.5 ,
161
+ xanchor : 'center'
162
+ } ,
163
+ margin : {
164
+ l : 50 ,
165
+ r : 50 ,
166
+ t : 40 ,
167
+ b : 60 // increased bottom margin to accommodate legend
168
+ } ,
169
+ shapes : [
170
+ // Normal CI lines
171
+ {
172
+ type : 'line' ,
173
+ x0 : criticalValues . normalCI . lower ,
174
+ x1 : criticalValues . normalCI . lower ,
175
+ y0 : 0 ,
176
+ y1 : 0.4 ,
177
+ line : {
178
+ color : '#1e40af' ,
179
+ width : 2 ,
180
+ dash : 'dash'
181
+ }
182
+ } ,
183
+ {
184
+ type : 'line' ,
185
+ x0 : criticalValues . normalCI . upper ,
186
+ x1 : criticalValues . normalCI . upper ,
187
+ y0 : 0 ,
188
+ y1 : 0.4 ,
189
+ line : {
190
+ color : '#1e40af' ,
191
+ width : 2 ,
192
+ dash : 'dash'
193
+ }
194
+ } ,
195
+ // T CI lines
196
+ {
197
+ type : 'line' ,
198
+ x0 : criticalValues . tCI . lower ,
199
+ x1 : criticalValues . tCI . lower ,
200
+ y0 : 0 ,
201
+ y1 : 0.4 ,
202
+ line : {
203
+ color : '#dc2626' ,
204
+ width : 2 ,
205
+ dash : 'dash'
206
+ }
207
+ } ,
208
+ {
209
+ type : 'line' ,
210
+ x0 : criticalValues . tCI . upper ,
211
+ x1 : criticalValues . tCI . upper ,
212
+ y0 : 0 ,
213
+ y1 : 0.4 ,
214
+ line : {
215
+ color : '#dc2626' ,
216
+ width : 2 ,
217
+ dash : 'dash'
218
+ }
219
+ }
220
+ ]
221
+ } ;
222
+
223
+ Plotly . newPlot ( 'plot' , traces , layout ) ;
224
+
225
+ // Update stats display
226
+ document . getElementById ( 'stats' ) . innerHTML = `
227
+ <p>Normal Distribution 95% CI: ${ criticalValues . normalCI . lower . toFixed ( 2 ) } to ${ criticalValues . normalCI . upper . toFixed ( 2 ) } </p>
228
+ <p>t Distribution 95% CI: ${ criticalValues . tCI . lower . toFixed ( 2 ) } to ${ criticalValues . tCI . upper . toFixed ( 2 ) } </p>
229
+ ` ;
230
+ }
231
+
232
+ // Setup event listeners
233
+ document . getElementById ( 'mean' ) . addEventListener ( 'input' , ( e ) => {
234
+ state . mean = parseFloat ( e . target . value ) ;
235
+ document . getElementById ( 'mean-value' ) . textContent = state . mean . toFixed ( 2 ) ;
236
+ updatePlot ( ) ;
237
+ } ) ;
238
+
239
+ document . getElementById ( 'std' ) . addEventListener ( 'input' , ( e ) => {
240
+ state . stdDev = parseFloat ( e . target . value ) ;
241
+ document . getElementById ( 'std-value' ) . textContent = state . stdDev . toFixed ( 2 ) ;
242
+ updatePlot ( ) ;
243
+ } ) ;
244
+
245
+ document . getElementById ( 'size' ) . addEventListener ( 'input' , ( e ) => {
246
+ state . sampleSize = parseInt ( e . target . value ) ;
247
+ document . getElementById ( 'size-value' ) . textContent = state . sampleSize ;
248
+ updatePlot ( ) ;
249
+ } ) ;
250
+
251
+ // Initial plot
252
+ updatePlot ( ) ;
253
+ </ script >
254
+ </ body >
255
+
256
+ </ html >
0 commit comments