Commit 8c835958 authored by Bruno López Trigo's avatar Bruno López Trigo

Portada a representación da árbore de decisión a D3 e solventados pequenos bugs nas explicacións

parent 40d1e7c1
......@@ -135,7 +135,6 @@ public class TreeInterpreter {
double splitValue, supMargin, infMargin;
Classification classification = new Classification();
HashMap<String, Boolean> modified = new HashMap();
HashMap<String, Double> splitValues = new HashMap();
ArrayList<HashMap<String, Boolean>> modifiedList = new ArrayList();
ArrayList<String> solutions = new ArrayList();
......@@ -152,11 +151,6 @@ public class TreeInterpreter {
ArrayList<Classification> classifications = new ArrayList();
while (!alternatives.isEmpty()) {
for(Map.Entry<String, Double> entry: splitValues.entrySet()){
if((double) entry.getValue() != alternatives.get(0).getNumericValue(entry.getKey())){
classification.addSplitValue(entry.getKey(), entry.getValue());
}
}
while (!(path.get(path.size() - 1) instanceof ConsequentNode)) {
if (path.get(path.size() - 1) instanceof NumericNode) {
att = ((NumericNode) path.get(path.size() - 1)).getAttribute();
......@@ -176,13 +170,14 @@ public class TreeInterpreter {
}
supMargin = alternatives.get(0).getNumericValue(att.getId()) + alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && supMargin > splitValue) {
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), supMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
modifiedList.add(modified);
alternatives.add(auxInstance);
} else if(modifiedList.get(0).get(att.getId()) && instance.getNumericValue(att.getId()) > splitValue) {
classification.addSplitValue(att.getId(), splitValue);
}
} else {
path.add(((NumericNode) path.get(path.size() - 1)).getRightChild());
......@@ -198,13 +193,14 @@ public class TreeInterpreter {
}
infMargin = alternatives.get(0).getNumericValue(att.getId()) - alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && infMargin <= splitValue) {
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), infMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
modifiedList.add(modified);
alternatives.add(auxInstance);
} else if(modifiedList.get(0).get(att.getId()) && instance.getNumericValue(att.getId()) <= splitValue) {
classification.addSplitValue(att.getId(), splitValue);
}
}
} else {
......@@ -222,13 +218,14 @@ public class TreeInterpreter {
}
supMargin = alternatives.get(0).getNumericValue(att.getId()) + alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && supMargin >= splitValue) {
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), supMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
modifiedList.add(modified);
alternatives.add(auxInstance);
} else if(modifiedList.get(0).get(att.getId()) && instance.getNumericValue(att.getId()) >= splitValue) {
classification.addSplitValue(att.getId(), splitValue);
}
} else {
path.add(((NumericNode) path.get(path.size() - 1)).getRightChild());
......@@ -244,13 +241,14 @@ public class TreeInterpreter {
}
infMargin = alternatives.get(0).getNumericValue(att.getId()) - alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && infMargin < splitValue) {
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), infMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
modifiedList.add(modified);
alternatives.add(auxInstance);
} else if(modifiedList.get(0).get(att.getId()) && instance.getNumericValue(att.getId()) < splitValue) {
classification.addSplitValue(att.getId(), splitValue);
}
}
}
......
......@@ -175,7 +175,7 @@ public class ExplainerManagerImpl implements ExplainerManager {
}
phrase = this.generator.generateClausePreModifier("confusion", false, "Only in exceptional cases", "involve", literal);
explanation.addClause(((ClauseGeneratorEn) this.generator).getRealisation(phrase));
} else {
} else if(numConseq > 4) {
literal = "the pairs ";
for (int i = 0; i < confusedConsequents.size(); i += 2) {
if ((i + 2) != confusedConsequents.size()) {
......@@ -445,7 +445,7 @@ public class ExplainerManagerImpl implements ExplainerManager {
this.generator = new ClauseGeneratorEs();
if (confusion != 0) {
phrase = this.generator.generateClause(
consequents.get(0).getName(), "ser", false, "confundido con " + consequents.get(1).getName() + " " + df.format(confusion) + "%");
consequents.get(0).getName(), "ser", false, "confundido con " + consequents.get(1).getName() + " un " + df.format(confusion) + "%");
} else {
phrase = this.generator.generateClausePostModifier(
consequents.get(0).getName(), false, "nunca", "ser", "confundido con " + consequents.get(1).getName());
......@@ -456,7 +456,7 @@ public class ExplainerManagerImpl implements ExplainerManager {
this.generator = new ClauseGeneratorGl();
if (confusion != 0) {
phrase = this.generator.generateClause(
consequents.get(0).getName(), "ser", false, "confundido con " + consequents.get(1).getName() + " " + df.format(confusion) + "%");
consequents.get(0).getName(), "ser", false, "confundido con " + consequents.get(1).getName() + " un " + df.format(confusion) + "%");
} else {
phrase = this.generator.generateClausePostModifier(
consequents.get(0).getName(), false, "nunca", "ser", "confundido con " + consequents.get(1).getName());
......@@ -613,6 +613,7 @@ public class ExplainerManagerImpl implements ExplainerManager {
explanation.addClause(((ClauseGeneratorEn) this.generator).getRealisation(phrase));
}
HashMap<String, Double> splitValues;
for (int i = 1; i < numAlternatives; i++) {
......
......@@ -223,6 +223,7 @@ public class InfoExtractor {
for (Antecedent a : this.classifications.get(alternative).getAntecedents()) {
if (!a.isCategoric()) {
Double split = this.classifications.get(alternative).getSplitValue(a.getDecision().split(" > | <= ")[0]);
if (split != null) {
values.put(this.config.getAttributeById(a.getDecision().split(" > | <= ")[0]).getName(), split);
}
......
package brunolopez.expliclas.models;
import java.util.HashMap;
public class NodeShape {
private String shape;
private HashMap<String, String> shapeProps;
public NodeShape() {
}
public NodeShape(String shape) {
this.shape = shape;
this.shapeProps = new HashMap();
}
public void addShape(String key, String value){
this.shapeProps.put(key, value);
}
public String getShape() {
return shape;
}
public HashMap<String, String> getShapeProps() {
return shapeProps;
}
}
package brunolopez.expliclas.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.HashMap;
public class VisualNode {
private String name;
private HashMap<String, String> attributes;
private String decision;
private ArrayList<VisualNode> children;
private NodeShape nodeSvgShape;
private boolean leaf;
private boolean visited;
public VisualNode() {
this.children = new ArrayList();
}
public VisualNode(String name, HashMap<String, String> attributes) {
public VisualNode(String name, String decision, boolean leaf) {
this.name = name;
this.attributes = attributes;
this.decision = decision;
this.children = new ArrayList();
this.leaf = leaf;
}
public VisualNode(String name, HashMap<String, String> attributes,
ArrayList<VisualNode> children, NodeShape nodeSvgShape) {
public VisualNode(String name, String decision,
ArrayList<VisualNode> children) {
this.name = name;
this.attributes = attributes;
this.decision = decision;
this.children = new ArrayList();
}
public HashMap<String, String> getAttributes() {
return attributes;
public void setVisited(boolean visited) {
this.visited = visited;
}
public void setAttributes(HashMap<String, String> attributes) {
this.attributes = attributes;
public boolean isVisited() {
return visited;
}
public ArrayList<VisualNode> getChildren() {
return children;
public void setLeaf(boolean leaf) {
this.leaf = leaf;
}
public boolean isLeaf() {
return leaf;
}
public String getDecision() {
return decision;
}
public void setDecision(String decision) {
this.decision = decision;
}
public NodeShape getNodeSvgShape() {
return nodeSvgShape;
public ArrayList<VisualNode> getChildren() {
return children;
}
public void setChildren(ArrayList<VisualNode> children) {
......@@ -58,9 +70,5 @@ public class VisualNode {
public void addChild(VisualNode node){
this.children.add(node);
}
public void setNodeSvgShape(NodeShape nodeSvgShape) {
this.nodeSvgShape = nodeSvgShape;
}
}
#Generated by Maven
#Wed Nov 07 16:46:05 CET 2018
#Fri Nov 09 17:35:07 CET 2018
version=1.0
groupId=brunolopez
artifactId=expliclas-api
......@@ -16,7 +16,6 @@ brunolopez/expliclas/services/ExplainerService.class
brunolopez/expliclas/services/ClassifierService.class
brunolopez/expliclas/models/Property.class
brunolopez/expliclas/models/GlobalConfig.class
brunolopez/expliclas/models/NodeShape.class
brunolopez/expliclas/models/NumericNode.class
brunolopez/expliclas/models/BuildParamsJ48.class
brunolopez/expliclas/utils/MapperJSON.class
......
......@@ -27,7 +27,6 @@
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/explainer/ClauseGenerator.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/models/Interval.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/explainer/ExplainerManagerImpl.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/models/NodeShape.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/classifiers/TreeBuilder.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/models/VisualNode.java
/home/brilemau/Projects/web/ExpliClas-TFG/codigo/expliclas-api/src/main/java/brunolopez/expliclas/utils/MapperJSON.java
......
This diff is collapsed.
......@@ -7,6 +7,7 @@
"@material-ui/core": "^3.2.2",
"@material-ui/icons": "^3.0.1",
"@material-ui/lab": "^3.0.0-alpha.21",
"d3": "^5.7.0",
"react": "^16.3.2",
"react-d3-tree": "^1.11.0",
"react-dom": "^16.3.2",
......
......@@ -50,7 +50,10 @@ const styles = theme => ({
marginTop: "30px",
backgroundColor: "#4caf50",
fontSize: "20px",
color: "white"
color: "white",
"&:hover": {
backgroundColor: "#357a38"
}
},
icon: {
marginLeft: 10
......@@ -276,7 +279,9 @@ class FormJ48 extends Component {
<Fragment>
<Paper className={classes.container}>
<Typography variant="h4">
Build {this.state.algorithm} parameters
{this.props.language.labels[this.props.language.id].build +
" " +
this.state.algorithm}
</Typography>
<form>
<Tooltip title="Dataset file in arff format" placement="top">
......@@ -287,7 +292,7 @@ class FormJ48 extends Component {
disabled={this.state.loading}
onClick={this.handleButtonClick}
>
{"Upload your dataset"}
{this.props.language.labels[this.props.language.id].upload}
{!this.state.file ? (
<CloudUpload className={classes.icon} />
) : (
......@@ -303,7 +308,9 @@ class FormJ48 extends Component {
<Tooltip title="Name to identify dataset" placement="top">
<TextField
id="standard-name"
label="dataset name"
label={
this.props.language.labels[this.props.language.id].datasetName
}
className={classes.textFieldName}
value={this.state.name}
onChange={this.handleChange("name")}
......@@ -542,7 +549,7 @@ class FormJ48 extends Component {
className={classes.button}
onClick={this.buildClassifier}
>
Build
{this.props.language.labels[this.props.language.id].buildButton}
</Button>
</form>
</Paper>
......
......@@ -50,7 +50,10 @@ const styles = theme => ({
marginTop: "30px",
backgroundColor: "#4caf50",
fontSize: "20px",
color: "white"
color: "white",
"&:hover": {
backgroundColor: "#357a38"
}
},
icon: {
marginLeft: 10
......@@ -267,7 +270,9 @@ class FormREP extends Component {
<Fragment>
<Paper className={classes.container}>
<Typography variant="h4">
Build {this.state.algorithm} parameters
{this.props.language.labels[this.props.language.id].build +
" " +
this.state.algorithm}
</Typography>
<form>
<Tooltip title="Dataset file in arff format" placement="top">
......@@ -278,7 +283,7 @@ class FormREP extends Component {
disabled={this.state.loading}
onClick={this.handleButtonClick}
>
{"Upload your dataset"}
{this.props.language.labels[this.props.language.id].upload}
<CloudUpload className={classes.icon} />
<input
onChange={this.changeFile}
......@@ -290,7 +295,9 @@ class FormREP extends Component {
<Tooltip title="Name to identify dataset" placement="top">
<TextField
id="standard-name"
label="dataset name"
label={
this.props.language.labels[this.props.language.id].datasetName
}
className={classes.textFieldName}
value={this.state.name}
onChange={this.handleChange("name")}
......@@ -461,7 +468,7 @@ class FormREP extends Component {
className={classes.button}
onClick={this.buildClassifier}
>
Build
{this.props.language.labels[this.props.language.id].buildButton}
</Button>
</form>
</Paper>
......
......@@ -50,7 +50,10 @@ const styles = theme => ({
marginTop: "30px",
backgroundColor: "#4caf50",
fontSize: "20px",
color: "white"
color: "white",
"&:hover": {
backgroundColor: "#357a38"
}
},
icon: {
marginLeft: 10
......@@ -266,7 +269,9 @@ class FormRandom extends Component {
<Fragment>
<Paper className={classes.container}>
<Typography variant="h4">
Build {this.state.algorithm} parameters
{this.props.language.labels[this.props.language.id].build +
" " +
this.state.algorithm}
</Typography>
<form>
<Tooltip title="Dataset file in arff format" placement="top">
......@@ -277,7 +282,7 @@ class FormRandom extends Component {
disabled={this.state.loading}
onClick={this.handleButtonClick}
>
{"Upload your dataset"}
{this.props.language.labels[this.props.language.id].upload}
<CloudUpload className={classes.icon} />
<input
onChange={this.changeFile}
......@@ -289,7 +294,9 @@ class FormRandom extends Component {
<Tooltip title="Name to identify dataset" placement="top">
<TextField
id="standard-name"
label="dataset name"
label={
this.props.language.labels[this.props.language.id].datasetName
}
className={classes.textFieldName}
value={this.state.name}
onChange={this.handleChange("name")}
......@@ -466,7 +473,7 @@ class FormRandom extends Component {
className={classes.button}
onClick={this.buildClassifier}
>
Build
{this.props.language.labels[this.props.language.id].buildButton}
</Button>
</form>
</Paper>
......
import React, { Component, Fragment } from "react";
import Tree from "react-d3-tree";
import "../css/CenteredTree.css";
import { CircularProgress, Typography } from "@material-ui/core";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
const styles = theme => ({
progress: {
margin: "auto",
marginTop: 80,
marginBottom: 30
},
loader: {
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
justifyItems: "center"
}
});
class CenteredTree extends Component {
constructor(props) {
super(props);
this.state = {
styles: {
links: {
strokeWidth: 1,
stroke: "#283593"
}
},
loading: true,
collapsible: false
};
//this.updateTree = this.updateTree.bind(this);
}
componentDidMount() {
const dimensions = this.treeContainer.getBoundingClientRect();
!this.isCancelled &&
this.setState({
translate: {
x: dimensions.width / 2,
y: 45
}
});
if (this.props.treeData) {
this.setState({
loading: false
});
}
}
componentWillReceiveProps(prevProps) {
if (
prevProps.treeData[0] &&
prevProps.treeData[0].nodeSvgShape.shapeProps.fill !==
this.props.treeData[0].nodeSvgShape.shapeProps.fill
) {
this.setState({
loading: true
});
setTimeout(
function() {
this.setState({ loading: false });
}.bind(this),
2000
);
}
}
componentWillUnmount() {
this.isCancelled = true;
}
render() {
const { classes } = this.props;
return this.state.loading ? (
<Fragment>
<div className={classes.loader}>
<CircularProgress className={classes.progress} size={100} />
<Typography variant="h3" align="center">
Loading
</Typography>
</div>
<div
id="treeContainer"
className="treeContainer"
ref={tc => (this.treeContainer = tc)}
/>
</Fragment>
) : (
<div
id="treeContainer"
className="treeContainer"
ref={tc => (this.treeContainer = tc)}
>
<Tree
ref="tree"
zoom={0.4}
onMouseOut={this.mouseOutTree}
onMouseOver={this.mouseOverTree}
data={[this.props.treeData]}
collapsible={this.state.collapsible}
translate={this.state.translate}
orientation="vertical"
pathFunc="diagonal"
/>
</div>
);
}
}
CenteredTree.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(CenteredTree);
......@@ -9,7 +9,9 @@ const styles = {
fontSize: 20,
padding: "2%",
paddingLeft: "20%",
paddingRight: "20%"
paddingRight: "20%",
maxHeight: 300,
overflow: "auto"
},
tab: {
fontSize: 16
......@@ -18,7 +20,7 @@ const styles = {
textAlign: "center"
},
panel: {
position: "absolute",
position: "fixed",
width: "100%",
bottom: 0,
left: 0
......
import React, { Component, Fragment } from "react";
import "../css/Matrix.css";
import { Typography } from "@material-ui/core";
import { Typography, CircularProgress } from "@material-ui/core";
import { withStyles } from "@material-ui/core/styles";
import { getConfusionBetween, getConfig } from "../global/API";
import PropTypes from "prop-types";
......@@ -19,6 +19,18 @@ const styles = theme => ({
marginTop: 40,
fontSize: 20,
textAlign: "center"
},
progress: {
margin: "auto",
marginTop: 80,
marginBottom: 30
},
loader: {
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
justifyItems: "center"
}
});
......@@ -221,7 +233,9 @@ class Matrix extends Component {