From 8c1d4d476270fe18e85a07db2199ba8b084bcd44 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 7 May 2025 17:15:39 -0600 Subject: [PATCH] data map update for linear regressions --- .../dataMapper.test.ts | 80 +++++-------------- .../trendlineDatasetCreator.test.ts | 46 +++++------ .../trendlineDatasetCreator.ts | 49 +++++++----- 3 files changed, 72 insertions(+), 103 deletions(-) diff --git a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/dataMapper.test.ts b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/dataMapper.test.ts index 084b5bf0b..0a1ce9752 100644 --- a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/dataMapper.test.ts +++ b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/dataMapper.test.ts @@ -1,20 +1,12 @@ import { describe, it, expect } from '@jest/globals'; import { dataMapper } from './dataMapper'; import { DEFAULT_COLUMN_LABEL_FORMAT } from '@/api/asset_interfaces/metric'; -import type { DatasetOption } from '../interfaces'; import type { IColumnLabelFormat } from '@/api/asset_interfaces/metric/charts'; describe('dataMapper', () => { it('should handle numeric x-axis values correctly', () => { - const dataset: DatasetOption = { - id: 'test', - data: [10, 20, 30], - dataKey: 'xAxis', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const data = [10, 20, 30]; + const xAxisColumn = 'xAxis'; const ticks = { ticks: [['1'], ['2'], ['3']], ticksKey: [{ key: 'xAxis', value: '' }] @@ -28,7 +20,7 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); expect(result).toEqual([ [0, 10], [1, 20], @@ -37,15 +29,8 @@ describe('dataMapper', () => { }); it('should handle date x-axis values correctly', () => { - const dataset: DatasetOption = { - id: 'test', - data: [100, 200, 300], - dataKey: 'date', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const data = [100, 200, 300]; + const xAxisColumn = 'date'; const dates = ['2023-01-01', '2023-01-02', '2023-01-03']; const ticks = { ticks: dates.map((d) => [d]), @@ -60,7 +45,7 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); // Test that we have the right number of data points expect(result.length).toBe(3); @@ -77,15 +62,8 @@ describe('dataMapper', () => { }); it('should handle categorical x-axis values by using indices', () => { - const dataset: DatasetOption = { - id: 'test', - data: [15, 25, 35], - dataKey: 'category', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const data = [15, 25, 35]; + const xAxisColumn = 'category'; const ticks = { ticks: [['A'], ['B'], ['C']], ticksKey: [{ key: 'category', value: '' }] @@ -99,7 +77,7 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); expect(result).toEqual([ [0, 15], [1, 25], @@ -108,15 +86,9 @@ describe('dataMapper', () => { }); it('should handle null/undefined values correctly', () => { - const dataset: DatasetOption = { - id: 'test', - data: [10, null, 30, null, 50] as (number | null)[], - dataKey: 'xAxis', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const rawData = [10, null, 30, null, 50] as (number | null)[]; + const data = rawData.map((val) => val ?? 0); // Convert null to 0 + const xAxisColumn = 'xAxis'; const ticks = { ticks: [['1'], ['2'], ['3'], ['4'], ['5']], ticksKey: [{ key: 'xAxis', value: '' }] @@ -130,7 +102,7 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); expect(result).toEqual([ [0, 10], [1, 0], @@ -141,15 +113,8 @@ describe('dataMapper', () => { }); it('should return empty array for empty dataset', () => { - const dataset: DatasetOption = { - id: 'test', - data: [], - dataKey: 'xAxis', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const data: number[] = []; + const xAxisColumn = 'xAxis'; const ticks = { ticks: [], ticksKey: [{ key: 'xAxis', value: '' }] @@ -163,20 +128,13 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); expect(result).toEqual([]); }); it('should handle missing ticks correctly', () => { - const dataset: DatasetOption = { - id: 'test', - data: [10, 20, 30], - dataKey: 'xAxis', - axisType: 'y', - tooltipData: [], - label: [] - }; - + const data = [10, 20, 30]; + const xAxisColumn = 'xAxis'; const ticks = { ticks: [['1'], [], ['3']], // Missing tick in the middle ticksKey: [{ key: 'xAxis', value: '' }] @@ -190,7 +148,7 @@ describe('dataMapper', () => { } }; - const result = dataMapper(dataset, ticks, columnLabelFormats); + const result = dataMapper(data, xAxisColumn, ticks, columnLabelFormats); expect(result).toEqual([ [0, 10], [1, 30] diff --git a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.test.ts b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.test.ts index f0f02c0ad..0e22bc269 100644 --- a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.test.ts +++ b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.test.ts @@ -496,7 +496,7 @@ describe('trendlineDatasetCreator', () => { id: 'test-column-id', data: [2, 4, 6, 8, 10], // Perfect linear data label: [{ key: 'test-label', value: 'Test Label' }], - dataKey: 'x-axis', + dataKey: 'test-column-id', axisType: 'y', tooltipData: [[{ key: 'test-tooltip', value: 'Test Tooltip' }]] } @@ -547,7 +547,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'linear_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Linear Regression' @@ -587,8 +587,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.linearRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.linearRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); @@ -608,7 +608,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'linear_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Linear Regression' @@ -648,8 +648,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.linearRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.linearRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); @@ -673,14 +673,14 @@ describe('trendlineDatasetCreator', () => { const approximateSlope = (lastPoint - firstPoint) / (data.length - 1); expect(approximateSlope).toBeCloseTo(1, 0); - expect(result[0].columnId).toEqual('test-column-id'); + expect(result[0].columnId).toEqual('x-axis'); }); it('should calculate linear regression with date-based ticks', () => { // Arrange const trendline: Trendline = { type: 'linear_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Linear Regression' @@ -727,8 +727,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.linearRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.linearRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); @@ -748,7 +748,7 @@ describe('trendlineDatasetCreator', () => { expect(data[0] as number).toBeCloseTo(102, 0); expect(data[data.length - 1] as number).toBeCloseTo(155, 0); - expect(result[0].columnId).toEqual('test-column-id'); + expect(result[0].columnId).toEqual('x-axis'); }); }); @@ -757,7 +757,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'exponential_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Exponential Regression' @@ -797,8 +797,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.exponentialRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.exponentialRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); @@ -815,7 +815,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'exponential_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Exponential Regression' @@ -858,8 +858,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.exponentialRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.exponentialRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); @@ -872,7 +872,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'exponential_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Exponential Regression' @@ -925,7 +925,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'exponential_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Exponential Regression' @@ -972,7 +972,7 @@ describe('trendlineDatasetCreator', () => { // Arrange const trendline: Trendline = { type: 'logarithmic_regression', - columnId: 'test-column-id', + columnId: 'x-axis', show: true, showTrendlineLabel: true, trendlineLabel: 'Logarithmic Regression' @@ -1013,8 +1013,8 @@ describe('trendlineDatasetCreator', () => { // Assert expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ - id: DATASET_IDS.logarithmicRegression('test-column-id'), - dataKey: 'test-column-id', + id: DATASET_IDS.logarithmicRegression('x-axis'), + dataKey: 'x-axis', axisType: 'y' }); diff --git a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.ts b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.ts index 37d82b3f9..ad521f99e 100644 --- a/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.ts +++ b/web/src/components/ui/charts/chartHooks/useDatasetOptions/useDataTrendlineOptions/trendlineDatasetCreator.ts @@ -22,7 +22,8 @@ export const trendlineDatasetCreator: Record< const datasets = datasetsWithTicks.datasets; const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -86,7 +87,8 @@ export const trendlineDatasetCreator: Record< const datasets = datasetsWithTicks.datasets; const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -153,7 +155,8 @@ export const trendlineDatasetCreator: Record< const datasets = datasetsWithTicks.datasets; const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -221,7 +224,8 @@ export const trendlineDatasetCreator: Record< const datasets = datasetsWithTicks.datasets; const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -282,9 +286,10 @@ export const trendlineDatasetCreator: Record< }, average: (trendline, datasetsWithTicks) => { - const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( + const { validData, selectedDatasets } = getValidDataAndTicks( datasetsWithTicks.datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -313,9 +318,10 @@ export const trendlineDatasetCreator: Record< }, min: (trendline, datasetsWithTicks) => { - const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( + const { validData, selectedDatasets } = getValidDataAndTicks( datasetsWithTicks.datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -339,9 +345,10 @@ export const trendlineDatasetCreator: Record< }, max: (trendline, datasetsWithTicks) => { - const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( + const { validData, selectedDatasets } = getValidDataAndTicks( datasetsWithTicks.datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -365,9 +372,10 @@ export const trendlineDatasetCreator: Record< }, median: (trendline, datasetsWithTicks) => { - const { validData, ticks, xAxisColumn, selectedDatasets } = getValidDataAndTicks( + const { validData, selectedDatasets } = getValidDataAndTicks( datasetsWithTicks.datasets, - trendline + trendline, + datasetsWithTicks.ticks ); if (!selectedDatasets || selectedDatasets.length === 0 || validData.length === 0) return []; @@ -404,10 +412,12 @@ export const trendlineDatasetCreator: Record< const getValidDataAndTicks = ( datasets: DatasetOptionsWithTicks['datasets'], - trendline: Trendline + trendline: Trendline, + datasetTicks: DatasetOptionsWithTicks['ticks'] ) => { - const selectedDatasets = datasets.filter((dataset) => dataset.dataKey === trendline.columnId); - const xAxisColumn = selectedDatasets[0].dataKey; + const selectedDatasets = + datasets?.filter((dataset) => dataset.dataKey === trendline.columnId) || []; + const xAxisColumn = selectedDatasets[0]?.dataKey; // If there's only one dataset, we can skip sorting if (selectedDatasets.length === 1) { @@ -417,11 +427,12 @@ const getValidDataAndTicks = ( dataset.data.forEach((value, index) => { const isValidData = value !== null && value !== undefined; - const associatedTick = dataset.ticksForScatter?.[index]; - if (isValidData && associatedTick !== undefined) { + const associatedTick = dataset.ticksForScatter?.[index] || datasetTicks?.[index]; + + if (isValidData) { validData.push(value as number); - ticks.push(associatedTick); + if (associatedTick !== undefined) ticks.push(associatedTick); } }); @@ -432,7 +443,7 @@ const getValidDataAndTicks = ( const pairs = selectedDatasets.reduce>((acc, dataset) => { dataset.data.forEach((value, index) => { const isValidData = value !== null && value !== undefined; - const associatedTick = dataset.ticksForScatter?.[index]; + const associatedTick = dataset.ticksForScatter?.[index] || datasetTicks?.[index]; if (isValidData && associatedTick !== undefined) { acc.push([associatedTick, value as number]);