React Chart - Data Aggregation

If the Chart contains many series points, displaying all of them may slow down its performance. In this case, it is better to aggregate the series points or replace a group of them with a single point. This aggregated series point conveys a summary of several points, which also facilitates analysis of large datasets. This article discusses steps to configure data aggregation in the Chart.

Multiple Series Demo Financial Series Demo

Enable Data Aggregation

You can enable data aggregation for individual series, all series of a specific type, or for all series in the Chart. The following code demonstrates all the three cases:

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        // ...
        commonSeriesSettings: {
            // Enables data aggregation for all series in the Chart
            aggregation: {
                enabled: true
            },
            fullstackedbar: {
                // Enables data aggregation for all Full-Stacked Bar series
                aggregation: {
                    enabled: true
                }
            }
        },
        series: [{
            // Enables data aggregation for an individual series
            aggregation: {
                enabled: true
            }
        }, {
            // ...
        }]
    });
});
Angular
HTML
TypeScript
<dx-chart ... >
    <dxo-common-series-settings>
        <!-- Enables data aggregation for all series in the Chart -->
        <dxo-aggregation
            [enabled]="true">
        </dxo-aggregation>

        <dxo-fullstackedbar>
            <!-- Enables data aggregation for all Full-Stacked Bar series -->
            <dxo-aggregation
                [enabled]="true">
            </dxo-aggregation>
        </dxo-fullstackedbar>

    </dxo-common-series-settings>

    <dxi-series>
        <!-- Enables data aggregation for an individual series -->
        <dxo-aggregation
            [enabled]="true">
        </dxo-aggregation>
    </dxi-series>
</dx-chart>
import { DxChartModule } from "devextreme-angular";
// ...
export class AppComponent {
    // ...
}
@NgModule({
    imports: [
        // ...
        DxChartModule
    ],
    // ...
})
Vue
App.vue
<template> 
    <DxChart ... >
        <DxCommonSeriesSettings ...
            :fullstackedbar="fullStackedBarSettings"> <!-- Enables data aggregation for all Full-Stacked Bar series -->
            <DxAggregation :enabled="true"/> <!-- Enables data aggregation for all series in the Chart -->
        </DxCommonSeriesSettings>
        <DxSeries>
            <DxAggregation :enabled="true"/> <!-- Enables data aggregation for an individual series -->
        </DxSeries>
    </DxChart>
</template>

<script>
import DxChart, {
    DxCommonSeriesSettings,
    DxSeries,
    DxAggregation
} from 'devextreme-vue/chart';

export default {
    components: {
        DxChart,
        DxCommonSeriesSettings,
        DxSeries,
        DxAggregation
    },
    data() {
        return {
            fullStackedBarSettings: { aggregation: { enabled: true } }
        };
    }
}
</script>
React
App.js
import React from 'react';
import Chart, {
    CommonSeriesSettings,
    Series,
    Aggregation
} from 'devextreme-react/chart';

const fullStackedBarSettings = { aggregation: { enabled: true } };

class App extends React.Component {
    render() {
        return (
            <Chart ... >
                <CommonSeriesSettings ...
                    fullstackedbar={fullStackedBarSettings}> {/* Enables data aggregation for all Full-Stacked Bar series */}
                    <Aggregation enabled={true} /> {/* Enables data aggregation for all series in the Chart */}
                </CommonSeriesSettings>
                <Series>
                    <Aggregation enabled={true} /> {/* Enables data aggregation for an individual series */}
                </Series>
            </Chart>
        );
    }
}

export default App;

Specify the Aggregation Interval

