Skip to content

Commit 4a2414c

Browse files
committed
select and fit model with data
1 parent 180aa1b commit 4a2414c

File tree

3 files changed

+153
-8
lines changed

3 files changed

+153
-8
lines changed

app.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@
2121
from flask import Flask
2222
from flask import render_template, jsonify, request
2323

24+
from numpy import asarray
2425
from functools import partial
26+
27+
from sklearn.svm import SVC
28+
from sklearn.naive_bayes import MultinomialNB
2529
from sklearn.preprocessing import MinMaxScaler
30+
from sklearn.linear_model import LogisticRegression
31+
from sklearn.metrics import precision_recall_fscore_support as prfs
2632
from sklearn.datasets import make_blobs, make_circles, make_moons, make_classification
2733

2834

@@ -75,6 +81,42 @@ def generate():
7581
return jsonify(data)
7682

7783

84+
@app.route("/fit", methods=["POST"])
85+
def fit():
86+
# TODO: test content type and send 400 if not JSON
87+
# Construct the model fit request
88+
data = request.get_json()
89+
params = data.get("model", {})
90+
dataset = data.get("dataset", [])
91+
model = {
92+
'bayes': MultinomialNB(),
93+
'svm': SVC(),
94+
'logit': LogisticRegression(),
95+
}.get(params.pop("model", None), None)
96+
97+
# Validate the request is correct and sane
98+
if model is None or len(dataset) == 0:
99+
return "invalid fit request", 400
100+
101+
# Set the hyperparameters on the model
102+
model.set_params(**params)
103+
104+
# Construct the dataset
105+
X, y = [], []
106+
for point in dataset:
107+
X.append([point["x"], point["y"]])
108+
y.append(point["c"])
109+
X, y = asarray(X), asarray(y)
110+
111+
# Fit the model to the dataset and get the training score
112+
model.fit(X, y)
113+
yhat = model.predict(X)
114+
metrics = prfs(y, yhat, average="macro")
115+
116+
return jsonify({
117+
"metrics": dict(zip(["precision", "recall", "f1", "support"], metrics))
118+
})
119+
78120
##########################################################################
79121
## Run the Web App
80122
##########################################################################

static/js/dataspace.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,35 @@ class Dataspace {
6868
});
6969
}
7070

71+
// Fit the model specified in the data fields to the data in the plot
72+
fit(model) {
73+
if (this.dataset.length == 0) {
74+
console.log("cannot fit model to no data!");
75+
return
76+
}
77+
78+
var data = {
79+
model: model,
80+
dataset: this.dataset,
81+
}
82+
83+
d3.json("/fit", {
84+
method: "POST",
85+
body: JSON.stringify(data),
86+
headers: {
87+
"Content-Type": "application/json; charset=UTF-8"
88+
}
89+
}).then(json => {
90+
$("#f1score").text(json.metrics.f1);
91+
$("#metrics").removeClass("invisible").addClass("visible");
92+
});
93+
}
94+
7195
// Reset the plotting area
7296
reset() {
7397
this.dataset = [];
7498
this.svg.selectAll("circle").remove();
99+
$("#metrics").removeClass("visible").addClass("invisible");
75100
}
76101

77102
}
@@ -112,4 +137,28 @@ $(document).ready(function() {
112137
return false;
113138
})
114139

140+
// Change the model hyperparameter tabs on select
141+
$("select#modelSelect").change(function(e) {
142+
e.preventDefault();
143+
$('#modelTabs [class*="active"]').removeClass("show active");
144+
145+
var model = $(e.target).val();
146+
$("#"+model).addClass("show active");
147+
return false;
148+
})
149+
150+
// Display the model when the fit button is clicked
151+
$("button#fitBtn").click(function(e) {
152+
e.preventDefault();
153+
154+
var form = $('#modelTabs [class*="active"] form');
155+
var data = form.serializeArray().reduce(function(obj, item) {
156+
obj[item.name] = item.value;
157+
return obj;
158+
}, {});
159+
160+
app.fit(data);
161+
return false;
162+
});
163+
115164
});

templates/index.html

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,17 @@
4545
</header>
4646

