Skip to content

Commit b618e81

Browse files
committed
heatmap wip
1 parent 3c958de commit b618e81

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

src/assets/synthetic-data.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ def run():
165165
{'type': 'heading', 'data': 'Output file:'}
166166
))
167167
setResult(json.dumps({'type': 'table', 'data': synthetic_data.to_json(orient="records")}))
168+
169+
np.random.seed(42)
170+
heatmap = np.random.rand(100, 10)
171+
172+
# Compute the correlation matrix
173+
correlation_matrix = np.corrcoef(heatmap, rowvar=False)
174+
setResult(json.dumps({'type': 'heatmap', 'data': correlation_matrix.tolist()}))
168175
return
169176
170177

src/components/componentMapper.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CSVData } from './bias-detection-interfaces/csv-data';
88
import { Fragment } from 'react/jsx-runtime';
99
import { Accordion } from './ui/accordion';
1010
import { useTranslation } from 'react-i18next';
11+
import HeatMapChart from './graphs/HeatMap';
1112

1213
const createArrayFromPythonDictionary = (dict: Record<string, number>) => {
1314
const resultArray = [];
@@ -127,6 +128,25 @@ export default function ComponentMapper({
127128
</ErrorBoundary>
128129
);
129130
}
131+
case 'heatmap': {
132+
console.log('heatmap data', resultItem.data);
133+
/*
134+
Array in Array
135+
136+
[
137+
[1,2,3],
138+
[4,5,6],
139+
[7,8,9]
140+
]
141+
*/
142+
return (
143+
<HeatMapChart
144+
key={index}
145+
data={resultItem.data}
146+
title={resultItem.title ?? ''}
147+
/>
148+
);
149+
}
130150
case 'barchart': {
131151
const barchartData = JSON.parse(resultItem.data)?.map(
132152
(x: Record<string, number>, index: number) => {

src/components/graphs/HeatMap.tsx

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import * as d3 from 'd3';
3+
4+
interface HeatMapChartProps {
5+
title: string;
6+
data: number[][];
7+
}
8+
9+
// Define margins for the chart
10+
const margin = { top: 10, right: 50, bottom: 40, left: 50 };
11+
// Define height for the chart, adjusting for margins
12+
const height = 300 - margin.top - margin.bottom;
13+
14+
// Define width of bars and adjust for screenwidth
15+
const barWidth = 0.05 * window.innerWidth < 40 ? 40 : 0.05 * window.innerWidth;
16+
const barGap = 5;
17+
18+
const HeatMapChart = ({ title, data }: HeatMapChartProps) => {
19+
const svgRef = useRef<SVGSVGElement>(null); // Reference to the SVG element
20+
const containerRef = useRef<HTMLDivElement>(null); // Reference to the container div
21+
const [containerWidth, setContainerWidth] = useState(800); // Default container width
22+
23+
useEffect(() => {
24+
// Clear any previous SVG content to avoid overlapping elements
25+
d3.select(svgRef.current).selectAll('*').remove();
26+
27+
// Create the SVG container and set its dimensions
28+
const svg = d3
29+
.select(svgRef.current)
30+
.attr('class', `min-h-[${height}px]`)
31+
.attr(
32+
'width',
33+
Math.max(
34+
containerWidth,
35+
margin.left + data.length * (barWidth + barGap)
36+
)
37+
)
38+
.attr(
39+
'height',
40+
data.length * (barWidth + barGap) + margin.top + margin.bottom
41+
)
42+
.append('g')
43+
.attr('transform', `translate(${margin.left},${margin.top})`);
44+
45+
svg.append('defs')
46+
.append('style')
47+
.attr('type', 'text/css')
48+
.text(
49+
"@import url('https://fonts.googleapis.com/css2?family=Avenir:wght@600');"
50+
);
51+
52+
const colorScale = d3
53+
.scaleSequential()
54+
.domain([-1, 1])
55+
.interpolator(d3.interpolateBlues);
56+
data.forEach((dataRow, rowIndex) => {
57+
dataRow.forEach((dataCell, cellIndex) => {
58+
svg.append('rect')
59+
.attr('x', cellIndex * barWidth)
60+
.attr('y', rowIndex * barWidth)
61+
.attr('width', barWidth)
62+
.attr('height', barWidth)
63+
.style('fill', function () {
64+
return colorScale(dataCell);
65+
});
66+
});
67+
});
68+
}, [data, title, containerWidth]);
69+
70+
useEffect(() => {
71+
// Set up the ResizeObserver to track changes in the container's size
72+
const resizeObserver = new ResizeObserver(entries => {
73+
if (!entries || entries.length === 0) return;
74+
const { width } = entries[0].contentRect;
75+
setContainerWidth(width); // Update the state with the new container width
76+
});
77+
78+
if (containerRef.current) {
79+
resizeObserver.observe(containerRef.current); // Start observing the container
80+
}
81+
82+
return () => {
83+
if (containerRef.current) {
84+
resizeObserver.unobserve(containerRef.current); // Cleanup on component unmount
85+
}
86+
};
87+
}, []);
88+
89+
// Render the chart container and SVG element with horizontal scroll if needed
90+
return (
91+
<div
92+
ref={containerRef}
93+
style={{ width: '100%', display: 'flex', overflowX: 'auto' }}
94+
className={`min-h-[${height}px] flex-col`}
95+
>
96+
<svg ref={svgRef}></svg>
97+
</div>
98+
);
99+
};
100+
101+
export default HeatMapChart;

0 commit comments

Comments
 (0)