Series points are grouped for aggregation using intervals: those points that fall within the same interval on the argument axis get aggregated together. You can specify the length of the intervals in axis units (numbers or dates), in pixels, or aggregate points by categories:

  • Axis units (for continuous and logarithmic axes only)
    Use the argumentAxis.aggregationInterval property.

    jQuery
    JavaScript
    $(function() {
        $("#chartContainer").dxChart({
            // ...
            argumentAxis: {
                // A new interval every 100 units
                aggregationInterval: 100,
                // A new interval every day
                aggregationInterval: "day",
                // A new interval every five days
                aggregationInterval: { days: 5 }
            }
        });
    });
    Angular
    HTML
    TypeScript
    <dx-chart ... >
        <dxo-argument-axis
            [aggregationInterval]="100"  <!-- A new interval every 100 units -->
            aggregationInterval="day"> <!-- A new interval every day -->
            <dxo-aggregation-interval
                [days]="5">            <!-- A new interval every five days -->
            </dxo-aggregation-interval>
        </dxo-argument-axis>
    </dx-chart>
    import { DxChartModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    }
    @NgModule({
        imports: [
            // ...
            DxChartModule
        ],
        // ...
    })
    Vue
    App.vue
    <template> 
        <DxChart ... >
            <DxArgumentAxis
                :aggregation-interval="100" <!-- A new interval every 100 units -->
                aggregation-interval="day">  <!-- A new interval every day -->
                <DxAggregationInterval :days="5"/> <!-- A new interval every five days -->
            </DxArgumentAxis>
        </DxChart>
    </template>
    
    <script>
    import DxChart, {
        DxArgumentAxis,
        DxAggregationInterval
    } from 'devextreme-vue/chart';
    
    export default {
        components: {
            DxChart,
            DxArgumentAxis,
            DxAggregationInterval
        }
    }
    </script>
    React
    App.js
    import React from 'react';
    import Chart, {
        ArgumentAxis,
        AggregationInterval
    } from 'devextreme-react/chart';
    
    class App extends React.Component {
        render() {
            return (
                <Chart ... >
                    <ArgumentAxis
                        aggregationInterval={100} {/* A new interval every 100 units */}
                        aggregationInterval="day">  {/* A new interval every day */}
                        <AggregationInterval days={5} /> {/* A new interval every five days */}
                    </ArgumentAxis>
                </Chart>
            );
        }
    }
    
    export default App;
  • Pixels
    Use the argumentAxis.aggregationGroupWidth property.

    jQuery
    JavaScript
    $(function() {
        $("#chartContainer").dxChart({
            // ...
            argumentAxis: {
                // A new interval every 100 pixels
                aggregationGroupWidth: 100
            }
        });
    });
    Angular
    HTML
    TypeScript
    <dx-chart ... >
        <dxo-argument-axis
            [aggregationGroupWidth]="100"> <!-- A new interval every 100 pixels -->
        </dxo-argument-axis>
    </dx-chart>
    import { DxChartModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    }
    @NgModule({
        imports: [
            // ...
            DxChartModule
        ],
        // ...
    })
    Vue
    App.vue
    <template> 
        <DxChart ... >
            <DxArgumentAxis :aggregation-group-width="100"/> <!-- A new interval every 100 pixels -->
        </DxChart>
    </template>
    
    <script>
    import DxChart, {
        DxArgumentAxis
    } from 'devextreme-vue/chart';
    
    export default {
        components: {
            DxChart,
            DxArgumentAxis
        }
    }
    </script>
    React
    App.js
    import React from 'react';
    import Chart, {
        ArgumentAxis
    } from 'devextreme-react/chart';
    
    class App extends React.Component {
        render() {
            return (
                <Chart ... >
                    <ArgumentAxis aggregationGroupWidth={100} /> {/* A new interval every 100 pixels */}
                </Chart>
            );
        }
    }
    
    export default App;
  • Categories
    Aggregation by categories can be applied only when the axis displays categories, and each category contains two or more points. To apply it, set argumentAxis.aggregateByCategory to true:

    jQuery
    JavaScript
    $(function() {
        $("#chartContainer").dxChart({
            // ...
            argumentAxis: {
                aggregateByCategory: true
            }
        });
    });
    Angular
    HTML
    TypeScript
    <dx-chart ... >
        <dxo-argument-axis
            [aggregateByCategory]="true">
        </dxo-argument-axis>
    </dx-chart>
    import { DxChartModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    }
    @NgModule({
        imports: [
            // ...
            DxChartModule
        ],
        // ...
    })
    Vue
    App.vue
    <template> 
        <DxChart ... >
            <DxArgumentAxis :aggregate-by-category="true"/>
        </DxChart>
    </template>
    
    <script>
    import DxChart, {
        DxArgumentAxis
    } from 'devextreme-vue/chart';
    
    export default {
        components: {
            DxChart,
            DxArgumentAxis
        }
    }
    </script>
    React
    App.js
    import React from 'react';
    import Chart, {
        ArgumentAxis
    } from 'devextreme-react/chart';
    
    class App extends React.Component {
        render() {
            return (
                <Chart ... >
                    <ArgumentAxis aggregateByCategory={true} />
                </Chart>
            );
        }
    }
    
    export default App;

Choose the Aggregation Method

Aggregation methods specify how series points should be aggregated. DevExtreme provides the most commonly used aggregation methods out of the box. To use one, specify a series' aggregation.method property. In its description, you will find the full list of provided aggregation methods. Note that different series types have different aggregation methods available.

The following code shows how to specify aggregation methods for each series individually. Data aggregation is enabled for all series in the commonSeriesSettings object.

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        // ...
        commonSeriesSettings: {
            aggregation: {
                enabled: true
            }
        },
        series: [{
            // ...
            type: "line",
            aggregation: {
                method: "min"
            }
        }, {
            // ...
            type: "bar",
            aggregation: {
                method: "max"
            }
        }]
    });
});
Angular
HTML
TypeScript
<dx-chart ... >
    <dxo-common-series-settings ... >
        <dxo-aggregation
            [enabled]="true">
        </dxo-aggregation>
    </dxo-common-series-settings>

    <dxi-series type="line" ... >
        <dxo-aggregation
            method="min">
        </dxo-aggregation>
    </dxi-series>

    <dxi-series type="bar" ... >
        <dxo-aggregation
            method="max">
        </dxo-aggregation>
    </dxi-series>
