lerobot/robot-learning-tutorial
508
1// Data transformation utilities for ChartRenderer2 3/**4 * Chart data transformations and calculations5 */6export class ChartTransforms {7 8 /**9 * Process metric data and calculate domains10 */11 static processMetricData(metricData, metricKey, normalizeLoss) {12 const runs = Object.keys(metricData || {});13 const hasAny = runs.some(r => (metricData[r] || []).length > 0);14 15 if (!hasAny) {16 return { 17 runs: [], 18 hasData: false, 19 minStep: 0, 20 maxStep: 0, 21 minVal: 0, 22 maxVal: 1,23 yDomain: [0, 1],24 stepSet: new Set(),25 hoverSteps: []26 };27 }28 29 // Calculate data bounds30 let minStep = Infinity, maxStep = -Infinity, minVal = Infinity, maxVal = -Infinity;31 runs.forEach(r => { 32 (metricData[r] || []).forEach(pt => { 33 minStep = Math.min(minStep, pt.step); 34 maxStep = Math.max(maxStep, pt.step); 35 minVal = Math.min(minVal, pt.value); 36 maxVal = Math.max(maxVal, pt.value); 37 }); 38 });39 40 // Determine Y domain based on metric type41 const isAccuracy = /accuracy/i.test(metricKey); 42 const isLoss = /loss/i.test(metricKey);43 let yDomain;44 45 if (isAccuracy) {46 yDomain = [0, 1];47 } else if (isLoss && normalizeLoss) {48 yDomain = [0, 1];49 } else {50 yDomain = [minVal, maxVal];51 }52 53 // Collect all steps for hover interactions54 const stepSet = new Set(); 55 runs.forEach(r => (metricData[r] || []).forEach(v => stepSet.add(v.step)));56 const hoverSteps = Array.from(stepSet).sort((a, b) => a - b); 57 58 return {59 runs,60 hasData: true,61 minStep,62 maxStep,63 minVal,64 maxVal,65 yDomain,66 stepSet,67 hoverSteps,68 isAccuracy,69 isLoss70 };71 }72 73 /**74 * Setup scales based on data and scale type75 */76 static setupScales(svgManager, processedData, logScaleX) {77 const { hoverSteps, yDomain } = processedData;78 const { x: xScale, y: yScale, line: lineGen } = svgManager.getScales();79 80 // Update scales81 yScale.domain(yDomain).nice();82 83 let stepIndex = null;84 85 if (logScaleX) {86 const minStep = Math.max(1, Math.min(...hoverSteps));87 const maxStep = Math.max(...hoverSteps);88 xScale.domain([minStep, maxStep]);89 lineGen.x(d => xScale(d.step));90 } else {91 stepIndex = new Map(hoverSteps.map((s, i) => [s, i]));92 xScale.domain([0, Math.max(0, hoverSteps.length - 1)]);93 lineGen.x(d => xScale(stepIndex.get(d.step)));94 }95 96 return { stepIndex };97 }98 99 /**100 * Create normalization function for Y values101 */102 static createNormalizeFunction(processedData, normalizeLoss) {103 const { isLoss, minVal, maxVal } = processedData;104 105 return (v) => {106 if (isLoss && normalizeLoss) {107 return ((maxVal > minVal) ? (v - minVal) / (maxVal - minVal) : 0);108 }109 return v;110 };111 }112 113 /**114 * Validate and clean data values115 */116 static validateData(metricData) {117 const cleanedData = {};118 119 Object.keys(metricData || {}).forEach(run => {120 const values = metricData[run] || [];121 cleanedData[run] = values.filter(pt => 122 pt && 123 typeof pt.step === 'number' && 124 typeof pt.value === 'number' &&125 Number.isFinite(pt.step) && 126 Number.isFinite(pt.value)127 );128 });129 130 return cleanedData;131 }132 133 /**134 * Calculate chart dimensions based on content135 */136 static calculateOptimalDimensions(dataCount, containerWidth) {137 // Suggest optimal dimensions based on data density138 const minHeight = 120;139 const maxHeight = 300;140 const baseHeight = 150;141 142 // More data points = slightly taller chart for better readability143 const heightMultiplier = Math.min(1.5, 1 + (dataCount / 1000) * 0.5);144 const suggestedHeight = Math.min(maxHeight, Math.max(minHeight, baseHeight * heightMultiplier));145 146 return {147 width: containerWidth || 800,148 height: suggestedHeight149 };150 }151 152 /**153 * Prepare hover step data for interactions154 */155 static prepareHoverSteps(processedData, logScaleX) {156 const { hoverSteps } = processedData;157 158 if (!hoverSteps.length) return { hoverSteps: [], stepIndex: null };159 160 let stepIndex = null;161 162 if (!logScaleX) {163 stepIndex = new Map(hoverSteps.map((s, i) => [s, i]));164 }165 166 return { hoverSteps, stepIndex };167 }168}169 