Skip to content

Commit

Permalink
Improved findTSNEConfig function, fixed controlled components states,…
Browse files Browse the repository at this point in the history
… added reset user parameters button
  • Loading branch information
Colin Troisemaine authored and Colin Troisemaine committed Jul 3, 2024
1 parent 0e6b98f commit 82f4252
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 249 deletions.
Binary file modified backend/__pycache__/server.cpython-310.pyc
Binary file not shown.
66 changes: 26 additions & 40 deletions backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import gc
import re

os.environ["OMP_NUM_THREADS"] = '1' # For the k-means warning...

app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
# app.secret_key = "My Secret key"
Expand Down Expand Up @@ -164,50 +166,34 @@ def equal_dicts(d1, d2, ignore_keys):
return d1_filtered == d2_filtered


def used_classes(config_dict):
# In the case where we show only the unknown classes, only the set of unknown classes should be equal
if config_dict['show_unknown_only'] is True:
return config_dict['unknown_classes']
# Otherwise, all the classes have to be the same
else:
return config_dict['known_classes'] + config_dict['unknown_classes']


def findTSNEConfig(results_dict, dataset_name, tsne_config_to_find):
# If it has to be viewed in a latent space, its complicated...
if tsne_config_to_find['view_in_encoder'] is True:
return None, None

if dataset_name in results_dict.keys():
for tsne_run_name in results_dict[dataset_name].keys():
tsne_run = results_dict[dataset_name][tsne_run_name]

# If it has to be viewed in a latent space, its complicated...
if tsne_run['tsne_config']['view_in_encoder'] is True or tsne_config_to_find['view_in_encoder'] is True:
return None, None
else:
same_tsne_config = False
if (tsne_run['tsne_config']['tsne_seed'] == tsne_config_to_find['tsne_seed']
and tsne_run['tsne_config']['tsne_perplexity'] == tsne_config_to_find['tsne_perplexity']
and set(tsne_run['tsne_config']['selected_features']) == set(tsne_config_to_find['selected_features'])):
if tsne_run["tsne_config"]['show_unknown_only'] == tsne_config_to_find['show_unknown_only']:
# In the case where we show only the unknown classes, only the set of unknown classes should be equal
if tsne_config_to_find['show_unknown_only'] is True:
if set(tsne_run['tsne_config']['unknown_classes']) == set(tsne_config_to_find['unknown_classes']):
same_tsne_config = True
else:
# Otherwise, all the classes have to be the same
if set(tsne_run['tsne_config']['known_classes'] + tsne_run['tsne_config']['unknown_classes']) == set(tsne_config_to_find['known_classes'] + tsne_config_to_find['unknown_classes']):
same_tsne_config = True

if same_tsne_config is True:
if os.path.isfile(tsne_run['tsne_filepath']):
app.logger.debug("Re-using t-SNE at " + tsne_run['tsne_filepath'])
tsne_array = pd.read_csv(tsne_run['tsne_filepath'], header=None)
return tsne_array, tsne_run_name

