React Correlation Plot

This example demonstrates a high performance Scatter chart grid with 5000 points per chart using the subcharts api in SciChart.js which can be used to create a React Correlation Plot

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.tsx

helpers.ts

theme.ts

correlationLinePoints.ts

Copy to clipboard
Minimise
Fullscreen
1import { appTheme } from "../../../theme";
2import {
3    EllipsePointMarker,
4    NumericAxis,
5    NumberRange,
6    SciChartSurface,
7    XyDataSeries,
8    XyScatterRenderableSeries,
9    FastLineRenderableSeries,
10    INumericAxisOptions,
11    ENumericFormat,
12    EAutoRange,
13    ESubSurfacePositionCoordinateMode,
14    SciChartSubSurface,
15    Rect,
16    I2DSubSurfaceOptions,
17    Thickness,
18    TextAnnotation,
19    ECoordinateMode,
20    EHorizontalAnchorPoint,
21    EVerticalAnchorPoint,
22    FadeAnimation,
23    ZoomExtentsModifier,
24    ZoomPanModifier,
25    MouseWheelZoomModifier,
26    EModifierMouseArgKey,
27    EExecuteOn,
28    EAxisAlignment,
29    TextLabelProvider,
30    ELabelAlignment,
31    ETitlePosition,
32    SciChartVerticalGroup,
33} from "scichart";
34
35import correlationLinePoints from "./correlationLinePoints";
36import { getSubChartPositionIndexes } from "../../FeatureDemos/SubChartsAPI/helpers";
37
38const axisOptions: INumericAxisOptions = {
39    useNativeText: true,
40    isVisible: false,
41    drawMajorBands: false,
42    drawMinorGridLines: false,
43    drawMinorTickLines: false,
44    drawMajorTickLines: false,
45    drawMajorGridLines: false,
46    labelStyle: { fontSize: 8 },
47    labelFormat: ENumericFormat.Decimal,
48    labelPrecision: 1,
49};
50
51// theme overrides
52const sciChartTheme = appTheme.SciChartJsTheme;
53
54export const drawExample = async (rootElement: string | HTMLDivElement) => {
55    // Use createSingle here to get the performance benefit of subcharts
56    const { wasmContext, sciChartSurface: mainSurface } = await SciChartSurface.createSingle(rootElement, {
57        theme: sciChartTheme,
58        title: "Hold Ctrl to Zoom / Pan the whole grid rather than an individual chart",
59        titleStyle: { fontSize: 14, position: ETitlePosition.Bottom },
60    });
61
62    const subChartsNumber = 24;
63    const columnsNumber = 6;
64    const rowsNumber = 4;
65
66    const pointsOnChart = 5000;
67
68    const subchartBorderColor = appTheme.VividSkyBlue;
69    const scatterColor = appTheme.VividSkyBlue;
70    const lineUp = appTheme.VividGreen;
71    const lineDown = appTheme.VividRed;
72    const lineHorizontal = appTheme.ForegroundColor;
73    const annotationColor = appTheme.ForegroundColor;
74
75    const annotationFontSize = 14;
76
77    const xAxisVisibleRange = new NumberRange(0, columnsNumber);
78    const yAxisVisibleRange = new NumberRange(0, rowsNumber);
79
80    const mainXAxis = new NumericAxis(wasmContext, {
81        zoomExtentsRange: new NumberRange(xAxisVisibleRange.min, xAxisVisibleRange.max),
82        drawMajorBands: false,
83        drawMajorGridLines: false,
84        drawMinorGridLines: false,
85        isVisible: true,
86        id: "mainXAxis",
87        visibleRange: new NumberRange(xAxisVisibleRange.min, xAxisVisibleRange.max),
88        // Uncomment this to limit panning when fully zoomed out
89        //visibleRangeLimit: new NumberRange(xAxisVisibleRange.min, xAxisVisibleRange.max),
90        axisAlignment: EAxisAlignment.Top,
91        useNativeText: false,
92        labelProvider: new TextLabelProvider({
93            labels: { 0.5: "A", 1.5: "B", 2.5: "C", 3.5: "D", 4.5: "E", 5.5: "F" },
94            useNativeText: false,
95        }),
96        labelStyle: {
97            alignment: ELabelAlignment.Center,
98            fontFamily: "Arial",
99            fontSize: 24,
100            fontWeight: "bold",
101            color: "White",
102        },
103    });
104
105    // provide hardcoded tick values for the x axis as these will be used to position column names
106    mainXAxis.tickProvider.getMajorTicks = (minorDelta: number, majorDelta: number, visibleRange: NumberRange) =>
107        [...new Array(columnsNumber)].map((d, i) => i + 0.5);
108
109    mainSurface.xAxes.add(mainXAxis);
110
111    const mainYAxis = new NumericAxis(wasmContext, {
112        zoomExtentsRange: new NumberRange(yAxisVisibleRange.min, yAxisVisibleRange.max),
113        drawMajorBands: false,
114        drawMajorGridLines: false,
115        drawMinorGridLines: false,
116        isVisible: true,
117        id: "mainYAxis",
118        visibleRange: new NumberRange(yAxisVisibleRange.min, yAxisVisibleRange.max),
119        // Uncomment this to limit panning when fully zoomed out
120        //visibleRangeLimit: new NumberRange(yAxisVisibleRange.min, yAxisVisibleRange.max),
121        axisAlignment: EAxisAlignment.Left,
122        flippedCoordinates: true,
123        useNativeText: false,
124        labelProvider: new TextLabelProvider({
125            labels: { 0.5: "1", 1.5: "2", 2.5: "3", 3.5: "4" },
126            useNativeText: false,
127        }),
128        labelStyle: {
129            alignment: ELabelAlignment.Center,
130            fontFamily: "Arial",
131            fontSize: 24,
132            fontWeight: "bold",
133            color: "White",
134        },
135    });
136    mainSurface.yAxes.add(mainYAxis);
137
138    // The executeCondition set here allows these modifiers to activate independently of the ones on the individual subSurfaces
139    mainSurface.chartModifiers.add(
140        new ZoomExtentsModifier({ executeCondition: { key: EModifierMouseArgKey.Ctrl } }),
141        new ZoomPanModifier({
142            executeCondition: { button: EExecuteOn.MouseLeftButton, key: EModifierMouseArgKey.Ctrl },
143        }),
144        new MouseWheelZoomModifier({ executeCondition: { key: EModifierMouseArgKey.Ctrl } })
145    );
146
147    const subChartPositioningCoordinateMode = ESubSurfacePositionCoordinateMode.DataValue;
148    const vGroup = new SciChartVerticalGroup();
149    let maxYRange = new NumberRange(-1, 1);
150
151    const initSubChart = (subChartIndex: number) => {
152        const { rowIndex, columnIndex } = getSubChartPositionIndexes(subChartIndex, columnsNumber);
153
154        const width = 1;
155        const height = 1;
156
157        const position = new Rect(columnIndex * width, rowIndex * height, width, height);
158        // sub-surface configuration
159        const subChartOptions: I2DSubSurfaceOptions = {
160            id: `subChart-${subChartIndex}`,
161            theme: sciChartTheme,
162            position,
163            parentXAxisId: mainXAxis.id,
164            parentYAxisId: mainYAxis.id,
165            coordinateMode: subChartPositioningCoordinateMode,
166            padding: Thickness.fromNumber(0),
167            viewportBorder: {
168                color: subchartBorderColor + "30",
169                border: 1,
170            },
171        };
172
173        // create sub-surface
174        const subChartSurface = SciChartSubSurface.createSubSurface(mainSurface, subChartOptions);
175
176        // add axes to the sub-surface
177        const subChartXAxis = new NumericAxis(wasmContext, {
178            ...axisOptions,
179            id: `${subChartSurface.id}-XAxis`,
180            growBy: new NumberRange(0.04, 0.04),
181            isVisible: rowIndex === rowsNumber - 1,
182        });
183
184        subChartSurface.xAxes.add(subChartXAxis);
185
186        const subChartYAxis = new NumericAxis(wasmContext, {
187            ...axisOptions,
188            id: `${subChartSurface.id}-YAxis`,
189            axisAlignment: EAxisAlignment.Left,
190            isVisible: columnIndex === 0,
191        });
192
193        subChartSurface.yAxes.add(subChartYAxis);
194
195        if (columnIndex === 0) {
196            // Synchonise axis sizes
197            vGroup.addSurfaceToGroup(subChartSurface);
198            // Set y ranges for previous Row
199            if (rowIndex > 0) {
200                const start = (rowIndex - 1) * columnsNumber;
201                for (let index = start; index < start + columnsNumber; index++) {
202                    mainSurface.subCharts[index].yAxes.get(0).visibleRange = maxYRange;
203                }
204            }
205            // reset range tracking
206            maxYRange = new NumberRange(-1, 1);
207        }
208
209        const gaussianRand = (mean: number = 0.5, dist: number = 1) => {
210            let rand = 0;
211            for (let i = 0; i < 6; i += 1) {
212                rand += Math.random() * dist + mean;
213            }
214            return rand / 6;
215        };
216
217        // generating random data for scatterplots
218        function generateScatterplotData(numElements: number) {
219            let x: number[] = [];
220            let y: number[] = [];
221
222            let randomNum = (Math.random() - 0.5) * 3;
223            let randomNum1 = Math.random();
224
225            for (let i = 0; i < numElements; i++) {
226                const xVal = gaussianRand(0.5, 1);
227                x.push(xVal);
228                y.push(gaussianRand(xVal * randomNum, 1 + randomNum1 * 3));
229            }
230            return { x, y };
231        }
232
233        const { x: xValues, y: yValues } = generateScatterplotData(pointsOnChart);
234
235        const { correlationCoefficient, linePoints } = correlationLinePoints(xValues, yValues);
236
237        const lineSeries = new FastLineRenderableSeries(wasmContext, {
238            dataSeries: new XyDataSeries(wasmContext, {
239                xValues: [linePoints.x1, linePoints.x2],
240                yValues: [linePoints.y1, linePoints.y2],
241            }),
242            stroke: correlationCoefficient > 0.1 ? lineUp : correlationCoefficient < -0.1 ? lineDown : lineHorizontal,
243            strokeThickness: 3,
244            opacity: 0.8,
245            animation: new FadeAnimation({ duration: 600, fadeEffect: true }),
246        });
247
248        const scatterSeries = new XyScatterRenderableSeries(wasmContext, {
249            dataSeries: new XyDataSeries(wasmContext, { xValues, yValues }),
250            pointMarker: new EllipsePointMarker(wasmContext, {
251                width: 2,
252                height: 2,
253                strokeThickness: 0,
254                fill: scatterColor,
255            }),
256            opacity: 1,
257        });
258
259        const annotation = new TextAnnotation({
260            x1: 5,
261            y1: 0.07,
262            xCoordinateMode: ECoordinateMode.Pixel,
263            yCoordinateMode: ECoordinateMode.Relative,
264            horizontalAnchorPoint: EHorizontalAnchorPoint.Left,
265            verticalAnchorPoint: EVerticalAnchorPoint.Center,
266            textColor: annotationColor,
267            fontSize: annotationFontSize,
268            fontFamily: "Default",
269            text: `i = ${subChartIndex}, r = ${correlationCoefficient.toFixed(2)}`,
270        });
271
272        subChartSurface.renderableSeries.add(scatterSeries, lineSeries);
273        subChartSurface.annotations.add(annotation);
274
275        const xRange = scatterSeries.getXRange();
276        const yRange = scatterSeries.getYRange(xRange);
277        if (yRange.min < maxYRange.min || yRange.max > maxYRange.max) {
278            maxYRange = maxYRange.union(yRange);
279        }
280
281        subChartSurface.chartModifiers.add(
282            new ZoomExtentsModifier(),
283            new ZoomPanModifier(),
284            new MouseWheelZoomModifier()
285        );
286    };
287
288    // generate the subcharts grid
289    for (let subChartIndex = 0; subChartIndex < subChartsNumber; subChartIndex += 1) {
290        initSubChart(subChartIndex);
291    }
292    // set last row y range
293    const start = (rowsNumber - 1) * columnsNumber;
294    for (let index = start; index < start + columnsNumber; index++) {
295        mainSurface.subCharts[index].yAxes.get(0).visibleRange = maxYRange;
296    }
297
298    return {
299        wasmContext,
300        sciChartSurface: mainSurface,
301    };
302};
303

