Files
iTree/src/components/Tree.jsx
2023-06-16 14:00:12 +02:00

285 lines
8.3 KiB
JavaScript

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
Box,
Button,
ButtonGroup,
FormControl,
FormLabel,
Input,
InputGroup,
InputLeftAddon,
InputRightAddon,
Spinner,
Stack,
Switch,
} from '@chakra-ui/react';
import { GrTechnology } from 'react-icons/gr';
import Node from './Node';
import { useLoadingContext } from '../contexts/LoadingContext';
import { executeAlgorithm } from '../utils/algorithm-executor';
import TestSetFileReader from './TestSetFileReader';
import ConfusionMatrix from './ConfusionMatrix';
import { getSizeTree } from '../utils/size-checker';
import { rebuildTestTree } from '../utils/rebuilderTestTree';
/**
* @typedef {import('../utils/decision-tree.js').DecisionTreeBuilder} DecisionTreeBuilder
*/
const logTree = root => console.log('ROOT', root);
/**
* @param {Object} props
* @param {DecisionTreeBuilder} props.options
* @param {boolean} props.headers
*/
const Tree = ({ options, headers }) => {
// const root = useMemo(() => {
// //return dt.TSPDecisionTree(options)
// var t0 = performance.now();
// var r = buildDecisionTree(options);
// var t1 = performance.now();
// console.log('Call to doSomething took ' + (t1 - t0) + ' milliseconds.');
// return r;
// }, [options]);
const [accuracyTraining, setAccuracyTraining] = useState(0);
const [accuracyTest, setAccuracyTest] = useState(0);
const [root, setRoot] = useState(null);
const [secondRoot, setSecondRoot] = useState(null);
const [sizeTree, setSizeTree] = useState({ joints: 0, leafs: 0 });
const [testSet, setTestSet] = useState(null);
const [showTestTree, setShowTestTree] = useState(false);
const { isLoading, setIsLoading } = useLoadingContext();
// const updateTestTree = useCallback(
// newRoot => {
// console.log(options.categoryAttr);
// let tmpRoot = JSON.parse(JSON.stringify(newRoot));
// if (testSet == null) {
// rebuildTestTree(tmpRoot, options.trainingSet, options.categoryAttr);
// console.log(tmpRoot);
// setSecondRoot(tmpRoot);
// } else {
// rebuildTestTree(tmpRoot, testSet, options.categoryAttr);
// //console.log(tmpRoot);
// setSecondRoot(tmpRoot);
// }
// },
// [options.categoryAttr, options.trainingSet, testSet]
// );
useEffect(() => {
setRoot(null);
setIsLoading(true);
setTestSet(null);
let terminated = false;
executeAlgorithm(options)
.then(value => {
if (terminated) {
return;
}
setRoot(value);
updateTestTree(value);
})
.catch(e => console.error(e))
.finally(() => {
if (!terminated) setIsLoading(false);
terminated = true;
});
return () => {
if (terminated) {
return;
}
terminated = true;
};
}, [options, setIsLoading]);
useEffect(() => {
if (root !== null) {
setSizeTree(getSizeTree(root));
}
}, [root]);
const requestChildChange = newRoot => {
setRoot(newRoot);
updateTestTree(newRoot);
};
function handleGetTestSet({ data }) {
setTestSet(data);
}
function updateTestTree(newRoot) {
console.log(options.categoryAttr);
let tmpRoot = JSON.parse(JSON.stringify(newRoot));
if (testSet == null) {
rebuildTestTree(tmpRoot, options.trainingSet, options.categoryAttr);
console.log(tmpRoot);
setSecondRoot(tmpRoot);
} else {
rebuildTestTree(tmpRoot, testSet, options.categoryAttr);
//console.log(tmpRoot);
setSecondRoot(tmpRoot);
}
}
function handleShowTestTree(e) {
//console.log(e);
setShowTestTree(e.target.checked);
updateTestTree(root);
}
return (
<div id="tree">
<Stack spacing={5} direction="row" justifyContent="space-between" pr={5} pl={5}>
<Stack id={'trainingTree'} spacing={5} direction="row" justifyContent="space-between">
<Box>
<InputGroup size="sm">
<InputLeftAddon
children="Accuracy:"
fontWeight={700}
bg={'#ddd'}
color="#black"
borderRadius="0.375rem"
/>
<Input
value={accuracyTraining.toFixed(3)}
readOnly
textAlign="center"
width={70}
pl={1}
pr={1}
/>
<InputRightAddon
children="%"
fontWeight={700}
bg={'#ddd'}
color="#black"
borderRadius="0.375rem"
/>
</InputGroup>
</Box>
<Box>
<ConfusionMatrix
tree={root}
data={options.trainingSet}
allClasses={options.allClasses}
categoryAttr={options.categoryAttr}
onChange={setAccuracyTraining}
disabled={isLoading}
/>
</Box>
</Stack>
<Stack spacing={2} direction="row">
<Box>
<Button
leftIcon={<GrTechnology />}
bg={'#ddd'}
color="#black"
_hover={{ bg: '#aaa' }}
onClick={() => logTree(root)}
size="sm"
>
Log tree
</Button>
{/* tree size */}
</Box>
<Box>
<ButtonGroup size="sm" isAttached>
<Button mr="-px" disabled>
Joints: {sizeTree.joints}
</Button>
<Button mr="-px" disabled>
Leafs: {sizeTree.leafs}
</Button>
</ButtonGroup>
</Box>
<Box>
<TestSetFileReader onChange={handleGetTestSet} isHeaders={headers} />
</Box>
</Stack>
<Stack id={'testTree'} spacing={5} direction="row" justifyContent="space-between">
{testSet !== null ? (
<>
<Box>
<InputGroup size="sm">
<InputLeftAddon
children="Accuracy:"
fontWeight={700}
bg={'#ddd'}
color="#black"
borderRadius="0.375rem"
/>
<Input
value={accuracyTest.toFixed(3)}
readOnly
textAlign="center"
width={70}
pl={1}
pr={1}
/>
<InputRightAddon
children="%"
fontWeight={700}
bg={'#ddd'}
color="#black"
borderRadius="0.375rem"
/>
</InputGroup>
</Box>
<Box>
<ConfusionMatrix
tree={root}
data={testSet}
allClasses={options.allClasses}
categoryAttr={options.categoryAttr}
onChange={setAccuracyTest}
disabled={isLoading}
/>
</Box>
<Box>
<FormControl display="flex" alignItems="center">
<FormLabel htmlFor="show-tree" mb="0">
Show test tree
</FormLabel>
<Switch
id="show-tree-switch"
disabled={testSet !== null}
onChange={e => handleShowTestTree(e)}
/>
</FormControl>
</Box>
</>
) : (
<div></div>
)}
</Stack>
</Stack>
{isLoading && <Spinner size="xl" />}
<Box p={5}>
<h1>Tree nodes:</h1>
{!root ? (
<p>No tree to show</p>
) : (
<Box d="flex" flexDirection="row">
<Box width="100%">
Training tree
<Node node={root} onChange={() => {}} requestChildChange={requestChildChange} side={true} />
</Box>
<Box width="100%" d={showTestTree ? '' : 'none'}>
Test tree
{showTestTree ? (
<Node node={secondRoot} onChange={() => {}} side={true} isNotModify />
) : (
<div></div>
)}
</Box>
</Box>
)}
</Box>
</div>
);
};
export default Tree;