diff --git a/public/algorithms/c45-tree.js b/public/algorithms/c45-tree.js index b06ad35..25aef5f 100644 --- a/public/algorithms/c45-tree.js +++ b/public/algorithms/c45-tree.js @@ -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, }; } diff --git a/public/algorithms/tsp-tree.js b/public/algorithms/tsp-tree.js index b047393..6e1bb08 100644 --- a/public/algorithms/tsp-tree.js +++ b/public/algorithms/tsp-tree.js @@ -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, }; } diff --git a/public/algorithms/tsp-weight-tree.js b/public/algorithms/tsp-weight-tree.js index 728052c..8d49e22 100644 --- a/public/algorithms/tsp-weight-tree.js +++ b/public/algorithms/tsp-weight-tree.js @@ -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, }; } diff --git a/src/components/Node/Joint.jsx b/src/components/Node/Joint.jsx index 4961af2..16da378 100644 --- a/src/components/Node/Joint.jsx +++ b/src/components/Node/Joint.jsx @@ -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 ( - {attr2} {predicateName} {pivot} + {attr2} {predicateName} {weight} {pivot} + {/* diff --git a/src/components/Node/Leaf.jsx b/src/components/Node/Leaf.jsx index d077d87..5616849 100644 --- a/src/components/Node/Leaf.jsx +++ b/src/components/Node/Leaf.jsx @@ -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 ( {quality}% + + }> + Unfold + + + requestLeafUnfold('c45')}>C 4.5 + requestLeafUnfold('tsp')}>TSP + requestLeafUnfold('tspw')}>TSP Weight + + ); } diff --git a/src/components/Node/ModalPopup.jsx b/src/components/Node/ModalPopup.jsx index 9ebf914..0aa8888 100644 --- a/src/components/Node/ModalPopup.jsx +++ b/src/components/Node/ModalPopup.jsx @@ -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'} ))} */} - + diff --git a/src/components/Node/index.jsx b/src/components/Node/index.jsx index 22346ff..e4113ef 100644 --- a/src/components/Node/index.jsx +++ b/src/components/Node/index.jsx @@ -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 ; } @@ -97,6 +124,7 @@ const Node = props => { matchedCount={matchedCount} notMatchedCount={notMatchedCount} quality={quality} + requestLeafUnfold={unfoldLeaf} /> ) : ( { match={match} notMatch={notMatch} onChange={onChange} + requestFoldToLeaf={foldJointToLeaf} nodeSet={nodeSet} + weight={weight} > diff --git a/src/utils/algorithm-executor.js b/src/utils/algorithm-executor.js index 332e0d1..0babc53 100644 --- a/src/utils/algorithm-executor.js +++ b/src/utils/algorithm-executor.js @@ -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, + }; +}