React Correlation Plot

Overview

This example demonstrates how to build a Correlation Plot in a React application using SciChart.js. A correlation plot is a powerful visualization tool used to show the correlation matrix between many variables, presented as a grid of sub-charts.

Technical Implementation

The core of this example is the use of the Subcharts API to efficiently create and manage the grid of charts within a single SciChartSurface. Each individual chart in the matrix displays a relationship between two variables using an XY Scatter Renderable Series. It uses randomly generated dataSets for each chart with a random correlation.

Features and Capabilities

The example demonstrates the programatic use of renderableSeries.getXRange and renderableSeries.getYRange to synchronise the y ranges for each row, and SciChartVerticalGroup to synchronise the chart sizes for the first column. The chart is fully interactive, supporting zooming and panning of individual charts and also the grid as a whole, when holding Ctrl. This is achieved using modifiers on the main surface which different executeCondition configured.

Integration and Best Practices

Integration into a React application is seamless using the <SciChartReact> component. The main chart configuration logic is encapsulated in the drawExample function, which is passed to the initChart prop. This approach ensures that the SciChart.js surface is correctly initialized and disposed of, aligning with React's component lifecycle and preventing memory leaks.

react Chart Examples & Demos

See Also: Scientific & Medical Charts (10 Demos)

React Vital Signs ECG/EKG Medical Demo | SciChart.js Demo

