mirror of
https://github.com/hsokolowski/iTree.git
synced 2026-07-30 13:55:48 -04:00
create confusion matrix with actual accuracy
This commit is contained in:
94
src/components/ConfusionMatrix/Table.jsx
Normal file
94
src/components/ConfusionMatrix/Table.jsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Textarea,
|
||||
Box,
|
||||
ButtonGroup,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { Table, Thead, Tbody, Tfoot, Tr, Th, Td, TableCaption } from '@chakra-ui/react';
|
||||
import { predict } from '../../utils/predict';
|
||||
|
||||
/**
|
||||
* @param {Object} props
|
||||
* @param {Array[]} props.confusionMatrix
|
||||
* @param {string[]} props.headers
|
||||
*/
|
||||
function TableComponent({ confusionMatrix, headers }) {
|
||||
return (
|
||||
<Table size="sm" overflowX="auto" d="block">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th colSpan={confusionMatrix.length + 2} textAlign="center">
|
||||
Actual
|
||||
</Th>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Th></Th>
|
||||
<Th></Th>
|
||||
{headers.map(x => (
|
||||
<Th>{x}</Th>
|
||||
))}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{confusionMatrix.map((x, idx) => {
|
||||
console.log(x, idx);
|
||||
return (
|
||||
<Tr>
|
||||
{idx === 0 ? (
|
||||
<Th
|
||||
rowSpan={confusionMatrix.length}
|
||||
style={{
|
||||
writingMode: 'vertical-rl',
|
||||
textOrientation: 'mixed',
|
||||
transform: 'rotate(-90deg)',
|
||||
//padding: '2px',
|
||||
}}
|
||||
>
|
||||
Predict
|
||||
</Th>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Th>{headers[idx]}</Th>
|
||||
{x.map(y => (
|
||||
<Td>{y}</Td>
|
||||
))}
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
{/* <Tr>
|
||||
<Td
|
||||
rowSpan={confusionMatrix.length}
|
||||
style={{
|
||||
writingMode: 'vertical-rl',
|
||||
textOrientation: 'revert',
|
||||
}}
|
||||
>
|
||||
Predict
|
||||
</Td>
|
||||
<Td>millimetres (mm)</Td>
|
||||
<Td>25.4</Td>
|
||||
</Tr> */}
|
||||
</Tbody>
|
||||
{/* <Tfoot mt={2}>
|
||||
<Tr fontSize={20}>
|
||||
<Th></Th>
|
||||
<Th></Th>
|
||||
<Tr>Accuracy:</Tr>
|
||||
<Tr>{acc.toFixed(3)}%</Tr>
|
||||
</Tr>
|
||||
</Tfoot> */}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default TableComponent;
|
||||
123
src/components/ConfusionMatrix/index.jsx
Normal file
123
src/components/ConfusionMatrix/index.jsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Textarea,
|
||||
Box,
|
||||
ButtonGroup,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { predict } from '../../utils/predict';
|
||||
import TableComponent from './Table';
|
||||
|
||||
/**
|
||||
* @param {Object} props
|
||||
* @param {Object} props.tree
|
||||
* @param {Function} props.onChange
|
||||
* @param {Object[]} props.data
|
||||
* @param {string[]} props.allClasses
|
||||
* @param {string} props.categoryAttr
|
||||
* @param {boolean} props.disabled
|
||||
*/
|
||||
function ConfusionMatrix({ tree, onChange, data, allClasses, categoryAttr, disabled }) {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [confusionMatrix, setConfusionMatrix] = useState([]);
|
||||
const [accuracy, setAccuracy] = useState(0);
|
||||
|
||||
function handleAccuracy(confusionMatrix) {
|
||||
if (confusionMatrix.length > 0) {
|
||||
let allTargets = 0;
|
||||
let goodTargets = 0;
|
||||
for (let i = 0; i < confusionMatrix[0].length; i++) {
|
||||
for (let j = 0; j < confusionMatrix[0].length; j++) {
|
||||
if (i === j) {
|
||||
goodTargets += confusionMatrix[i][j];
|
||||
}
|
||||
allTargets += confusionMatrix[i][j];
|
||||
}
|
||||
}
|
||||
let acc = (goodTargets / allTargets) * 100;
|
||||
console.log(acc);
|
||||
return acc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//console.log(allClasses);
|
||||
let CM = buildArray(allClasses.length);
|
||||
// console.log(CM);
|
||||
// console.log(categoryAttr);
|
||||
// console.log(tree);
|
||||
// console.log(data);
|
||||
|
||||
if (!disabled && tree !== null) {
|
||||
let prediction, clazz;
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
prediction = predict(tree, data[index]);
|
||||
clazz = data[index][categoryAttr];
|
||||
//console.log(prediction, clazz);
|
||||
|
||||
if (prediction === clazz) {
|
||||
CM[allClasses.indexOf(clazz)][allClasses.indexOf(clazz)]++;
|
||||
} else {
|
||||
CM[allClasses.indexOf(clazz)][allClasses.indexOf(prediction)]++;
|
||||
}
|
||||
}
|
||||
setConfusionMatrix(CM);
|
||||
let tmpAccuracy = handleAccuracy(CM);
|
||||
setAccuracy(tmpAccuracy);
|
||||
onChange(tmpAccuracy);
|
||||
//console.log(CM);
|
||||
}
|
||||
}, [tree, disabled, allClasses, categoryAttr, data]);
|
||||
|
||||
function buildArray(lenght) {
|
||||
let arr = [];
|
||||
for (var x = 0; x < lenght; x++) {
|
||||
arr[x] = [];
|
||||
for (var y = 0; y < lenght; y++) {
|
||||
arr[x][y] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
bg={'#ddd'}
|
||||
color="#black"
|
||||
_hover={{ bg: '#aaa' }}
|
||||
onClick={onOpen}
|
||||
size="sm"
|
||||
disabled={disabled}
|
||||
>
|
||||
Confusion Matrix
|
||||
</Button>
|
||||
|
||||
<Modal onClose={onClose} size={'xl'} isOpen={isOpen} blockScrollOnMount={false}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Confusion Matrix</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody p={5} display="flex" justifyContent="center">
|
||||
<TableComponent headers={allClasses} confusionMatrix={confusionMatrix} />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button>Accuracy: {accuracy.toFixed(3)}%</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConfusionMatrix;
|
||||
@@ -24,6 +24,7 @@ function DataViewer({ node, side, onChange, hide }) {
|
||||
const finalRef = React.useRef();
|
||||
const scrollBehavior = 'inside';
|
||||
const sideSubTitle = side ? 'Matched' : 'Not Matched';
|
||||
const nodeSize = node.nodeSet ? node.nodeSet.length : node.trainingSet2.length;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -48,17 +49,9 @@ function DataViewer({ node, side, onChange, hide }) {
|
||||
<Tooltip hasArrow label="View data in node" bg="purple.600" placement="right">
|
||||
<Button
|
||||
mt={1}
|
||||
// px={2}
|
||||
// py={3}
|
||||
// h={26}
|
||||
colorScheme="facebook"
|
||||
border={'1px solid #333'}
|
||||
// bg={'#ddd'}
|
||||
// color="black"
|
||||
// _hover={{
|
||||
// bg: '#333',
|
||||
// color: 'white',
|
||||
// }}
|
||||
disabled={nodeSize ? false : true}
|
||||
onClick={onOpen}
|
||||
leftIcon={<CgDatabase />}
|
||||
variant="outline"
|
||||
@@ -66,7 +59,7 @@ function DataViewer({ node, side, onChange, hide }) {
|
||||
fontSize="14px"
|
||||
fontWeight="semibold"
|
||||
>
|
||||
{sideSubTitle} ({node.nodeSet ? node.nodeSet.length : node.trainingSet2.length})
|
||||
{sideSubTitle} ({nodeSize})
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useLoadingContext } from '../contexts/LoadingContext';
|
||||
import { executeAlgorithm } from '../utils/algorithm-executor';
|
||||
import TestSetFileReader from './TestSetFileReader';
|
||||
import Predicter from './Predicter';
|
||||
import ConfusionMatrix from './ConfusionMatrix';
|
||||
|
||||
/**
|
||||
* @typedef {import('../utils/decision-tree.js').DecisionTreeBuilder} DecisionTreeBuilder
|
||||
@@ -68,7 +69,7 @@ const Tree = ({ options }) => {
|
||||
|
||||
useEffect(() => {
|
||||
logTree(root);
|
||||
setAccuracy(Math.random() * 10);
|
||||
//setAccuracy(Math.random() * 10);
|
||||
}, [root]);
|
||||
|
||||
const requestChildChange = newRoot => setRoot(newRoot);
|
||||
@@ -93,7 +94,7 @@ const Tree = ({ options }) => {
|
||||
color="#black"
|
||||
borderRadius="0.375rem"
|
||||
/>
|
||||
<Input value={accuracy} readOnly textAlign="center" />
|
||||
<Input value={accuracy.toFixed(3)} readOnly textAlign="center" />
|
||||
<InputRightAddon
|
||||
children="%"
|
||||
fontWeight={700}
|
||||
@@ -104,7 +105,7 @@ const Tree = ({ options }) => {
|
||||
</InputGroup>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
{/* <Button
|
||||
bg={'#ddd'}
|
||||
color="#black"
|
||||
_hover={{ bg: '#aaa' }}
|
||||
@@ -112,7 +113,15 @@ const Tree = ({ options }) => {
|
||||
size="sm"
|
||||
>
|
||||
Confusion Matrix
|
||||
</Button>
|
||||
</Button> */}
|
||||
<ConfusionMatrix
|
||||
tree={root}
|
||||
data={options.trainingSet}
|
||||
allClasses={options.allClasses}
|
||||
categoryAttr={options.categoryAttr}
|
||||
onChange={setAccuracy}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
export function predict(tree, item) {
|
||||
var attr1, attr2, value, predicate, pivot, match;
|
||||
|
||||
// Traversing tree from the root to leaf
|
||||
while (true) {
|
||||
if (tree.category) {
|
||||
if (tree?.category) {
|
||||
// only leafs contains predicted category
|
||||
return tree.category;
|
||||
}
|
||||
console.log(
|
||||
tree.predicateName,
|
||||
tree.weight,
|
||||
tree.predicateName === '>=',
|
||||
tree.predicateName === '==',
|
||||
tree.predicateName === '<'
|
||||
);
|
||||
// console.log(
|
||||
// tree.predicateName,
|
||||
// tree.weight,
|
||||
// tree.predicateName === '>=',
|
||||
// tree.predicateName === '==',
|
||||
// tree.predicateName === '<'
|
||||
// );
|
||||
if (tree.weight) {
|
||||
attr1 = tree.attr2;
|
||||
attr2 = tree.pivot;
|
||||
@@ -23,7 +22,7 @@ export function predict(tree, item) {
|
||||
predicate = predicates['w'];
|
||||
match = predicate(value, pivot, tree.weight);
|
||||
|
||||
console.log('predict - waga', match);
|
||||
// console.log('predict - waga', match);
|
||||
}
|
||||
if (tree.predicateName === '>=' || tree.predicateName === '==') {
|
||||
attr1 = tree.attr2;
|
||||
@@ -32,7 +31,7 @@ export function predict(tree, item) {
|
||||
predicate = predicates[tree.predicateName];
|
||||
match = predicate(value, pivot);
|
||||
|
||||
console.log('predict - c45', match);
|
||||
// console.log('predict - c45', match);
|
||||
}
|
||||
if (tree.predicateName === '<') {
|
||||
attr1 = tree.attr2;
|
||||
@@ -43,7 +42,7 @@ export function predict(tree, item) {
|
||||
predicate = predicates[tree.predicateName];
|
||||
match = predicate(value, pivot);
|
||||
|
||||
console.log('predict - tsp', match);
|
||||
// console.log('predict - tsp', match);
|
||||
}
|
||||
|
||||
// move to one of subtrees
|
||||
|
||||
Reference in New Issue
Block a user