thinkwee commited on
Commit
e77058f
·
1 Parent(s): b4db294

fix display

Browse files
Files changed (1) hide show
  1. charts.js +137 -105
charts.js CHANGED
@@ -81,6 +81,68 @@ document.querySelectorAll('.nav-tab').forEach(tab => {
81
  // SCALING ANALYSIS - 3 Charts with animated dimension switching
82
  // ============================================================================
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  // Exact axis ranges from Python scripts
85
  const SCALING_Y_RANGES = {
86
  'mimic': [5, 40], // Python: y_min=5, y_max=40
@@ -95,48 +157,52 @@ function initScalingCharts() {
95
  const data = DDR_DATA.scaling[scenario];
96
  if (!data) return;
97
 
98
- const traces = [];
99
  const models = Object.keys(data);
 
100
 
 
 
 
 
 
 
 
101
  models.forEach(model => {
102
- const modelData = data[model];
 
 
103
 
104
  traces.push({
105
- x: modelData.turns,
106
- y: modelData.accuracy,
107
  mode: 'lines+markers',
108
  name: model,
109
- line: {
110
- color: DDR_DATA.modelColors[model] || '#888',
111
- width: 2
112
- },
113
- marker: {
114
- size: 6,
115
- color: DDR_DATA.modelColors[model] || '#888'
116
- },
117
- hovertemplate: `<b>${model}</b><br>Turn: %{x}<br>Accuracy: %{y:.2f}%<extra></extra>`
118
  });
119
  });
120
 
121
- // Use exact Y-axis range from Python
122
  const yRange = SCALING_Y_RANGES[scenario] || [0, 100];
123
- const maxTurn = Math.max(...models.flatMap(m => data[m].turns));
124
 
125
  const layout = {
126
  ...darkLayout,
127
  xaxis: {
128
  ...darkLayout.xaxis,
129
  title: { text: 'Number of Interaction Turns', font: { size: 11, color: '#e2e8f0' } },
130
- dtick: 10, // 10 turn intervals (matching Python's auto)
131
- range: [0, maxTurn + 5],
132
- tick0: 0
 
 
 
133
  },
134
  yaxis: {
135
  ...darkLayout.yaxis,
136
  title: { text: 'Accuracy (%)', font: { size: 11, color: '#e2e8f0' } },
137
- dtick: 5, // 5% intervals
138
- range: yRange,
139
- tick0: yRange[0]
140
  },
141
  showlegend: true
142
  };
@@ -145,7 +211,6 @@ function initScalingCharts() {
145
  });
146
  }
147
 
148
-
149
  function updateScalingCharts(dimension) {
150
  const scenarios = ['mimic', '10k', 'globem'];
151
  const xLabels = {
@@ -159,101 +224,68 @@ function updateScalingCharts(dimension) {
159
  if (!data) return;
160
 
161
  const models = Object.keys(data);
162
- const yRange = SCALING_Y_RANGES[scenario] || [0, 100];
163
 
164
- // Prepare new data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  const newTraces = [];
166
- const allXValues = [];
167
 
168
  const hoverLabels = { 'turn': 'Turns', 'token': 'Tokens', 'cost': 'Cost' };
169
- const hoverFormat = dimension === 'token' ? '%{x:,.0f}' : (dimension === 'cost' ? '$%{x:.4f}' : '%{x}');
170
 
171
- models.forEach(model => {
172
- const modelData = data[model];
173
- let xValues;
 
 
 
174
  switch (dimension) {
175
- case 'turn': xValues = modelData.turns; break;
176
- case 'token': xValues = modelData.tokens; break;
177
- case 'cost': xValues = modelData.costs; break;
178
  }
179
- allXValues.push(...xValues);
180
 
181
  newTraces.push({
182
- x: xValues,
183
- y: modelData.accuracy,
184
- mode: 'lines+markers',
185
- name: model,
186
- line: { color: DDR_DATA.modelColors[model] || '#888', width: 2 },
187
- marker: { size: 6, color: DDR_DATA.modelColors[model] || '#888' },
188
- hovertemplate: `<b>${model}</b><br>${hoverLabels[dimension]}: ${hoverFormat}<br>Accuracy: %{y:.2f}%<extra></extra>`
189
  });
190
  });
191
 
192
- const maxX = Math.max(...allXValues);
193
- const minX = Math.min(...allXValues.filter(v => v > 0));
194
-
195
- // Determine final axis settings
196
- let finalXaxis = {
197
- 'xaxis.title.text': xLabels[dimension]
198
- };
199
-
200
- if (dimension === 'turn') {
201
- finalXaxis['xaxis.type'] = 'linear';
202
- finalXaxis['xaxis.dtick'] = 10;
203
- finalXaxis['xaxis.range'] = [0, maxX + 5];
204
- finalXaxis['xaxis.tickformat'] = null;
205
- } else if (dimension === 'token') {
206
- finalXaxis['xaxis.type'] = 'linear';
207
- finalXaxis['xaxis.dtick'] = 10000;
208
- finalXaxis['xaxis.range'] = [0, maxX * 1.05];
209
- finalXaxis['xaxis.tickformat'] = ',.0f';
210
- } else { // cost
211
- finalXaxis['xaxis.type'] = 'log';
212
- finalXaxis['xaxis.dtick'] = null;
213
- finalXaxis['xaxis.range'] = [Math.log10(minX * 0.5), Math.log10(maxX * 1.5)];
214
- finalXaxis['xaxis.tickformat'] = '$.3f';
215
- finalXaxis['xaxis.exponentformat'] = 'none';
216
- }
217
-
218
- // STEP 1: Unset dtick/tickformat to prevent freezing during animation
219
- // When switching range 100 -> 30000, if dtick stays 10, it tries to draw 3000 ticks!
220
- Plotly.relayout(`scaling-${scenario}`, {
221
- 'xaxis.dtick': null,
222
- 'xaxis.tickformat': null
223
- }).then(() => {
224
- // STEP 2: Animate data and range together
225
- // This creates the smooth "morphing" effect as axis scales with data
226
- Plotly.animate(`scaling-${scenario}`, {
227
- data: newTraces,
228
- layout: {
229
- xaxis: {
230
- ...darkLayout.xaxis,
231
- title: { text: xLabels[dimension], font: { size: 11, color: '#e2e8f0' } },
232
- type: finalXaxis['xaxis.type'],
233
- range: finalXaxis['xaxis.range'],
234
- dtick: null, // Keep auto-ticks during animation
235
- tickformat: null
236
- },
237
- yaxis: {
238
- ...darkLayout.yaxis,
239
- title: { text: 'Accuracy (%)', font: { size: 11, color: '#e2e8f0' } },
240
- dtick: 5,
241
- range: yRange
242
- }
243
- }
244
- }, {
245
- transition: {
246
- duration: 500,
247
- easing: 'cubic-in-out'
248
- },
249
- frame: {
250
- duration: 500,
251
- redraw: true
252
- }
253
- }).then(() => {
254
- // STEP 3: Apply precise ticks after animation finishes
255
- Plotly.relayout(`scaling-${scenario}`, finalXaxis);
256
- });
257
  });
258
  });
259
  }
 
81
  // SCALING ANALYSIS - 3 Charts with animated dimension switching
82
  // ============================================================================
83
 
84
+
85
+ // ============================================================================
86
+ // SCALING ANALYSIS - Normalized Coordinate System for Smooth Animation
87
+ // ============================================================================
88
+
89
+ // Helper to normalize values to [0, 1]
90
+ function normalizeData(values, type) {
91
+ if (values.length === 0) return { normalized: [], min: 0, max: 1 };
92
+
93
+ let min, max;
94
+ let normalized;
95
+
96
+ if (type === 'log') {
97
+ // Filter positive values for log
98
+ const positiveValues = values.filter(v => v > 0);
99
+ min = Math.min(...positiveValues);
100
+ max = Math.max(...positiveValues);
101
+ const logMin = Math.log10(min);
102
+ const logMax = Math.log10(max);
103
+ const range = logMax - logMin || 1;
104
+
105
+ normalized = values.map(v => v > 0 ? (Math.log10(v) - logMin) / range : 0);
106
+ } else {
107
+ min = 0; // Always start linear scales at 0 for this use case
108
+ max = Math.max(...values);
109
+ const range = max - min || 1;
110
+
111
+ normalized = values.map(v => (v - min) / range);
112
+ }
113
+
114
+ return { normalized, min, max };
115
+ }
116
+
117
+ // Helper to generate pretty ticks for normalized scale [0, 1]
118
+ function generateTicks(min, max, type) {
119
+ const tickVals = [0, 0.2, 0.4, 0.6, 0.8, 1.0];
120
+ let tickText;
121
+
122
+ if (type === 'log') {
123
+ const logMin = Math.log10(min);
124
+ const logMax = Math.log10(max);
125
+ const range = logMax - logMin;
126
+
127
+ tickText = tickVals.map(v => {
128
+ const val = Math.pow(10, logMin + (v * range));
129
+ if (val >= 1) return val.toFixed(1);
130
+ return val.toFixed(3); // More precision for small costs
131
+ });
132
+ // Format as currency
133
+ tickText = tickText.map(t => '$' + t);
134
+ } else {
135
+ const range = max - min;
136
+ tickText = tickVals.map(v => {
137
+ const val = min + (v * range);
138
+ if (val >= 1000) return (val / 1000).toFixed(0) + 'k';
139
+ return val.toFixed(0);
140
+ });
141
+ }
142
+
143
+ return { tickVals, tickText };
144
+ }
145
+
146
  // Exact axis ranges from Python scripts
147
  const SCALING_Y_RANGES = {
148
  'mimic': [5, 40], // Python: y_min=5, y_max=40
 
157
  const data = DDR_DATA.scaling[scenario];
158
  if (!data) return;
159
 
 
160
  const models = Object.keys(data);
161
+ const traces = [];
162
 
163
+ // Initial dimension is 'turn'
164
+ const allTurns = models.flatMap(m => data[m].turns);
165
+ const { normalized: normTurns, min: minTurn, max: maxTurn } = normalizeData(allTurns, 'linear');
166
+ const { tickVals, tickText } = generateTicks(minTurn, maxTurn, 'linear');
167
+
168
+ // We need to slice the normalized array back to per-model arrays
169
+ let offset = 0;
170
  models.forEach(model => {
171
+ const len = data[model].turns.length;
172
+ const modelNormX = normTurns.slice(offset, offset + len);
173
+ offset += len;
174
 
175
  traces.push({
176
+ x: modelNormX,
177
+ y: data[model].accuracy,
178
  mode: 'lines+markers',
179
  name: model,
180
+ line: { color: DDR_DATA.modelColors[model] || '#888', width: 2 },
181
+ marker: { size: 6, color: DDR_DATA.modelColors[model] || '#888' },
182
+ hovertemplate: `<b>${model}</b><br>Turn: %{customdata}<br>Accuracy: %{y:.2f}%<extra></extra>`,
183
+ customdata: data[model].turns // Store real values for hover
 
 
 
 
 
184
  });
185
  });
186
 
 
187
  const yRange = SCALING_Y_RANGES[scenario] || [0, 100];
 
188
 
189
  const layout = {
190
  ...darkLayout,
191
  xaxis: {
192
  ...darkLayout.xaxis,
193
  title: { text: 'Number of Interaction Turns', font: { size: 11, color: '#e2e8f0' } },
194
+ type: 'linear', // ALWAYS LINEAR
195
+ range: [-0.05, 1.05], // FIXED RANGE
196
+ tickmode: 'array',
197
+ tickvals: tickVals,
198
+ ticktext: tickText,
199
+ zeroline: false
200
  },
201
  yaxis: {
202
  ...darkLayout.yaxis,
203
  title: { text: 'Accuracy (%)', font: { size: 11, color: '#e2e8f0' } },
204
+ dtick: 5,
205
+ range: yRange
 
206
  },
207
  showlegend: true
208
  };
 
211
  });
212
  }