</dx-chart>
import { DxChartModule } from "devextreme-angular";
// ...
export class AppComponent {
    // ...
}
@NgModule({
    imports: [
        // ...
        DxChartModule
    ],
    // ...
})
Vue
App.vue
<template> 
    <DxChart ... >
        <DxCommonSeriesSettings ... >
            <DxAggregation :enabled="true"/>
        </DxCommonSeriesSettings>
        <DxSeries ...
            type="line">
            <DxAggregation method="min"/>
        </DxSeries>
        <DxSeries ...
            type="bar">
            <DxAggregation method="max"/>
        </DxSeries>
    </DxChart>
</template>

<script>
import DxChart, {
    DxCommonSeriesSettings,
    DxSeries,
    DxAggregation
} from 'devextreme-vue/chart';

export default {
    components: {
        DxChart,
        DxCommonSeriesSettings,
        DxSeries,
        DxAggregation
    }
}
</script>
React
App.js
import React from 'react';
import Chart, {
    CommonSeriesSettings,
    Series,
    Aggregation
} from 'devextreme-react/chart';

class App extends React.Component {
    render() {
        return (
            <Chart ... >
                <CommonSeriesSettings ... >
                    <Aggregation enabled={true} />
                </CommonSeriesSettings>
                <Series ...
                    type="line">
                    <Aggregation method="min" />
                </Series>
                <Series ...
                    type="bar">
                    <Aggregation method="max" />
                </Series>
            </Chart>
        );
    }
}

export default App;

View Demo

You can implement a custom aggregate function instead of using an out-of-the-box aggregation method.

Implement a Custom Aggregate Function

When implementing a custom aggregate function, use the aggregationInfo object that is passed to it as the first argument. This object contains information on the aggregation interval for which the function is called and the data objects that fall within this interval. In addition, you can access the Series object, which is passed to the function as the second argument.

To apply the function, assign it to the series' aggregation.calculate property and set the aggregation.method property to "custom".

View Demo

In the following code, a custom aggregation function implements the median filter algorithm:

jQuery
JavaScript
$(function() {
    $("#chartContainer").dxChart({
        dataSource: [
            { argument: 1, value: 10 },
            // ...
        ],
        series: [{
            argumentField: "argument",
            valueField: "value",
            aggregation: {
                enabled: true,
                method: "custom",
                calculate: function (aggregationInfo) {
                    if (aggregationInfo.data.length) {
                        return {
                            argument: aggregationInfo.intervalStart,
                            value: aggregationInfo.data[Math.floor(aggregationInfo.data.length / 2)].value
                        };
                    }
                }

            }
        }]
    });
});
Angular
HTML
TypeScript
<dx-chart [dataSource]="data">
    <dxi-series
        argumentField="argument"
        valueField="value">
        <dxo-aggregation
            [enabled]="true"
            method="custom"
            [calculate]="calcMedianFilter">
        </dxo-aggregation>
    </dxi-series>
</dx-chart>
import { DxChartModule } from "devextreme-angular";
// ...
export class AppComponent {
    data = [
        { argument: 1, value: 10 },
        // ...
    ];
    calcMedianFilter (aggregationInfo) {
        if (aggregationInfo.data.length) {
            return {
                argument: aggregationInfo.intervalStart,
                value: aggregationInfo.data[Math.floor(aggregationInfo.data.length / 2)].value
            };
        }
    };
}
@NgModule({
    imports: [
        // ...
        DxChartModule
    ],
    // ...
})
Vue
App.vue
<template> 
    <DxChart ...
        :data-source="data">
        <DxSeries
            argument-field="argument"
            value-field="value">
            <DxAggregation
                :enabled="true"
                :calculate="calcMedianFilter"
                method="custom"
            />
        </DxSeries>
    </DxChart>
</template>

<script>
import DxChart, {
    DxSeries,
    DxAggregation
} from 'devextreme-vue/chart';

export default {
    components: {
        DxChart,
        DxSeries,
        DxAggregation
    },

    data() {
        return {
            data: [
                { argument: 1, value: 10 },
                // ...
            ]
        };
    },

    methods: {
        calcMedianFilter(aggregationInfo) {
            if(aggregationInfo.data.length) {
                return {
                    argument: aggregationInfo.intervalStart,
                    value: aggregationInfo.data[Math.floor(aggregationInfo.data.length / 2)].value
                };
            }
        }
    }
}
</script>
React
App.js
import React from 'react';
import Chart, {
    Series,
    Aggregation
} from 'devextreme-react/chart';

const data = [
    { argument: 1, value: 10 },
    // ...
];

class App extends React.Component {
    render() {
        return (
            <Chart ...
                dataSource={data}>
                <Series
                    argumentField="argument"
                    valueField="value">
                    <Aggregation
                        enabled={true}
                        calculate={calcMedianFilter}
                        method="custom"
                    />
                </Series>
            </Chart>
        );
    }
}

function calcMedianFilter(aggregationInfo) {
    if(aggregationInfo.data.length) {
        return {
            argument: aggregationInfo.intervalStart,
            value: aggregationInfo.data[Math.floor(aggregationInfo.data.length / 2)].value
        };
    }
}

export default App;