Skip to content

Commit 3348e9c

Browse files
committed
count barchart for synthetic data
1 parent 78cb8b4 commit 3348e9c

File tree

5 files changed

+232
-4
lines changed

5 files changed

+232
-4
lines changed

src/assets/synthetic-data.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,10 @@ def run():
140140
{'type': 'data-set-preview', 'data': ''}
141141
))
142142
143+
dtypes_dict = real_data.dtypes.to_dict()
144+
dtypes_dict = {k: 'float' if v == 'float64' else 'category' if v == 'O' else v for k, v in dtypes_dict.items()}
143145
144146
if (sdgMethod == 'cart'):
145-
dtypes_dict = real_data.dtypes.to_dict()
146-
dtypes_dict = {k: 'float' if v == 'float64' else 'category' if v == 'O' else v for k, v in dtypes_dict.items()}
147147
label_encoders = {}
148148
for column in real_data.select_dtypes(include=['object']).columns:
149149
label_encoders[column] = LabelEncoder()
@@ -193,7 +193,15 @@ def run():
193193
194194
setResult(json.dumps({'type': 'heatmap', 'real': real_data.corr().to_json(orient="records"), 'synthetic': synthetic_data.corr().to_json(orient="records")}))
195195
196-
setResult(json.dumps({'type': 'distribution', 'real': real_data.to_json(orient="records"), 'synthetic': synthetic_data.to_json(orient="records")}))
196+
# copy dataframe and assign NaN to all values
197+
synth_df = real_data.copy()
198+
synth_df[:] = np.nan
199+
200+
# combine empty synthetic data with original data and with encoded data
201+
combined_data = pd.concat((real_data.assign(realOrSynthetic='real'), synth_df.assign(realOrSynthetic='synthetic')), keys=['real','synthetic'], names=['Data'])
202+
# combined_data_encoded = pd.concat((df_encoded.assign(realOrSynthetic='real_encoded'), synth_df.assign(realOrSynthetic='synthetic')), keys=['real_encoded','synthetic'], names=['Data'])
203+
204+
setResult(json.dumps({'type': 'distribution', 'real': real_data.to_json(orient="records"), 'synthetic': synthetic_data.to_json(orient="records"), 'dataTypes': json.dumps(dtypes_dict), 'combined_data' : combined_data.to_json(orient="records")}))
197205
198206
return
199207
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React from 'react';
2+
import CountBarChart from './graphs/CountBarChart';
3+
4+
export interface ChartProps {
5+
realData: object[];
6+
syntheticData: object[];
7+
dataTypes: Record<string, string>;
8+
combined_data: object;
9+
comparison: boolean;
10+
}
11+
12+
export const UnivariateCharts = (props: ChartProps) => {
13+
if (
14+
props.realData &&
15+
props.syntheticData &&
16+
props.dataTypes &&
17+
props.combined_data &&
18+
props.realData.length > 0 &&
19+
props.syntheticData.length > 0
20+
) {
21+
const realData = props.realData;
22+
//const syntheticData = props.syntheticData;
23+
const dataTypes = props.dataTypes;
24+
//const combinedData = props.combined_data;
25+
const realDataKeys = Object.keys(realData[0]);
26+
//const syntheticDataKeys = Object.keys(syntheticData[0]);
27+
//const combinedDataKeys = Object.keys(combinedData[0]);
28+
29+
/*
30+
31+
<SingleBarChart
32+
key={index}
33+
data={barchartData}
34+
title={resultItem.title ?? ''}
35+
/>
36+
37+
*/
38+
39+
return realDataKeys
40+
.filter(column => {
41+
return column != 'realOrSynthetic';
42+
})
43+
.map((column, index) => {
44+
const isFloatColumn = dataTypes[column] == 'float';
45+
if (isFloatColumn) {
46+
return (
47+
<React.Fragment key={index}>
48+
<h2>{column}</h2>
49+
<CountBarChart
50+
key={index}
51+
column={column}
52+
realData={realData.map(
53+
row =>
54+
(
55+
row as unknown as Record<
56+
string,
57+
object
58+
>
59+
)[column] as unknown as number
60+
)}
61+
/>
62+
</React.Fragment>
63+
);
64+
}
65+
66+
return (
67+
<div key={index}>
68+
{column} : {dataTypes[column]}
69+
{}
70+
</div>
71+
);
72+
});
73+
}
74+
return <div></div>;
75+
};

src/components/componentMapper.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Accordion } from './ui/accordion';
1010
import { useTranslation } from 'react-i18next';
1111
import HeatMapChart from './graphs/HeatMap';
1212
import DistributionBarChart from './graphs/DistributionBarChart';
13+
import { UnivariateCharts } from './UnivariateCharts';
1314