213
 
 
214
  function updateScalingCharts(dimension) {
215
  const scenarios = ['mimic', '10k', 'globem'];
216
  const xLabels = {
 
224
  if (!data) return;
225
 
226
  const models = Object.keys(data);
 
227
 
228
+ // 1. Collect all raw X values for normalization
229
+ const allRawX = [];
230
+ models.forEach(model => {
231
+ switch (dimension) {
232
+ case 'turn': allRawX.push(...data[model].turns); break;
233
+ case 'token': allRawX.push(...data[model].tokens); break;
234
+ case 'cost': allRawX.push(...data[model].costs); break;
235
+ }
236
+ });
237
+
238
+ // 2. Normalize data
239
+ const type = dimension === 'cost' ? 'log' : 'linear';
240
+ const { normalized: allNormX, min: minX, max: maxX } = normalizeData(allRawX, type);
241
+ const { tickVals, tickText } = generateTicks(minX, maxX, type);
242
+
243
+ // 3. Prepare update data
244
  const newTraces = [];
245
+ let offset = 0;
246
 
247
  const hoverLabels = { 'turn': 'Turns', 'token': 'Tokens', 'cost': 'Cost' };
248
+ const hoverFormat = dimension === 'token' ? (v) => v.toLocaleString() : (dimension === 'cost' ? (v) => '$' + v.toFixed(4) : (v) => v);
249
 
250
+ models.forEach((model, i) => {
251
+ const len = data[model].turns.length; // Assuming all arrays same length
252
+ const modelNormX = allNormX.slice(offset, offset + len);
253
+
254
+ // Get raw values for customdata (hover)
255
+ let rawValues;
256
  switch (dimension) {
257
+ case 'turn': rawValues = data[model].turns; break;
258
+ case 'token': rawValues = data[model].tokens; break;
259
+ case 'cost': rawValues = data[model].costs; break;
260
  }
261
+ offset += len;
262
 
263
  newTraces.push({
264
+ x: modelNormX,
265
+ y: data[model].accuracy,
266
+ customdata: rawValues,
267
+ hovertemplate: `<b>${model}</b><br>${hoverLabels[dimension]}: %{customdata}<br>Accuracy: %{y:.2f}%<extra></extra>`
 
 
 
268
  });
269
  });
270
 
271
+ // 4. Animate!
272
+ // Since axis range is fixed [-0.05, 1.05], we only animate points and update tick labels
273
+ Plotly.animate(`scaling-${scenario}`, {
274
+ data: newTraces,
275
+ layout: {
276
+ 'xaxis.title.text': xLabels[dimension],
277
+ 'xaxis.tickvals': tickVals,
278
+ 'xaxis.ticktext': tickText
279
+ }
280
+ }, {
281
+ transition: {
282
+ duration: 800,
283
+ easing: 'cubic-in-out'
284
+ },
285
+ frame: {
286
+ duration: 800,
287
+ redraw: true
288
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  });
290
  });
291
  }