React Vital Signs ECG/EKG Medical Demo

In this example we are simulating four channels of data showing that SciChart.js can be used to draw real-time ECG/EKG charts and graphs to monitor heart reate, body temperature, blood pressure, pulse rate, SPO2 blood oxygen, volumetric flow and more.

React Chart with Logarithmic Axis Example | SciChart.js

React Chart with Logarithmic Axis Example

Demonstrates Logarithmic Axis on a React Chart using SciChart.js. SciChart supports logarithmic axis with scientific or engineering notation and positive and negative values

LiDAR 3D Point Cloud of Geospatial Data | SciChart.js

LiDAR 3D Point Cloud of Geospatial Data

Demonstrating the capability of SciChart.js to create JavaScript 3D Point Cloud charts and visualize LiDAR data from the UK Defra Survey.

React Chart with Vertically Stacked Axes | SciChart.js

React Chart with Vertically Stacked Axes

Demonstrates Vertically Stacked Axes on a React Chart using SciChart.js, allowing data to overlap

Realtime Audio Spectrum Analyzer Chart | SciChart.js Demo

Realtime Audio Spectrum Analyzer Chart Example

See the frequency of recordings with the React audio spectrum analyzer example from SciChart. This real-time audio visualizer demo uses a Fourier Transform.

Realtime Audio Analyzer Bars Demo | SciChart.js Demo

Realtime Audio Analyzer Bars Demo

Demonstrating the capability of SciChart.js to create a JavaScript Audio Analyzer Bars and visualize the Fourier-Transform of an audio waveform in realtime.

Interactive Waterfall Chart | React Charts | SciChart.js

Interactive Waterfall Spectral Chart

Demonstrates how to create a Waterfall chart in SciChart.js, showing chromotragraphy data with interactive selection of points.

Interactive Phasor Diagram chart | React Charts | SciChart.js

Phasor Diagram Chart Example

See the React Phasor Diagram example to combine a Cartesian surface with a Polar subsurface. Get seamless React integration with SciChart. View demo now.

NEW!
React Semiconductors Dashboard | JavaScript Charts | SciChart.js

Semiconductors Dashboard

React **Semiconductors Dashboard** using SciChart.js, by leveraging the **FastRectangleRenderableSeries**, and its `customTextureOptions` property to have a custom tiling texture fill.

NEW!
React Wafer Analysis Chart | JavaScript Charts | SciChart.js

Wafer Analysis Chart

React **Wafer Analysis Chart** using SciChart.js, by leveraging the **FastRectangleRenderableSeries**, and crossfilter to enable live filtering.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.