new layout

This commit is contained in:
Hubert Sokołowski
2021-04-18 18:52:33 +02:00
parent 8bc691fb53
commit 626119e260
11 changed files with 210 additions and 152 deletions

View File

@@ -3,6 +3,6 @@
"checkJs": true,
"jsx": "react",
"allowSyntheticDefaultImports": true,
"lib": ["webworker"]
"lib": ["es2019"]
}
}

View File

@@ -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

View File

@@ -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}
/>
{/* </div> */}
<ChevronLeftIcon w={10} h={10} />
<IconContext.Provider value={{ style: { height: 10 } }}>
<Stack direction="row" spacing={0}>
<ChevronRightIcon w={10} h={10} />
</Stack>
</IconContext.Provider>
<Input value={value} size="md" name="c45-value" onChange={onPivotChange} />
</Stack>
);

View File

@@ -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 (
<Box>
<Button
mt={1}
px={2}
py={3}
onClick={onOpen}
leftIcon={<CgDatabase />}
colorScheme="teal"
variant="solid"
size="sm"
fontSize="14px"
fontWeight="semibold"
>
{/* View Data */}
{/* <CgDatabase /> */}
{sideSubTitle}
</Button>
<Modal
finalFocusRef={finalRef}
isOpen={isOpen}
onClose={onClose}
size="xl"
scrollBehavior={scrollBehavior}
blockScrollOnMount={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Data Viewer [ {sideSubTitle} ]</ModalHeader>
<ModalCloseButton />
<ModalBody></ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
<Button variant="ghost">Secondary Action</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Box>
);
}
export default DataViewer;

View File

@@ -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 (
<Box>
<Badge
borderRadius="full"
px="2"
py="1"
colorScheme="teal"
<Box mt={1}>
<ButtonGroup size="md" isAttached variant="solid" className="tree-branch">
<Button size="xs" colorScheme="green" onClick={handleOpenModalClick}>
{attr2} <b>{predicateName}</b> {weight} {pivot}
</Button>
<Button size="xs" borderRadius="0.375rem" rightIcon={<CloseIcon />} onClick={foldToLeaf}>
Fold
</Button>
</ButtonGroup>
{/* <Badge
borderRadius="0.375rem"
px="3"
py="2"
variant="subtle"
colorScheme="green"
onClick={handleOpenModalClick}
className={'badge'}
marginRight={3}
fontSize={16}
>
{attr2} <b>{predicateName}</b> {weight} {pivot}
</Badge>
<Button size="xs" rightIcon={<CloseIcon />} onClick={foldToLeaf}>
</Badge> */}
{/* <Button size="xs" borderRadius="0.375rem" rightIcon={<CloseIcon />} onClick={foldToLeaf}>
Fold
</Button>
</Button> */}
<ModalPopup
isOpen={isOpen}
nodeSet={nodeSet}
@@ -57,35 +68,6 @@ function Joint({ children, attr2, predicateName, pivot, weight, requestFoldToLea
onOpen={onOpen}
weight={weight}
/>
{/* <Modal onClose={onClose} size={size} isOpen={isOpen}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{attr2} <b>{predicateName}</b> {pivot}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
Text ( kolejny komponent z konfiguratorem){' '}
{nodeSet.map(x => (
<div>
{x.attr1001} {x[attr2]}-{x[pivot]} {x[attr2] < x[pivot] ? 'Match' : 'NotMatch'}
</div>
))}
<Configurator />
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Close</Button>
</ModalFooter>
</ModalContent>
</Modal> */}
{/* <div>
Match
{JSON.stringify(match)}
</div>
<div>
notMatch
{JSON.stringify(notMatch)}
</div> */}
{children}
</Box>
);

View File

@@ -7,12 +7,14 @@ function Leaf({ category, matchedCount, notMatchedCount, quality, requestLeafUnf
<Box
d="flex"
alignItems="baseline"
borderWidth="1px"
borderWidth="2px"
borderRadius="lg"
flexDirection="column"
maxW="sm"
maxW="xs"
p="1"
my="2"
boxShadow="md"
className={'tree-branch'}
>
<Box
color="gray.500"

View File

@@ -18,7 +18,7 @@ function ModalPopup({ attr2, predicateName, pivot, weight, isOpen, nodeSet, onOp
};
return (
<Modal onClose={onClose} size="lg" isOpen={isOpen}>
<Modal onClose={onClose} size="lg" isOpen={isOpen} blockScrollOnMount={false}>
<ModalOverlay />
<ModalContent>
<ModalHeader>

View File

@@ -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 (
<div className={`node ${highlighted ? 'highlight' : ''}`} onClick={onNodeClicked}>
{category ? (
<Leaf
category={category}
matchedCount={matchedCount}
notMatchedCount={notMatchedCount}
quality={quality}
requestLeafUnfold={unfoldLeaf}
/>
) : (
<Joint
attr2={attr2}
predicateName={predicateName}
pivot={pivot}
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)} />
</Joint>
)}
<Box d="flex" flexDirection="column" p="1" paddingLeft={3}>
<DataViewer node={node} side={side} />
{category ? (
<Leaf
category={category}
matchedCount={matchedCount}
notMatchedCount={notMatchedCount}
quality={quality}
requestLeafUnfold={unfoldLeaf}
/>
) : (
<Joint
attr2={attr2}
predicateName={predicateName}
pivot={pivot}
match={match}
notMatch={notMatch}
onChange={onChange}
requestFoldToLeaf={foldJointToLeaf}
nodeSet={nodeSet}
weight={weight}
>
<Node
node={match}
onChange={onChange}
requestChildChange={requestChildChangeIfMatchIs(true)}
side={side}
/>
<Node
node={notMatch}
onChange={onChange}
requestChildChange={requestChildChangeIfMatchIs(false)}
side={!side}
/>
</Joint>
)}
</Box>
</div>
);
};

View File

@@ -69,7 +69,7 @@ const Tree = ({ options }) => {
{!root ? (
<p>No tree to show</p>
) : (
<Node node={root} onChange={() => {}} requestChildChange={requestChildChange} />
<Node node={root} onChange={() => {}} requestChildChange={requestChildChange} side={true} />
)}
</Box>
</div>

View File

@@ -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;

View File

@@ -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;
});
}