# # In the case where we show all classes, only the set of known + unknown classes should be equal
# if tsne_config_to_find['show_unknown_only'] is False and tsne_run["tsne_config"][
# 'show_unknown_only'] is False:
# if set(tsne_run["tsne_config"]['known_classes'] + tsne_run["tsne_config"]['unknown_classes']) == set(
# tsne_config_to_find['known_classes'] + tsne_config_to_find['unknown_classes']):
# # If the sets are equal, we compare the two dicts while ignoring the keys 'known_classes' and 'unknown_classes'
# if equal_dicts(tsne_run["tsne_config"], tsne_config_to_find, ['known_classes', 'unknown_classes']):
# if os.path.isfile(tsne_run['tsne_filepath']):
# tsne_array = pd.read_csv(tsne_run['tsne_filepath'], header=None)
# return tsne_array, tsne_run_name
# # If we show only the known classes, the whole config should be equal
# elif tsne_run["tsne_config"] == tsne_config_to_find:
# if os.path.isfile(tsne_run['tsne_filepath']):
# tsne_array = pd.read_csv(tsne_run['tsne_filepath'], header=None)
# return tsne_array, tsne_run_name
# We compare only the configuration elements that matter for the t-SNE:
# For instance, the target doesn't matter in the t-SNE, it only matters when coloring the plot.
if (tsne_run['tsne_config']['view_in_encoder'] == tsne_config_to_find['view_in_encoder']
and tsne_run['tsne_config']['tsne_seed'] == tsne_config_to_find['tsne_seed']
and tsne_run['tsne_config']['tsne_perplexity'] == tsne_config_to_find['tsne_perplexity']
and set(tsne_run['tsne_config']['selected_features']) == set(tsne_config_to_find['selected_features'])
and set(used_classes(tsne_run["tsne_config"])) == set(used_classes(tsne_config_to_find))):
if os.path.isfile(tsne_run['tsne_filepath']):
app.logger.debug("Re-using t-SNE at " + tsne_run['tsne_filepath'])
tsne_array = pd.read_csv(tsne_run['tsne_filepath'], header=None)
return tsne_array, tsne_run_name

return None, None

Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/AgglomerativeClustering.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AgglomerativeClustering extends React.Component {
max={42} // To set to the total number of clusters
tooltip='off'
onChange={changeEvent => this.setState({agglomerative_clustering_value: changeEvent.target.value})}
// ToDo control component's value directly with value={...}
/>
</Col>

Expand Down
31 changes: 18 additions & 13 deletions frontend/src/components/DataVisualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Tooltip } from "@mui/material";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { regular } from '@fortawesome/fontawesome-svg-core/import.macro'
import Plot from 'react-plotly.js';
import { faRotate } from "@fortawesome/free-solid-svg-icons";


class DataVisualization extends React.Component {
Expand All @@ -26,23 +27,27 @@ class DataVisualization extends React.Component {
<Col className="d-flex flex-column" style={{height: '100%', paddingLeft: "6px"}}>
<Row className="d-flex flex-row" style={{paddingRight: "6px"}}>
<div style={{display: "flex"}}>
<h5>Data visualization</h5>
{/*
<Tooltip title="Save image" style={{marginLeft: "auto", marginRight: "6px"}}>
<Button className="btn-secondary" onClick={this.props.onSaveImageButtonClick}>
<Col className="col-auto d-flex flex-column" style={{justifyContent: "center", flexGrow:'1', textAlign: "left"}}>
<h5>Data visualization</h5>
</Col>
<Col className="col-auto d-flex flex-column" style={{justifyContent: "center"}}>
<Tooltip title="Reset all parameters to default" style={{marginLeft: "auto", marginRight: "6px"}}>
<Button className="btn-secondary" onClick={this.props.onResetParametersToDefault}>
<div className="d-flex py-1">
<FontAwesomeIcon icon={regular('floppy-disk')}/>
<FontAwesomeIcon icon={faRotate} />
</div>
</Button>
</Tooltip>
*/}
<Tooltip title="Clear cached data in server" style={{marginLeft: "auto", marginRight: "6px"}}>
<Button className="btn-secondary" onClick={this.props.onClearCacheButtonClick}>
<div className="d-flex py-1">
<FontAwesomeIcon icon={regular('trash-can')}/>
</div>
</Button>
</Tooltip>
</Col>
<Col className="col-auto d-flex flex-column" style={{justifyContent: "center"}}>
<Tooltip title="Clear cached data in server" style={{marginLeft: "auto", marginRight: "6px"}}>
<Button className="btn-secondary" onClick={this.props.onClearCacheButtonClick}>
<div className="d-flex py-1">
<FontAwesomeIcon icon={regular('trash-can')}/>
</div>
</Button>
</Tooltip>
</Col>
</div>
</Row>
<Row className="d-flex flex-row mt-1" style={{flexGrow:'1', overflowY: "auto", height:"100%"}}>
Expand Down
74 changes: 5 additions & 69 deletions frontend/src/components/DatasetSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,75 +12,11 @@ import Container from "react-bootstrap/Container";
import React from 'react';
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import FireSwalError from "./pop_up_notifiers/FireSwalError";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {regular} from "@fortawesome/fontawesome-svg-core/import.macro";
import Tooltip from "@mui/material/Tooltip";

class DatasetSelector extends React.Component {

constructor(props) {
super(props);
this.state = {
selectedFile: "",
field_separator: ',',
};
}

onFileChange = event => {
this.setState({ selectedFile: event.target.files[0].name })
}

onDatasetUnload = () => {
this.setState({selectedFile: ""})
document.getElementById("my_input_file_form").value = "";
this.props.unloadDatasetHandler()
}

onDatasetSeparatorChange = selected_sep => {
this.setState({field_separator: selected_sep.target.value})
}

onFileUpload = () => {
if (this.state.selectedFile === ""){
FireSwalError('Please select a file to load')
return
}

if (this.state.field_separator === ""){
FireSwalError('Please specify a field separator')
return
}

const dataset_name = this.state.selectedFile.replace(/\.[^/.]+$/, "")
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
'selected_file_path': this.state.selectedFile,
'field_separator': this.state.field_separator,
'dataset_name': dataset_name})
}
fetch('/getFileHeader', requestOptions) // Don't need to specify the full localhost:5000/... as the proxy is set in package.json
.then(serverPromise => {
if (serverPromise.status === 500) {
FireSwalError('Status 500 - Server error', 'Please make sure that the server is running')
}
if (serverPromise.status === 422) {
serverPromise.json().then(error => {
FireSwalError('Status 422 - Server error', error['error_message'])
})
}
if (serverPromise.status === 200) {
serverPromise.json().then(response => {
// The features we just received from the server are sent to the FullPage.js component
this.props.onNewFeaturesLoaded(response['file_header'])
this.props.setDatasetNameHandler(dataset_name)
})
}
})
};

