CoolFace
Apppublic

lerobot/robot-learning-tutorial

sourceHugging Faceupdated 1y agoView on Hugging Face
508likes
chart-transforms.js169 linesDownload Raw Back to utils
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