1415
const createArrayFromPythonDictionary = (dict: Record<string, number>) => {
1516
const resultArray = [];
@@ -177,10 +178,26 @@ export default function ComponentMapper({
177178
);
178179
}
179180
case 'distribution': {
181+
console.log(
182+
'distribution',
183+
JSON.parse(resultItem.dataTypes)
184+
);
185+
180186
const realData = JSON.parse(resultItem.real);
181187
const syntheticData = JSON.parse(resultItem.synthetic);
188+
189+
console.log('realData', realData);
182190
return (
183191
<div key={`distribution-${index}`}>
192+
<UnivariateCharts
193+
realData={realData}
194+
syntheticData={syntheticData}
195+
dataTypes={JSON.parse(resultItem.dataTypes)}
196+
combined_data={JSON.parse(
197+
resultItem.combined_data
198+
)}
199+
comparison={false}
200+
/>
184201
{realData.length === 0 ||
185202
syntheticData.length === 0
186203
? null
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import * as d3 from 'd3';
3+
import { useTranslation } from 'react-i18next';
4+
5+
interface CountBarChartProps {
6+
column: string;
7+
realData: number[];
8+
}
9+
10+
// Define margins for the chart
11+
const margin = { top: 30, right: 50, bottom: 40, left: 50 };
12+
// Define height for the chart, adjusting for margins
13+
const height = 300 - margin.top - margin.bottom;
14+
15+
// Define width of bars and adjust for screenwidth
16+
// const barWidth = 0.05 * window.innerWidth < 40 ? 40 : 0.05 * window.innerWidth;
17+
// const barGap = 5;
18+
19+
const CountBarChart = ({ column, realData }: CountBarChartProps) => {
20+
const svgRef = useRef<SVGSVGElement>(null); // Reference to the SVG element
21+
const containerRef = useRef<HTMLDivElement>(null); // Reference to the container div
22+
const [containerWidth, setContainerWidth] = useState(800); // Default container width
23+
const { t } = useTranslation();
24+
useEffect(() => {
25+
const plotWidth = containerWidth - margin.left - margin.right;
26+
const plotHeight = height - margin.top - margin.bottom;
27+
28+
const xScale = d3
29+
.scaleLinear()
30+
.domain([d3.min(realData) || 0, d3.max(realData) || 1])
31+
.range([0, plotWidth]);
32+
33+
const binsReal = d3
34+
.bin()
35+
.domain(xScale.domain() as [number, number])
36+
.thresholds(30)(realData);
37+
38+
// Clear any previous SVG content to avoid overlapping elements
39+
d3.select(svgRef.current).selectAll('*').remove();
40+
41+
// Create the SVG container and set its dimensions
42+
const svg = d3
43+
.select(svgRef.current)
44+
.attr('class', `min-h-[${height}px]`)
45+
.attr('width', containerWidth)
46+
.attr('height', height + margin.top + margin.bottom)
47+
.append('g')
48+
.attr('transform', `translate(${margin.left},${margin.top})`);
49+
// Add axes
50+
svg.append('g')
51+
.attr('transform', `translate(0, ${plotHeight})`)
52+
.call(d3.axisBottom(xScale));
53+
54+
svg.append('defs')
55+
.append('style')
56+
.attr('type', 'text/css')
57+
.text(
58+
"@import url('https://fonts.googleapis.com/css2?family=Avenir:wght@600');"
59+
);
60+
61+
//const realDensityFactor = 1 / realData.length;
62+
63+
const yScale = d3
64+
.scaleLinear()
65+
.domain([0, d3.max([...binsReal.map(bin => bin.length)]) || 1])
66+
.range([plotHeight, 0]);
67+
68+
// Add axes
69+
svg.append('g')
70+
.attr('transform', `translate(0, ${plotHeight})`)
71+
.call(d3.axisBottom(xScale));
72+
svg.append('g').call(d3.axisLeft(yScale));
73+
74+
// Draw real data histogram
75+
svg.selectAll('.bar-real')
76+
.data(binsReal)
77+
.enter()
78+
.append('rect')
79+
.attr('class', 'bar-real')
80+
.attr('x', d => xScale(d.x0 || 0))
81+
.attr('y', d => yScale(d.length))
82+
.attr('width', d => xScale(d.x1 || 0) - xScale(d.x0 || 0) - 1)
83+
.attr('height', d => plotHeight - yScale(d.length))
84+
.style('fill', 'steelblue')
85+
.style('opacity', 0.5);
86+
87+
// Add title
88+
svg.append('text')
89+
.attr('x', plotWidth / 2)
90+
.attr('y', 10)
91+
.attr('text-anchor', 'middle')
92+
.style('font-size', '12px')
93+
.style('font-weight', 'bold')
94+
.text(`${t('distribution.distributionFor')} ${column}`);
95+
}, [containerWidth, column, realData]);
96+
97+
useEffect(() => {
98+
// Set up the ResizeObserver to track changes in the container's size
99+
const resizeObserver = new ResizeObserver(entries => {
100+
if (!entries || entries.length === 0) return;
101+
const { width } = entries[0].contentRect;
102+
setContainerWidth(width); // Update the state with the new container width
103+
});
104+
105+
if (containerRef.current) {
106+
resizeObserver.observe(containerRef.current); // Start observing the container
107+
}
108+
109+
return () => {
110+
if (containerRef.current) {
111+
resizeObserver.unobserve(containerRef.current); // Cleanup on component unmount
112+
}
113+
};
114+
}, []);
115+
116+
// Render the chart container and SVG element with horizontal scroll if needed
117+
return (
118+
<div
119+
ref={containerRef}
120+
style={{ width: '100%', display: 'flex', overflowX: 'auto' }}
121+
className={`min-h-[${height}px] flex-col`}
122+
>
123+
<svg ref={svgRef}></svg>
124+
</div>
125+
);
126+
};
127+
128+
export default CountBarChart;

src/components/graphs/SingleBarChart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const SingleBarChart = ({ title, data }: SingleBarChartProps) => {
2424
const svgRef = useRef<SVGSVGElement>(null); // Reference to the SVG element
2525
const containerRef = useRef<HTMLDivElement>(null); // Reference to the container div
2626
const [containerWidth, setContainerWidth] = useState(800); // Default container width
27-
27+
console.log('SingleBarChart', data);
2828
// Create x-axis scale using d3.scaleBand, with padding for spacing between bars
2929
const x0 = useMemo(
3030
() =>

0 commit comments

Comments
 (0)