render() {
return (
<Container>
Expand All @@ -90,7 +26,7 @@ class DatasetSelector extends React.Component {
</Row>
<Row className="d-flex flex-row">
<Col className="col-12 d-flex flex-column justify-content-center">
<input type="file" onChange={this.onFileChange} style={{backgroundColor: "white", color: "black"}} id="my_input_file_form"/>
<input type="file" onChange={(event) => this.props.onFileChange(event)} style={{backgroundColor: "white", color: "black"}} id="my_input_file_form"/>
</Col>
</Row>
<Row className="d-flex flex-row pt-2">
Expand All @@ -104,17 +40,17 @@ class DatasetSelector extends React.Component {
<Col className="col-6 d-flex flex-column justify-content-center">
<input type="text"
placeholder="field separator"
onChange={this.onDatasetSeparatorChange}
defaultValue={this.state.field_separator}
onChange={this.props.onDatasetSeparatorChange}
value={this.props.field_separator}
/>
</Col>
</Row>
<Row className="d-flex flex-row pt-2">
<Col className="col-6 d-flex flex-column" style={{textAlign: "right"}}>
<button type="button" className="btn btn-primary" onClick={this.onFileUpload} style={{width:'100px'}}>Load</button>
<button type="button" className="btn btn-primary" onClick={this.props.onFileUpload} style={{width:'100px'}}>Load</button>
</Col>
<Col className="col-6 d-flex flex-column align-items-end" style={{textAlign: "right"}}>
<button type="button" className="btn btn-danger" onClick={this.onDatasetUnload} style={{width:'100px'}}>Unload</button>
<button type="button" className="btn btn-danger" onClick={this.props.onDatasetUnload} style={{width:'100px'}}>Unload</button>
</Col>
</Row>
</Col>
Expand Down
Loading

0 comments on commit 82f4252

Please sign in to comment.