javascriptchartsechartsskewweighted-graph

How can I skew certain y-axis values on a line chart?


I'm building a line chart in ECharts, but I want to skew/weight a specific range of the y-axis to be larger than it would be linearly. For example, I would like values 0-70 to take up 1/3 of the chart size and the remaining 70-100 to take up 2/3. I don't have much experience with charting, so I'm a little lost on the best approach.

An example of a chart that doesn't have any skewing with visual indicators of what should be skewed

Here are the ECharts options I'm using to create the line chart, also available in this Codesandbox:

import { format } from "date-fns";
import { createFakeValues } from "../utils";

const dataValues = createFakeValues({
  yValues: [29, 32, 35, 40, 47, 49, 50, 49, 48, 45, 43, 39, 35, 30, 27, 25, 24],
  startDate: new Date("2024-12-01T18:27:08.199Z"),
  dateDeltaMs: 1800000,
});

const eChartsDataValues = dataValues.map((dv) => [dv.date, dv.value]);

export const eChartsOptions = {
  dataset: [
    {
      source: eChartsDataValues,
      dimensions: ["timestamp", "value"],
    },
  ],
  xAxis: {
    type: "time",
  },
  yAxis: {
    min: 0,
    max: 50,
  },
  series: [
    {
      name: "Y Level",
      type: "line",
      smooth: true,
      datasetIndex: 0,
      showSymbol: false,
      encode: {
        x: "timestamp",
        y: "value",
      },
      markArea: {
        silent: true,
        emphasis: {
          disabled: true,
        },
        label: {
          fontSize: 12,
          textBorderColor: "transparent",
          position: "insideBottomLeft",
        },
        data: [
          [
            {
              name: "This is mathematically a shorter range (40-50), but it should take up the majority of space on the graph",
              yAxis: 40,
              itemStyle: {
                color: "red",
              },
            },
            {
              yAxis: 50,
            },
          ],
          [
            {
              name: "This is mathematically a bigger range (0-40) but should take up a smaller section of the graph",
              yAxis: 0,
              itemStyle: {
                color: "green",
              },
            },
            {
              yAxis: 40,
            },
          ],
        ],
      },
    },
  ],
};


Solution

  • As far as I know, there is no out of the box feature for that. The only solution that comes to my mind is skewing the data before passing it to the series and unskewing it whenever the actual values are shown (axisLabel, tooltip, ...).

    Example:

    const data = [10,20,30,40,50,60,70,80,90,100];
    
    function skew(dataPoint) {
      if (dataPoint >= 0 && dataPoint <= 70) {
        return dataPoint * (100/70);
      } else if (dataPoint > 70 && dataPoint <= 100) {
        return 100 + (dataPoint - 70) * (200 / 30);
      } else {
        return 'NA';
      }
    }
    
    function unskew(dataPoint) {
      if (dataPoint >= 0 && dataPoint <= 100) {
        return dataPoint * (70/100);
      } else if (dataPoint > 100 && dataPoint <= 300) {
        return 70 + (dataPoint - 100) * (30/200);
      } else {
        return 'NA';
      }
    }
    
    option = {
      xAxis: {
        type: 'category',
      },
      yAxis: {
        type: 'value',
        axisLabel: {
          formatter: (value) => unskew(value)
        }
      },
      series: [
        {
          type: 'line',
          data: data.map(dataPoint => skew(dataPoint))
        },
      ]
    };
    

    You could also add customValues for axisLabel and axisTick. See this example.