4747
<main role="main" class="container">
48-
<!-- dataspace -->
48+
<!--result -->
4949
<div class="row">
50+
<div class="col mb-0">
51+
<p class="invisible text-right mb-0 pb-0" id="metrics">
52+
F1 Score: <span id="f1score"></span>
53+
</p>
54+
</div>
55+
</div>
56+
57+
<!-- dataspace -->
58+
<div class="row mt-0">
5059
<div class="col-md-12">
5160
<div class="border border-primary">
5261
<!-- the data visualization canvas -->
@@ -55,21 +64,32 @@
5564
</div>
5665
</div><!-- dataspace ends -->
5766

58-
<!-- controls -->
67+
<!-- data controls -->
5968
<div class="row mt-2">
60-
<div class="col-md-6">
61-
<form class="form-inline" id="dataEntryForm">
69+
<div class="col-md-4">
70+
<form class="form-inline" id="modelSelectForm">
71+
<label class="my-1 mr-2" for="modelSelect">Select Model Family</label>
72+
<select class="custom-select my-1 mr-sm-2" id="modelSelect" role="tablist">
73+
<option value="bayes" role="tab">Naive Bayes</option>
74+
<option value="svm" role="tab">SVM</option>
75+
<option value="logit" role="tab">Logistic</option>
76+
</select>
77+
<button class="btn btn-primary" id="fitBtn">Fit</button>
78+
</form>
79+
</div>
80+
<div class="col-md-4">
81+
<form class="form-inline justify-content-center" id="dataEntryForm">
6282
<label class="my-1 mr-2" for="classSelect">Add Points to Class</label>
6383
<select class="custom-select my-1 mr-sm-2" id="classSelect">
6484
<option value="0" selected>0</option>
6585
<option value="1">1</option>
6686
<option value="2">2</option>
6787
<option value="3">3</option>
6888
</select>
69-
<button class="btn btn-primary" id="resetBtn">Reset</button>
89+
<button class="btn btn-secondary" id="resetBtn">Reset</button>
7090
</form>
7191
</div>
72-
<div class="col-md-6">
92+
<div class="col-md-4">
7393
<form class="form-inline pull-right" id="datasetForm">
7494
<label class="my-1 mr-2" for="generator">Generate Dataset</label>
7595
<select class="custom-select my-1 mr-sm-2" name="generator">
@@ -83,7 +103,40 @@
83103
<button type="submit" class="btn btn-primary" id="createBtn">Generate</button>
84104
</form>
85105
</div>
86-
</div><!-- controls ends -->
106+
<div class="clearfix"></div>
107+
</div><!-- data controls ends -->
108+
109+
<!-- model controls -->
110+
<div class="row mt-2">
111+
<div class="tab-content" id="modelTabs">
112+
<div class="tab-pane fade show active" id="bayes" role="tabpanel">
113+
<div class="col">
114+
<p>Naive Bayes</p>
115+
<form class="form">
116+
<input type="hidden" name="model" value="bayes" />
117+
</form>
118+
</div>
119+
</div>
120+
<div class="tab-pane fade" id="svm" role="tabpanel">
121+
<div class="col">
122+
<p>Support Vector Machine</p>
123+
<form class="form">
124+
<input type="hidden" name="model" value="svm" />
125+
</form>
126+
</div>
127+
</div>
128+
<div class="tab-pane fade" id="logit" role="tabpanel">
129+
<div class="col">
130+
<p>Logistic Regression</p>
131+
<form class="form">
132+
<input type="hidden" name="model" value="logit" />
133+
</form>
134+
</div>
135+
</div>
136+
</div>
137+
</div><!-- model controls ends -->
138+
139+
87140
</main>
88141

89142
<footer class="footer bg-light">
@@ -133,6 +186,7 @@ <h5 class="modal-title" id="aboutModalLabel">About Data Space</h5>
133186
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
134187
integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>
135188
<script src="https://d3js.org/d3.v5.min.js"></script>
189+
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
136190
<script src="https://unpkg.com/d3-fetch"></script>
137191
<script src="{{ url_for('static', filename='js/dataspace.js') }}"></script>
138192

@@ -147,4 +201,4 @@ <h5 class="modal-title" id="aboutModalLabel">About Data Space</h5>
147201
</script>
148202

149203
</body>
150-
</html>
204+
</html>

0 commit comments

Comments
 (0)