Apache echarts plugin - multiple boxplot over time groups

Hello Community,

i have currently a small issue that i can not solve by my own…
I am using the Billiballa/bilibala-echarts-panel: Echarts panel for grafana (github.com) in Grafana and i need to display multiple boxplots over multiple timegroups.

Currently the boxplots are all shown in the last time group. That should not be the case any more and every boyplot shall be shown in the dedicated time group

Can anybody support or does anybody have experience with that panel plugin?

const xAxisData = [];

const boxPlotSeriesByTimeGroup = {};

data.series.forEach((s) => {
    const sData = Array.from(s.fields.find((f) => f.name === 'value_double').values.buffer);
    const sTime = Array.from(s.fields.find((f) => f.name === 'systime').values.buffer);

    // Group the timestamps by 30 minutes
    const groupedTime = sTime.reduce((acc, t) => {
        const date = new Date(t);
        const roundedMinutes = Math.round(date.getMinutes() / 30) * 30;
        date.setMinutes(roundedMinutes);
        const timeGroup = `${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
        if (!acc[timeGroup]) {
            acc[timeGroup] = [];
        }
        acc[timeGroup].push(t);
        return acc;
    }, {});

    // Loop over the time groups and calculate box plot statistics
    Object.entries(groupedTime).forEach(([timeGroup, timestamps]) => {
        // Convert timestamps to x-axis data
        xAxisData.push(timeGroup.split(' ').join('\n'));

        // Calculate box plot statistics
        const timeGroupData = sData.filter((d, i) => timestamps.includes(sTime[i]));
        timeGroupData.sort((a, b) => a - b);
        const q1 = timeGroupData[Math.floor(timeGroupData.length * 0.25)];
        const q2 = timeGroupData[Math.floor(timeGroupData.length * 0.5)];
        const q3 = timeGroupData[Math.floor(timeGroupData.length * 0.75)];
        const iqr = q3 - q1;
        const min = timeGroupData[0];
        const max = timeGroupData[timeGroupData.length - 1];
        const whiskerMin = timeGroupData.find((d) => d >= q1 - 1.5 * iqr);
        const whiskerMax = timeGroupData.find((d) => d <= q3 + 1.5 * iqr);
        const boxData = [min, q1, q2, q3, max];
        const whiskerData = [whiskerMin, whiskerMax];

        // Add box plot series for the time group
        if (!boxPlotSeriesByTimeGroup[timeGroup]) {
            boxPlotSeriesByTimeGroup[timeGroup] = [];
        }
        boxPlotSeriesByTimeGroup[timeGroup].push({
            data: [boxData, whiskerData],
            itemStyle: {
                color: 'yellow',
            },
            type: 'boxplot',
            tooltip: {
                formatter: (params) => {
                    if (params.componentType === 'series') {
                        const {
                            value
                        } = params;
                        if (value instanceof Array) {
                            return `Min: ${value[0]}<br>Q1: ${value[1]}<br>Median: ${value[2]}<br>Q3: ${value[3]}<br>Max: ${value[4]}`;
                        } else {
                            return `Value: ${value}`;
                        }
                    }
                },
            },
        });
    });
});

xAxisData.sort();

const axisOption = {
    axisTick: {
        show: true,
    },
    axisLine: {
        show: true,
    },
    axisLabel: {
        color: 'rgba(128, 128, 128, .9)',
        formatter: (value) => value.toString().split('\n').join(' '),
    },
    splitLine: {
        lineStyle: {
            color: 'rgba(128, 128, 128, .2)',
        },
    },
};

const series = Object.entries(boxPlotSeriesByTimeGroup).flatMap(([timeGroup, boxPlotSeries]) => {
    return boxPlotSeries.map((seriesData) => {
        return {
            data: seriesData.data,
            type: 'boxplot',
            itemStyle: {
                color: 'yellow',
            },
            tooltip: {
                formatter: (params) => {
                    if (params.componentType === 'series') {
                        const {
                            value
                        } = params;
                        if (value instanceof Array) {
                            return `Min: ${value[0]}<br>Q1: ${value[1]}<br>Median: ${value[2]}<br>Q3: ${value[3]}<br>Max: ${value[4]}`;
                        } else {
                            return `Value: ${value}`;
                        }
                    }
                },
            },
        };
    });
});

return {
    backgroundColor: 'transparent',
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'shadow',
        },
    },
    legend: {
        left: '0',
        bottom: '0',
        textStyle: {
            color: 'rgba(128, 128, 128, .9)',
        },
    },
    xAxis: Object.assign({
            type: 'category',
            data: xAxisData,
            max: xAxisData.length - 1,
            inverse: false,
        },
        axisOption
    ),
    yAxis: Object.assign({
            type: 'value',
            min: 'dataMin',
            axisLabel: {
                formatter: '{value}',
            },
        },
        axisOption
    ),
    grid: {
        left: 0,
        right: 16,
        top: 6,
        bottom: 24,
        containLabel: true,
    },
    series,
};

@jakobgabriel Is there a specific reason you are using an outdated ECharts panel from bilibala?

I suggest trying with a newer version, which we forked, to see if it’s resolved.

Basically i was using that one for multiple dashboards, thats why i was not aware about the new forked plugin.

Anyway, do you know if we can use the above mentioned script in the new plugin?

1 Like

@jakobgabriel We did it fully compatible for migration. Let me know if not.

Check your script using the latest version of the plugin to see if it may be resolved in the latest version of Apache ECharts.