From 626119e26002d505ffccc9170c9244499ae1de21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Soko=C5=82owski?= Date: Sun, 18 Apr 2021 18:52:33 +0200 Subject: [PATCH] new layout --- jsconfig.json | 2 +- public/algorithms/tsp-tree.js | 133 ++++++++------------ src/components/Node/Configurator/TabC45.jsx | 12 +- src/components/Node/DataViewer/index.jsx | 66 ++++++++++ src/components/Node/Joint.jsx | 58 +++------ src/components/Node/Leaf.jsx | 6 +- src/components/Node/ModalPopup.jsx | 2 +- src/components/Node/index.jsx | 65 ++++++---- src/components/Tree.jsx | 2 +- src/css/main.scss | 12 ++ src/utils/algorithm-executor.js | 4 + 11 files changed, 210 insertions(+), 152 deletions(-) create mode 100644 src/components/Node/DataViewer/index.jsx diff --git a/jsconfig.json b/jsconfig.json index d1d79e7..58bd9c5 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -3,6 +3,6 @@ "checkJs": true, "jsx": "react", "allowSyntheticDefaultImports": true, - "lib": ["webworker"] + "lib": ["es2019"] } } diff --git a/public/algorithms/tsp-tree.js b/public/algorithms/tsp-tree.js index 6e1bb08..f58172a 100644 --- a/public/algorithms/tsp-tree.js +++ b/public/algorithms/tsp-tree.js @@ -33,40 +33,32 @@ function buildDecisionTreeTSP( maxTreeDepth, ignoredAttributes, } = builder; - //console.log("########## NOWY WEZEL ########", trainingSet.length); + /** @type {string | number} */ var _quality = 0; + + // LEAF if (maxTreeDepth === 0 || trainingSet.length <= minItemsCount) { - console.log('LISC BO MAX TREE DEPTH', maxTreeDepth, 'LISC ILOSC', trainingSet.length); - //gger; + console.log('Liść bo maxTreeDepth:', maxTreeDepth, ' Ilość elementów:', trainingSet.length); let _category = mostFrequentValue(trainingSet, categoryAttr); let _positiveCounter = 0; - //console.log("KATEGORIA JAKO:", _category); - trainingSet.forEach(element => { + for (let element of trainingSet) { if (element[categoryAttr] == _category) _positiveCounter++; - }); + } let _negativeCounter = trainingSet.length - _positiveCounter; _quality = _positiveCounter / trainingSet.length; _quality = _quality * 100; - _quality = _quality.toFixed(2); - //ugger; + return { category: _category, - quality: _quality, + quality: _quality.toFixed(2), matchedCount: _positiveCounter, notMatchedCount: _negativeCounter, trainingSet2: trainingSet, }; } - var attributes = builder.allAttributes.filter(function (el) { - return ![...ignoredAttributes, categoryAttr].includes(el); - }); - //console.log(builder.minItemsCount, builder.trainingSet.length); + var attributes = builder.allAttributes.filter(el => el !== categoryAttr && !ignoredAttributes.includes(el)); - // tu juz musi byc przekazana cm wyzerowana - - var podzial = []; - //console.log(attributes); var right = 0, left = 0; var maxDif = 100; @@ -83,31 +75,25 @@ function buildDecisionTreeTSP( notMatch = []; //######################### - //# tu gdy zmieniamy # + //# force changes # //######################### if (isChanged) { - right = left = 0; - leftList = []; - rightList = []; - classMatrix = [ - new Array(builder.allClasses.length).fill(0), - new Array(builder.allClasses.length).fill(0), - ]; - - for (let index = 0; index < trainingSet.length; index++) { - const element = trainingSet[index]; + // division + for (let element of trainingSet) { + const attribute = element[categoryAttr]; if (element[changedAttribute1] < element[changedAttribute2]) { left++; leftList.push(element); - classMatrix[0][builder.allClasses.indexOf(element[categoryAttr])]++; + classMatrix[0][builder.allClasses.indexOf(attribute)]++; } else { right++; rightList.push(element); - classMatrix[1][builder.allClasses.indexOf(element[categoryAttr])]++; + classMatrix[1][builder.allClasses.indexOf(attribute)]++; } } - //console.log(classMatrix); + + // probability var probR = 0, probL = 0, rankL = 0, @@ -119,28 +105,22 @@ function buildDecisionTreeTSP( rankL += probL * probL; rankR += probR * probR; } - //console.log("Rank Lewy",rankL,"Rank Prawy",rankR); + // setting new values var currentDif = (right / trainingSet.length) * (1 - rankR) + (left / trainingSet.length) * (1 - rankL); if (currentDif < maxDif) { - //console.log("------Zapisanie maxDif-------"); - //console.log(attr1,attr2); - //console.log("R/L ", right + ":" + left); - //console.log("cur/mD",currentDif + ":" + maxDif); maxDif = currentDif; attribute1 = changedAttribute1; attribute2 = changedAttribute2; match = leftList; notMatch = rightList; - podzial = classMatrix; - //console.log("-----------------------------"); + //podzial = classMatrix; } } else { - for (let i = 0; i < attributes.length; i++) { - let attr1 = attributes[i]; - for (let j = 0; j < attributes.length; j++) { - let attr2 = attributes[j]; + for (let attr1 of attributes) { + for (let attr2 of attributes) { if (attr1 !== attr2) { + console.log(attr1, attr2); right = left = 0; leftList = []; rightList = []; @@ -149,20 +129,22 @@ function buildDecisionTreeTSP( new Array(builder.allClasses.length).fill(0), ]; - for (let index = 0; index < trainingSet.length; index++) { - const element = trainingSet[index]; + // division + for (let element of trainingSet) { + const attribute = element[categoryAttr]; if (element[attr1] < element[attr2]) { left++; leftList.push(element); - classMatrix[0][builder.allClasses.indexOf(element[categoryAttr])]++; + classMatrix[0][builder.allClasses.indexOf(attribute)]++; } else { right++; rightList.push(element); - classMatrix[1][builder.allClasses.indexOf(element[categoryAttr])]++; + classMatrix[1][builder.allClasses.indexOf(attribute)]++; } } - //console.log(classMatrix); + + // probability var probR = 0, probL = 0, rankL = 0, @@ -174,74 +156,60 @@ function buildDecisionTreeTSP( rankL += probL * probL; rankR += probR * probR; } - //console.log("Rank Lewy",rankL,"Rank Prawy",rankR); + // setting new values var currentDif = (right / trainingSet.length) * (1 - rankR) + (left / trainingSet.length) * (1 - rankL); + console.log('left', left, 'right', right, 'currentDiff', currentDif); if (currentDif < maxDif) { - //console.log("------Zapisanie maxDif-------"); - //console.log(attr1,attr2); - //console.log("R/L ", right + ":" + left); - //console.log("cur/mD",currentDif + ":" + maxDif); maxDif = currentDif; attribute1 = attr1; attribute2 = attr2; match = leftList; notMatch = rightList; - podzial = classMatrix; - //console.log("-----------------------------"); + //podzial = classMatrix; } } } } } - //console.log("PO WYLICZENIU NAJLEPSZEGO"); - //console.log(attribute1, attribute2); - //console.log("L/R ", match.length + ":" + notMatch.length); - //console.log(podzial); - console.log('MaxDifference:', maxDif); + // LEAF if (!maxDif) { - //console.log("LISC BO MAX DIF ZERO", trainingSet.length); + console.log('Liść bo maxDif:', maxDif); let _category = mostFrequentValue(trainingSet, categoryAttr); let _positiveCounter = 0; - //console.log("KATEGORIA JAKO:", _category); - trainingSet.forEach(element => { + for (let element of trainingSet) { if (element[categoryAttr] == _category) _positiveCounter++; - }); + } let _negativeCounter = trainingSet.length - _positiveCounter; _quality = _positiveCounter / trainingSet.length; _quality = _quality * 100; - _quality = _quality.toFixed(2); return { category: _category, - quality: _quality, + quality: _quality.toFixed(2), matchedCount: _positiveCounter, notMatchedCount: _negativeCounter, trainingSet2: trainingSet, }; } - // sprawdzic - // wssytskies stringi do ignored + + //LEAF if (match.length === 0 || notMatch.length === 0) { - console.log('LISC BO JEDNA ZE STRON MA 0'); + console.log('Liść bo Lewa/Prawa wynosi 0'); let _category = mostFrequentValue(trainingSet, categoryAttr); let _positiveCounter = 0; - //console.log(_category); - trainingSet.forEach(element => { + for (let element of trainingSet) { if (element[categoryAttr] == _category) _positiveCounter++; - }); + } let _negativeCounter = trainingSet.length - _positiveCounter; _quality = _positiveCounter / trainingSet.length; _quality = _quality * 100; - _quality = _quality.toFixed(2); - // restriction by maximal depth of tree - // or size of training set is to small - // so we have to terminate process of building tree + return { category: _category, - quality: _quality, + quality: _quality.toFixed(2), matchedCount: _positiveCounter, notMatchedCount: _negativeCounter, trainingSet2: trainingSet, @@ -254,6 +222,7 @@ function buildDecisionTreeTSP( builder.trainingSet = notMatch; var notMatchSubTree = buildDecisionTreeTSP(builder); + console.log('TUTAJ'); return { attr2: attribute2, @@ -265,17 +234,17 @@ function buildDecisionTreeTSP( notMatchedCount: notMatch.length, nodeSet: match.concat(notMatch), }; - //console.log(attributes); } function countUniqueValues(items, attr) { - var counter = {}; + ////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; - } + //// for (var i = items.length - 1; i >= 0; i--) { + //// // items[i][attr] - value of attribute + //// counter[items[i][attr]] = 0; + //// } + var counter = Object.fromEntries(items.map(item => [item[attr], 0])); // counting number of occurrences of each of values // of attribute diff --git a/src/components/Node/Configurator/TabC45.jsx b/src/components/Node/Configurator/TabC45.jsx index 8784f78..dbb9882 100644 --- a/src/components/Node/Configurator/TabC45.jsx +++ b/src/components/Node/Configurator/TabC45.jsx @@ -1,6 +1,8 @@ import React from 'react'; import { Input, Stack } from '@chakra-ui/react'; -import { ChevronLeftIcon } from '@chakra-ui/icons'; +import { ChevronRightIcon } from '@chakra-ui/icons'; +import { CgMathEqual } from 'react-icons/cg'; +import { IconContext } from 'react-icons'; import { Search as SearchBar } from '../../SearchBar'; import './alg-style.scss'; import { useAttributesContext } from '../../../contexts/AttributesContext'; @@ -22,7 +24,13 @@ export default function TabC45({ attribute, value, changeValues }) { closeOnSelect={true} /> {/* */} - + + + + + + + ); diff --git a/src/components/Node/DataViewer/index.jsx b/src/components/Node/DataViewer/index.jsx new file mode 100644 index 0000000..d899b53 --- /dev/null +++ b/src/components/Node/DataViewer/index.jsx @@ -0,0 +1,66 @@ +import React, { useState } from 'react'; +import { + Box, + Button, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + useDisclosure, +} from '@chakra-ui/react'; +import { CgDatabase } from 'react-icons/cg'; + +function DataViewer({ node, side }) { + const { isOpen, onOpen, onClose } = useDisclosure(); + const finalRef = React.useRef(); + const scrollBehavior = 'inside'; + const sideSubTitle = side ? 'Matched' : 'Not Matched'; + + return ( + + + + + + Data Viewer [ {sideSubTitle} ] + + + + + + + + + + + ); +} + +export default DataViewer; diff --git a/src/components/Node/Joint.jsx b/src/components/Node/Joint.jsx index 16da378..3768114 100644 --- a/src/components/Node/Joint.jsx +++ b/src/components/Node/Joint.jsx @@ -2,6 +2,7 @@ import { Badge, Box, Button, + ButtonGroup, Modal, ModalBody, ModalCloseButton, @@ -31,21 +32,31 @@ function Joint({ children, attr2, predicateName, pivot, weight, requestFoldToLea const foldToLeaf = () => requestFoldToLeaf(); return ( - - + + + + + {/* {attr2} {predicateName} {weight} {pivot} - - + */} - {/* - - - - {attr2} {predicateName} {pivot} - - - - Text ( kolejny komponent z konfiguratorem){' '} - {nodeSet.map(x => ( -
- {x.attr1001} {x[attr2]}-{x[pivot]} {x[attr2] < x[pivot] ? 'Match' : 'NotMatch'} -
- ))} - -
- - - -
-
*/} - {/*
- Match - {JSON.stringify(match)} -
-
- notMatch - {JSON.stringify(notMatch)} -
*/} {children}
); diff --git a/src/components/Node/Leaf.jsx b/src/components/Node/Leaf.jsx index 15fc52c..7959378 100644 --- a/src/components/Node/Leaf.jsx +++ b/src/components/Node/Leaf.jsx @@ -7,12 +7,14 @@ function Leaf({ category, matchedCount, notMatchedCount, quality, requestLeafUnf + diff --git a/src/components/Node/index.jsx b/src/components/Node/index.jsx index e4113ef..39aa4c1 100644 --- a/src/components/Node/index.jsx +++ b/src/components/Node/index.jsx @@ -1,7 +1,8 @@ -import { Spinner } from '@chakra-ui/react'; +import { Box, Spinner, useDisclosure } from '@chakra-ui/react'; import React, { useEffect, useMemo, useState } from 'react'; import { useBuilderConfigContext } from '../../contexts/BuilderConfigContext'; import { executeAlgorithm, mostFrequentValue } from '../../utils/algorithm-executor'; +import DataViewer from './DataViewer'; //import { addNode } from "./utils"; import Joint from './Joint'; @@ -12,6 +13,7 @@ const Node = props => { const { builderConfig } = useBuilderConfigContext(); const [highlighted, setHighlighted] = useState(false); const [loading, setLoading] = useState(false); + const [side, setSide] = useState(props.side); const [node, setNode] = useState(props.node || {}); useEffect(() => setNode(props.node || {}), [props.node, setNode]); const { @@ -118,30 +120,43 @@ const Node = props => { return (
- {category ? ( - - ) : ( - - - - - )} + + + {category ? ( + + ) : ( + + + + + )} +
); }; diff --git a/src/components/Tree.jsx b/src/components/Tree.jsx index 01ae8e2..66da8e8 100644 --- a/src/components/Tree.jsx +++ b/src/components/Tree.jsx @@ -69,7 +69,7 @@ const Tree = ({ options }) => { {!root ? (

No tree to show

) : ( - {}} requestChildChange={requestChildChange} /> + {}} requestChildChange={requestChildChange} side={true} /> )}
diff --git a/src/css/main.scss b/src/css/main.scss index 1df92e1..d8b7f84 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -39,12 +39,24 @@ $gradient-radial: radial-gradient(#d8dbe2ff, #a9bcd0ff, #58a4b0ff, #373f51ff, #1 transition: all 0.5s; background: white; color: #333; + border-left: 1px dashed black; // width: 50%; } +.node:first-of-type { + border-left: 1px solid transparent; +} .node.highlight { background: #09f; color: white; } +.tree-branch { + position: relative; + &::before { + content: '--'; + position: absolute; + left: -14px; + } +} #tree { text-align: left; diff --git a/src/utils/algorithm-executor.js b/src/utils/algorithm-executor.js index 0babc53..ee64701 100644 --- a/src/utils/algorithm-executor.js +++ b/src/utils/algorithm-executor.js @@ -7,6 +7,7 @@ const workersMap = { export function executeAlgorithm(options, changeOptions = {}) { console.log(options); return new Promise((resolve, reject) => { + console.time(options.algorithm); const worker = new Worker(`algorithms/${workersMap[options.algorithm]}-tree.js`); worker.onmessage = ({ data }) => { console.log('got a result', data); @@ -15,6 +16,9 @@ export function executeAlgorithm(options, changeOptions = {}) { }; worker.onerror = reject; worker.postMessage({ _builder: { ...options }, ...changeOptions }); + }).then(data => { + console.timeEnd(options.algorithm); + return data; }); }