Folding and expanding leaves

This commit is contained in:
Hubert Sokołowski
2021-04-10 23:36:41 +02:00
parent f7e2cd47e7
commit 7bccb2c8b1
8 changed files with 111 additions and 17 deletions

View File

@@ -55,7 +55,7 @@ function buildDecisionTreeC45(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
//console.log(categoryAttr);
@@ -81,7 +81,7 @@ function buildDecisionTreeC45(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
@@ -260,7 +260,7 @@ function buildDecisionTreeC45(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}

View File

@@ -55,7 +55,7 @@ function buildDecisionTreeTSP(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
var attributes = builder.allAttributes.filter(function (el) {
@@ -219,7 +219,7 @@ function buildDecisionTreeTSP(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
// sprawdzic
@@ -244,7 +244,7 @@ function buildDecisionTreeTSP(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}

View File

@@ -17,7 +17,7 @@
* @param {boolean} isChanged
*/
//TSP-weight
export function buildDecisionTreeTSPW(
function buildDecisionTreeTSPW(
_builder,
isChanged = false,
changedAttribute1 = null,
@@ -52,7 +52,7 @@ export function buildDecisionTreeTSPW(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
var attributes = builder.allAttributes.filter(function (el) {
@@ -217,7 +217,7 @@ export function buildDecisionTreeTSPW(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}
// sprawdzic
@@ -242,7 +242,7 @@ export function buildDecisionTreeTSPW(
quality: _quality,
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: trainingSet.map(x => x[categoryAttr]),
trainingSet2: trainingSet,
};
}

View File

@@ -11,11 +11,12 @@ import {
ModalOverlay,
useDisclosure,
} from '@chakra-ui/react';
import { CloseIcon } from '@chakra-ui/icons';
import React, { useState } from 'react';
import Configurator from './Configurator';
import ModalPopup from './ModalPopup';
function Joint({ children, attr2, predicateName, pivot, match, notMatch, onChange, nodeSet }) {
function Joint({ children, attr2, predicateName, pivot, weight, requestFoldToLeaf, onChange, nodeSet }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const [size, setSize] = useState('lg');
@@ -26,6 +27,9 @@ function Joint({ children, attr2, predicateName, pivot, match, notMatch, onChang
const buildNewNode = () => {
onChange();
};
const foldToLeaf = () => requestFoldToLeaf();
return (
<Box>
<Badge
@@ -35,9 +39,13 @@ function Joint({ children, attr2, predicateName, pivot, match, notMatch, onChang
colorScheme="teal"
onClick={handleOpenModalClick}
className={'badge'}
marginRight={3}
>
{attr2} <b>{predicateName}</b> {pivot}
{attr2} <b>{predicateName}</b> {weight} {pivot}
</Badge>
<Button size="xs" rightIcon={<CloseIcon />} onClick={foldToLeaf}>
Fold
</Button>
<ModalPopup
isOpen={isOpen}
nodeSet={nodeSet}
@@ -47,6 +55,7 @@ function Joint({ children, attr2, predicateName, pivot, match, notMatch, onChang
onChange={onChange}
onClose={onClose}
onOpen={onOpen}
weight={weight}
/>
{/* <Modal onClose={onClose} size={size} isOpen={isOpen}>
<ModalOverlay />

View File

@@ -1,7 +1,8 @@
import { Box } from '@chakra-ui/react';
import { Box, Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react';
import React from 'react';
import { ChevronDownIcon, DownloadIcon } from '@chakra-ui/icons';
function Leaf({ category, matchedCount, notMatchedCount, quality }) {
function Leaf({ category, matchedCount, notMatchedCount, quality, requestLeafUnfold }) {
return (
<Box
d="flex"
@@ -36,6 +37,16 @@ function Leaf({ category, matchedCount, notMatchedCount, quality }) {
<Box mt="1" ml="2" fontWeight="semibold" as="h4" lineHeight="tight" isTruncated>
{quality}%
</Box>
<Menu closeOnSelect closeOnBlur>
<MenuButton fontSize="sm" w={20} h={6} as={Button} rightIcon={<ChevronDownIcon />}>
Unfold
</MenuButton>
<MenuList>
<MenuItem onClick={() => requestLeafUnfold('c45')}>C 4.5</MenuItem>
<MenuItem onClick={() => requestLeafUnfold('tsp')}>TSP</MenuItem>
<MenuItem onClick={() => requestLeafUnfold('tspw')}>TSP Weight</MenuItem>
</MenuList>
</Menu>
</Box>
);
}

View File

@@ -11,7 +11,7 @@ import {
} from '@chakra-ui/react';
import Configurator from './Configurator';
function ModalPopup({ attr2, predicateName, pivot, isOpen, nodeSet, onOpen, onChange, onClose }) {
function ModalPopup({ attr2, predicateName, pivot, weight, isOpen, nodeSet, onOpen, onChange, onClose }) {
const handleOnChange = value => {
onClose();
onChange(value);
@@ -32,7 +32,7 @@ function ModalPopup({ attr2, predicateName, pivot, isOpen, nodeSet, onOpen, onCh
{x.attr1001} {x[attr2]}-{x[pivot]} {x[attr2] < x[pivot] ? 'Match' : 'NotMatch'}
</div>
))} */}
<Configurator onChange={handleOnChange} attribute={attr2} pivot={pivot} />
<Configurator onChange={handleOnChange} attribute={attr2} pivot={pivot} weight={weight} />
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Close</Button>

View File

@@ -1,7 +1,7 @@
import { Spinner } from '@chakra-ui/react';
import React, { useEffect, useMemo, useState } from 'react';
import { useBuilderConfigContext } from '../../contexts/BuilderConfigContext';
import { executeAlgorithm } from '../../utils/algorithm-executor';
import { executeAlgorithm, mostFrequentValue } from '../../utils/algorithm-executor';
//import { addNode } from "./utils";
import Joint from './Joint';
@@ -25,9 +25,13 @@ const Node = props => {
predicateName,
pivot,
nodeSet,
weight,
} = node;
const onNodeClicked = e => {
if (!e.target.classList.contains('node')) {
return;
}
//console.log(e);
e.stopPropagation();
console.log('Node clicked', node);
@@ -85,6 +89,29 @@ const Node = props => {
props.requestChildChange(targetNode);
};
const foldJointToLeaf = () => {
const foldResult = mostFrequentValue(nodeSet, builderConfig.categoryAttr);
setNode(foldResult);
props.requestChildChange(foldResult);
};
const unfoldLeaf = algorithm => {
setLoading(true);
executeAlgorithm({
...builderConfig,
trainingSet: node.trainingSet2,
algorithm,
})
.then(value => {
setNode(value);
props.requestChildChange(value);
})
.catch(e => console.error(e))
.finally(() => {
setLoading(false);
});
};
if (loading) {
return <Spinner size="xl" />;
}
@@ -97,6 +124,7 @@ const Node = props => {
matchedCount={matchedCount}
notMatchedCount={notMatchedCount}
quality={quality}
requestLeafUnfold={unfoldLeaf}
/>
) : (
<Joint
@@ -106,7 +134,9 @@ const Node = props => {
match={match}
notMatch={notMatch}
onChange={onChange}
requestFoldToLeaf={foldJointToLeaf}
nodeSet={nodeSet}
weight={weight}
>
<Node node={match} onChange={onChange} requestChildChange={requestChildChangeIfMatchIs(true)} />
<Node node={notMatch} onChange={onChange} requestChildChange={requestChildChangeIfMatchIs(false)} />

View File

@@ -17,3 +17,47 @@ export function executeAlgorithm(options, changeOptions = {}) {
worker.postMessage({ _builder: { ...options }, ...changeOptions });
});
}
function countUniqueValues(items, attr) {
var counter = {};
// detecting different values of attribute
for (var i = items.length - 1; i >= 0; i--) {
// items[i][attr] - value of attribute
counter[items[i][attr]] = 0;
}
// counting number of occurrences of each of values
// of attribute
for (var j = items.length - 1; j >= 0; j--) {
counter[items[j][attr]] += 1;
}
return counter;
}
export function mostFrequentValue(items, attr) {
const counter = countUniqueValues(items, attr);
var mostFrequentCount = 0;
var mostFrequentValue;
for (var value in counter) {
if (counter[value] > mostFrequentCount) {
mostFrequentCount = counter[value];
mostFrequentValue = value;
}
}
const _positiveCounter = items.filter(element => element[attr] === mostFrequentValue).length;
const _negativeCounter = items.length - _positiveCounter;
const _quality = 100 * (_positiveCounter / items.length);
return {
category: mostFrequentValue,
quality: _quality.toFixed(2),
matchedCount: _positiveCounter,
notMatchedCount: _negativeCounter,
trainingSet2: items,
};
}