├── .gitignore
├── README.md
├── build
├── iris.csv
└── main.js
├── gulpfile.js
├── index.html
├── package.json
├── react_example.py
├── requirements.txt
└── scripts
├── barChart.js
├── iris.csv
├── main.js
└── scatterPlot.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.pyc
3 | venv
4 | .eslintrc
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | flask_react_example
2 | =====================
3 |
4 | A minimal example of an interactive scikit-learn model, using react and d3js. To run in a
5 | virtual environment:
6 |
7 | ```
8 | $ virtualenv venv
9 | New python executable in venv/bin/python
10 | Installing setuptools, pip, wheel...done.
11 | $ source venv/bin/activate
12 | (venv)$ pip install -r requirements.txt
13 | ...
14 | (venv)$ python react_example.py
15 | * Running on http://127.0.0.1:5001/ (Press CTRL+C to quit)
16 | ```
17 | then go to [localhost:5001](http://localhost:5001/).
18 |
19 | Just want to see it running? Go over to [here](http://reactdemo.colindcarroll.com/).
20 |
--------------------------------------------------------------------------------
/build/iris.csv:
--------------------------------------------------------------------------------
1 | Sepal Length,Sepal Width,Petal Length,Petal Width,Species
2 | 5.1,3.5,1.4,0.2,setosa
3 | 4.9,3.0,1.4,0.2,setosa
4 | 4.7,3.2,1.3,0.2,setosa
5 | 4.6,3.1,1.5,0.2,setosa
6 | 5.0,3.6,1.4,0.2,setosa
7 | 5.4,3.9,1.7,0.4,setosa
8 | 4.6,3.4,1.4,0.3,setosa
9 | 5.0,3.4,1.5,0.2,setosa
10 | 4.4,2.9,1.4,0.2,setosa
11 | 4.9,3.1,1.5,0.1,setosa
12 | 5.4,3.7,1.5,0.2,setosa
13 | 4.8,3.4,1.6,0.2,setosa
14 | 4.8,3.0,1.4,0.1,setosa
15 | 4.3,3.0,1.1,0.1,setosa
16 | 5.8,4.0,1.2,0.2,setosa
17 | 5.7,4.4,1.5,0.4,setosa
18 | 5.4,3.9,1.3,0.4,setosa
19 | 5.1,3.5,1.4,0.3,setosa
20 | 5.7,3.8,1.7,0.3,setosa
21 | 5.1,3.8,1.5,0.3,setosa
22 | 5.4,3.4,1.7,0.2,setosa
23 | 5.1,3.7,1.5,0.4,setosa
24 | 4.6,3.6,1.0,0.2,setosa
25 | 5.1,3.3,1.7,0.5,setosa
26 | 4.8,3.4,1.9,0.2,setosa
27 | 5.0,3.0,1.6,0.2,setosa
28 | 5.0,3.4,1.6,0.4,setosa
29 | 5.2,3.5,1.5,0.2,setosa
30 | 5.2,3.4,1.4,0.2,setosa
31 | 4.7,3.2,1.6,0.2,setosa
32 | 4.8,3.1,1.6,0.2,setosa
33 | 5.4,3.4,1.5,0.4,setosa
34 | 5.2,4.1,1.5,0.1,setosa
35 | 5.5,4.2,1.4,0.2,setosa
36 | 4.9,3.1,1.5,0.1,setosa
37 | 5.0,3.2,1.2,0.2,setosa
38 | 5.5,3.5,1.3,0.2,setosa
39 | 4.9,3.1,1.5,0.1,setosa
40 | 4.4,3.0,1.3,0.2,setosa
41 | 5.1,3.4,1.5,0.2,setosa
42 | 5.0,3.5,1.3,0.3,setosa
43 | 4.5,2.3,1.3,0.3,setosa
44 | 4.4,3.2,1.3,0.2,setosa
45 | 5.0,3.5,1.6,0.6,setosa
46 | 5.1,3.8,1.9,0.4,setosa
47 | 4.8,3.0,1.4,0.3,setosa
48 | 5.1,3.8,1.6,0.2,setosa
49 | 4.6,3.2,1.4,0.2,setosa
50 | 5.3,3.7,1.5,0.2,setosa
51 | 5.0,3.3,1.4,0.2,setosa
52 | 7.0,3.2,4.7,1.4,versicolor
53 | 6.4,3.2,4.5,1.5,versicolor
54 | 6.9,3.1,4.9,1.5,versicolor
55 | 5.5,2.3,4.0,1.3,versicolor
56 | 6.5,2.8,4.6,1.5,versicolor
57 | 5.7,2.8,4.5,1.3,versicolor
58 | 6.3,3.3,4.7,1.6,versicolor
59 | 4.9,2.4,3.3,1.0,versicolor
60 | 6.6,2.9,4.6,1.3,versicolor
61 | 5.2,2.7,3.9,1.4,versicolor
62 | 5.0,2.0,3.5,1.0,versicolor
63 | 5.9,3.0,4.2,1.5,versicolor
64 | 6.0,2.2,4.0,1.0,versicolor
65 | 6.1,2.9,4.7,1.4,versicolor
66 | 5.6,2.9,3.6,1.3,versicolor
67 | 6.7,3.1,4.4,1.4,versicolor
68 | 5.6,3.0,4.5,1.5,versicolor
69 | 5.8,2.7,4.1,1.0,versicolor
70 | 6.2,2.2,4.5,1.5,versicolor
71 | 5.6,2.5,3.9,1.1,versicolor
72 | 5.9,3.2,4.8,1.8,versicolor
73 | 6.1,2.8,4.0,1.3,versicolor
74 | 6.3,2.5,4.9,1.5,versicolor
75 | 6.1,2.8,4.7,1.2,versicolor
76 | 6.4,2.9,4.3,1.3,versicolor
77 | 6.6,3.0,4.4,1.4,versicolor
78 | 6.8,2.8,4.8,1.4,versicolor
79 | 6.7,3.0,5.0,1.7,versicolor
80 | 6.0,2.9,4.5,1.5,versicolor
81 | 5.7,2.6,3.5,1.0,versicolor
82 | 5.5,2.4,3.8,1.1,versicolor
83 | 5.5,2.4,3.7,1.0,versicolor
84 | 5.8,2.7,3.9,1.2,versicolor
85 | 6.0,2.7,5.1,1.6,versicolor
86 | 5.4,3.0,4.5,1.5,versicolor
87 | 6.0,3.4,4.5,1.6,versicolor
88 | 6.7,3.1,4.7,1.5,versicolor
89 | 6.3,2.3,4.4,1.3,versicolor
90 | 5.6,3.0,4.1,1.3,versicolor
91 | 5.5,2.5,4.0,1.3,versicolor
92 | 5.5,2.6,4.4,1.2,versicolor
93 | 6.1,3.0,4.6,1.4,versicolor
94 | 5.8,2.6,4.0,1.2,versicolor
95 | 5.0,2.3,3.3,1.0,versicolor
96 | 5.6,2.7,4.2,1.3,versicolor
97 | 5.7,3.0,4.2,1.2,versicolor
98 | 5.7,2.9,4.2,1.3,versicolor
99 | 6.2,2.9,4.3,1.3,versicolor
100 | 5.1,2.5,3.0,1.1,versicolor
101 | 5.7,2.8,4.1,1.3,versicolor
102 | 6.3,3.3,6.0,2.5,virginica
103 | 5.8,2.7,5.1,1.9,virginica
104 | 7.1,3.0,5.9,2.1,virginica
105 | 6.3,2.9,5.6,1.8,virginica
106 | 6.5,3.0,5.8,2.2,virginica
107 | 7.6,3.0,6.6,2.1,virginica
108 | 4.9,2.5,4.5,1.7,virginica
109 | 7.3,2.9,6.3,1.8,virginica
110 | 6.7,2.5,5.8,1.8,virginica
111 | 7.2,3.6,6.1,2.5,virginica
112 | 6.5,3.2,5.1,2.0,virginica
113 | 6.4,2.7,5.3,1.9,virginica
114 | 6.8,3.0,5.5,2.1,virginica
115 | 5.7,2.5,5.0,2.0,virginica
116 | 5.8,2.8,5.1,2.4,virginica
117 | 6.4,3.2,5.3,2.3,virginica
118 | 6.5,3.0,5.5,1.8,virginica
119 | 7.7,3.8,6.7,2.2,virginica
120 | 7.7,2.6,6.9,2.3,virginica
121 | 6.0,2.2,5.0,1.5,virginica
122 | 6.9,3.2,5.7,2.3,virginica
123 | 5.6,2.8,4.9,2.0,virginica
124 | 7.7,2.8,6.7,2.0,virginica
125 | 6.3,2.7,4.9,1.8,virginica
126 | 6.7,3.3,5.7,2.1,virginica
127 | 7.2,3.2,6.0,1.8,virginica
128 | 6.2,2.8,4.8,1.8,virginica
129 | 6.1,3.0,4.9,1.8,virginica
130 | 6.4,2.8,5.6,2.1,virginica
131 | 7.2,3.0,5.8,1.6,virginica
132 | 7.4,2.8,6.1,1.9,virginica
133 | 7.9,3.8,6.4,2.0,virginica
134 | 6.4,2.8,5.6,2.2,virginica
135 | 6.3,2.8,5.1,1.5,virginica
136 | 6.1,2.6,5.6,1.4,virginica
137 | 7.7,3.0,6.1,2.3,virginica
138 | 6.3,3.4,5.6,2.4,virginica
139 | 6.4,3.1,5.5,1.8,virginica
140 | 6.0,3.0,4.8,1.8,virginica
141 | 6.9,3.1,5.4,2.1,virginica
142 | 6.7,3.1,5.6,2.4,virginica
143 | 6.9,3.1,5.1,2.3,virginica
144 | 5.8,2.7,5.1,1.9,virginica
145 | 6.8,3.2,5.9,2.3,virginica
146 | 6.7,3.3,5.7,2.5,virginica
147 | 6.7,3.0,5.2,2.3,virginica
148 | 6.3,2.5,5.0,1.9,virginica
149 | 6.5,3.0,5.2,2.0,virginica
150 | 6.2,3.4,5.4,2.3,virginica
151 | 5.9,3.0,5.1,1.8,virginica
152 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var source = require('vinyl-source-stream');
2 | var gulp = require('gulp');
3 | var gutil = require('gulp-util');
4 | var browserify = require('browserify');
5 | var babelify = require('babelify');
6 | var watchify = require('watchify');
7 | var notify = require('gulp-notify');
8 |
9 | var stylus = require('gulp-stylus');
10 | var autoprefixer = require('gulp-autoprefixer');
11 | var uglify = require('gulp-uglify');
12 | var rename = require('gulp-rename');
13 | var buffer = require('vinyl-buffer');
14 |
15 | var browserSync = require('browser-sync');
16 | var reload = browserSync.reload;
17 | var historyApiFallback = require('connect-history-api-fallback')
18 |
19 |
20 | /*
21 | Styles Task
22 | */
23 |
24 | gulp.task('styles',function() {
25 | // move over fonts
26 |
27 | gulp.src('css/fonts/**.*')
28 | .pipe(gulp.dest('build/css/fonts'))
29 |
30 | // Compiles CSS
31 | gulp.src('css/style.styl')
32 | .pipe(stylus())
33 | .pipe(autoprefixer())
34 | .pipe(gulp.dest('./build/css/'))
35 | .pipe(reload({stream:true}))
36 | });
37 |
38 | /*
39 | Images
40 | */
41 | gulp.task('images',function(){
42 | gulp.src('css/images/**')
43 | .pipe(gulp.dest('./build/css/images'))
44 | });
45 |
46 | /*
47 | Browser Sync
48 | */
49 | gulp.task('browser-sync', function() {
50 | browserSync({
51 | // we need to disable clicks and forms for when we test multiple rooms
52 | server : {},
53 | middleware : [ historyApiFallback() ],
54 | ghostMode: false
55 | });
56 | });
57 |
58 | function handleErrors() {
59 | var args = Array.prototype.slice.call(arguments);
60 | notify.onError({
61 | title: 'Compile Error',
62 | message: '<%= error.message %>'
63 | }).apply(this, args);
64 | this.emit('end'); // Keep gulp from hanging on this task
65 | }
66 |
67 | function buildScript(file, watch) {
68 | var props = {
69 | entries: ['./scripts/' + file],
70 | debug : true,
71 | transform: [babelify.configure({stage : 0 })]
72 | };
73 |
74 | // watchify() if watch requested, otherwise run browserify() once
75 | var bundler = watch ? watchify(browserify(props)) : browserify(props);
76 |
77 | function rebundle() {
78 | var stream = bundler.bundle();
79 | return stream
80 | .on('error', handleErrors)
81 | .pipe(source(file))
82 | .pipe(gulp.dest('./build/'))
83 | // If you also want to uglify it
84 | // .pipe(buffer())
85 | // .pipe(uglify())
86 | // .pipe(rename('app.min.js'))
87 | // .pipe(gulp.dest('./build'))
88 | .pipe(reload({stream:true}))
89 | }
90 |
91 | // listen for an update and run rebundle
92 | bundler.on('update', function() {
93 | rebundle();
94 | gutil.log('Rebundle...');
95 | });
96 |
97 | // run it once the first time buildScript is called
98 | return rebundle();
99 | }
100 |
101 | gulp.task('scripts', function() {
102 | return buildScript('main.js', false); // this will run once because we set watch to false
103 | });
104 |
105 | // run 'scripts' task first, then watch for future changes
106 | gulp.task('default', ['images','styles','scripts','browser-sync'], function() {
107 | gulp.watch('css/**/*', ['styles']); // gulp watch for stylus changes
108 | return buildScript('main.js', true); // browserify watch for JS changes
109 | });
110 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sample Flask React App
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flask-react-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "autobind-decorator": "1.3.2",
13 | "babel": "5.8.23",
14 | "babelify": "6.4.0",
15 | "bootstrap-sass": "^3.3.5",
16 | "browser-sync": "2.9.11",
17 | "browserify": "11.2.0",
18 | "connect-history-api-fallback": "1.1.0",
19 | "d3": "^3.5.6",
20 | "gulp": "3.9.0",
21 | "gulp-autoprefixer": "3.1.0",
22 | "gulp-notify": "2.2.0",
23 | "gulp-rename": "1.2.2",
24 | "gulp-stylus": "2.1.0",
25 | "gulp-uglify": "1.4.2",
26 | "gulp-util": "3.0.7",
27 | "history": "1.12.5",
28 | "install": "0.1.8",
29 | "jquery": "^2.1.4",
30 | "re-base": "1.2.1",
31 | "react": "0.14.0",
32 | "react-addons-css-transition-group": "0.14.0",
33 | "react-catalyst": "0.3.0",
34 | "react-dom": "0.14.0",
35 | "react-mixin": "3.0.0",
36 | "react-router": "1.0.0-rc3",
37 | "vinyl-buffer": "1.0.0",
38 | "vinyl-source-stream": "1.1.0",
39 | "watchify": "3.4.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/react_example.py:
--------------------------------------------------------------------------------
1 | import os
2 | from sklearn import datasets
3 | from sklearn.ensemble import RandomForestClassifier
4 | from flask import Flask, make_response, jsonify, request
5 |
6 | DIR = os.path.dirname(os.path.abspath(__file__))
7 | app = Flask(__name__, static_folder='build')
8 |
9 |
10 | def get_model():
11 | iris = datasets.load_iris()
12 | model = RandomForestClassifier(n_estimators=1000).fit(iris.data, iris.target)
13 | labels = list(iris.target_names)
14 | return model, labels
15 |
16 |
17 | MODEL, LABELS = get_model()
18 |
19 |
20 | @app.route('/')
21 | def index():
22 | return make_response(open(os.path.join(DIR, 'index.html')).read())
23 |
24 |
25 | @app.route('/api/predict')
26 | def predict():
27 | def getter(label):
28 | return float(request.args.get(label, 0))
29 | try:
30 | features = map(getter, ['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth'])
31 | probs = MODEL.predict_proba(features)[0]
32 | except ValueError:
33 | probs = (1. / len(LABELS) for _ in LABELS)
34 |
35 | val = {"data": [{"label": label, "prob": prob} for label, prob in zip(LABELS, probs)]}
36 | return jsonify(val)
37 |
38 |
39 | if __name__ == '__main__':
40 | app.run(port=5001)
41 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==1.0
2 | itsdangerous==0.24
3 | Jinja2==2.11.3
4 | MarkupSafe==0.23
5 | numpy==1.22.0
6 | scikit-learn==0.16.1
7 | scipy==0.16.1
8 | sklearn==0.0
9 | Werkzeug==2.2.3
10 | wheel==0.38.1
11 |
--------------------------------------------------------------------------------
/scripts/barChart.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var $ = require('jquery');
3 | var ns = {}
4 |
5 | ns.create = function(el, props){
6 | var svg = d3.select(el)
7 | .append('svg')
8 | .attr('width', props.width)
9 | .attr('height', props.height)
10 | .attr('class', 'barChart');
11 |
12 | var h = this._helpers(el, {
13 | width: props.width,
14 | height: props.height
15 | })
16 |
17 | $.get(this._url(props), function(data){
18 | svg.selectAll('rect')
19 | .data(data.data, function(d){ return d.label;})
20 | .enter()
21 | .append('rect')
22 | .attr('width', h.barWidth)
23 | .attr('fill', function(d){return h.c(d.label)})
24 | .attr('x', function(d, i) { return i * (h.barWidth + h.barPadding) })
25 | .attr('height', 0)
26 | .attr('y', h.height)
27 | .transition()
28 | .duration(h.duration)
29 | .ease(h.ease)
30 | .attr('height', function(d){return h.height - h.y(d.prob)})
31 | .attr('y', function(d){return h.y(d.prob)});
32 |
33 | var text = svg.selectAll('text')
34 | .data(data.data, function(d){ return d.label; })
35 |
36 | text.enter()
37 | .append('text')
38 | .attr('x', function(d, i) { return i * (h.barWidth + h.barPadding) })
39 | .attr('dx', h.barWidth/2)
40 | .attr('dy', '1.2em')
41 | .attr('text-anchor', 'middle')
42 | .text(function(d) {return (100 * d.prob).toFixed(1) + '%';})
43 | .attr('fill', 'white')
44 | .attr('y', h.height)
45 | .transition()
46 | .duration(h.duration)
47 | .ease(h.ease)
48 | .attr('y', function(d){ return h.y(d.prob)});
49 |
50 | text.enter()
51 | .append('text')
52 | .attr('y', 10 + h.height)
53 | .attr('x', function(d, i) { return i * (h.barWidth + h.barPadding);})
54 | .attr('dx', h.barWidth/2)
55 | .attr('text-anchor', 'middle')
56 | .attr('fill', 'black')
57 | .text(function(d) { return d.label;});
58 | })
59 | }
60 |
61 | ns._helpers = function(el, props){
62 | var margin = {top: 0, right: 20, bottom: 30, left: 40},
63 | width = props.width - margin.left - margin.right,
64 | height = props.height - margin.top - margin.bottom,
65 | yScale = d3.scale.linear()
66 | .domain([0, 1])
67 | .range([height, 0]),
68 | colorScale = d3.scale.category10(),
69 | barWidth = width / 3;
70 |
71 | return {
72 | y: yScale,
73 | c: colorScale,
74 | width: width,
75 | height: height,
76 | barWidth: barWidth,
77 | barPadding: 2,
78 | duration: 300,
79 | ease: 'linear'
80 | }
81 | }
82 |
83 | ns._url = function(props) {
84 | return ('/api/predict?' +
85 | 'sepalLength=' + props.features['Sepal Length'] + '&' +
86 | 'sepalWidth=' + props.features['Sepal Width'] + '&' +
87 | 'petalLength=' + props.features['Petal Length'] + '&' +
88 | 'petalWidth=' + props.features['Petal Width']);
89 | }
90 |
91 | ns.update = function(el, props){
92 | var h = this._helpers(el, {
93 | width: props.width,
94 | height: props.height
95 | })
96 |
97 | $.get(this._url(props), function(data){
98 | var svg = d3.select(el).select('.barChart');
99 | svg.selectAll('rect')
100 | .data(data.data, function(d){ return d.label;})
101 | .transition()
102 | .duration(h.duration)
103 | .ease(h.ease)
104 | .attr('height', function(d){return h.height - h.y(d.prob)})
105 | .attr('y', function(d){return h.y(d.prob)});
106 |
107 | svg.selectAll('text')
108 | .data(data.data, function(d){ return d.label; })
109 | .transition()
110 | .duration(h.duration)
111 | .ease(h.ease)
112 | .text(function(d) {return (100 * d.prob).toFixed(1) + '%';})
113 | .attr('y', function(d){ return h.y(d.prob)});
114 | })
115 | }
116 |
117 | export default ns;
118 |
--------------------------------------------------------------------------------
/scripts/iris.csv:
--------------------------------------------------------------------------------
1 | Sepal Length,Sepal Width,Petal Length,Petal Width,Species
2 | 5.1,3.5,1.4,0.2,setosa
3 | 4.9,3.0,1.4,0.2,setosa
4 | 4.7,3.2,1.3,0.2,setosa
5 | 4.6,3.1,1.5,0.2,setosa
6 | 5.0,3.6,1.4,0.2,setosa
7 | 5.4,3.9,1.7,0.4,setosa
8 | 4.6,3.4,1.4,0.3,setosa
9 | 5.0,3.4,1.5,0.2,setosa
10 | 4.4,2.9,1.4,0.2,setosa
11 | 4.9,3.1,1.5,0.1,setosa
12 | 5.4,3.7,1.5,0.2,setosa
13 | 4.8,3.4,1.6,0.2,setosa
14 | 4.8,3.0,1.4,0.1,setosa
15 | 4.3,3.0,1.1,0.1,setosa
16 | 5.8,4.0,1.2,0.2,setosa
17 | 5.7,4.4,1.5,0.4,setosa
18 | 5.4,3.9,1.3,0.4,setosa
19 | 5.1,3.5,1.4,0.3,setosa
20 | 5.7,3.8,1.7,0.3,setosa
21 | 5.1,3.8,1.5,0.3,setosa
22 | 5.4,3.4,1.7,0.2,setosa
23 | 5.1,3.7,1.5,0.4,setosa
24 | 4.6,3.6,1.0,0.2,setosa
25 | 5.1,3.3,1.7,0.5,setosa
26 | 4.8,3.4,1.9,0.2,setosa
27 | 5.0,3.0,1.6,0.2,setosa
28 | 5.0,3.4,1.6,0.4,setosa
29 | 5.2,3.5,1.5,0.2,setosa
30 | 5.2,3.4,1.4,0.2,setosa
31 | 4.7,3.2,1.6,0.2,setosa
32 | 4.8,3.1,1.6,0.2,setosa
33 | 5.4,3.4,1.5,0.4,setosa
34 | 5.2,4.1,1.5,0.1,setosa
35 | 5.5,4.2,1.4,0.2,setosa
36 | 4.9,3.1,1.5,0.1,setosa
37 | 5.0,3.2,1.2,0.2,setosa
38 | 5.5,3.5,1.3,0.2,setosa
39 | 4.9,3.1,1.5,0.1,setosa
40 | 4.4,3.0,1.3,0.2,setosa
41 | 5.1,3.4,1.5,0.2,setosa
42 | 5.0,3.5,1.3,0.3,setosa
43 | 4.5,2.3,1.3,0.3,setosa
44 | 4.4,3.2,1.3,0.2,setosa
45 | 5.0,3.5,1.6,0.6,setosa
46 | 5.1,3.8,1.9,0.4,setosa
47 | 4.8,3.0,1.4,0.3,setosa
48 | 5.1,3.8,1.6,0.2,setosa
49 | 4.6,3.2,1.4,0.2,setosa
50 | 5.3,3.7,1.5,0.2,setosa
51 | 5.0,3.3,1.4,0.2,setosa
52 | 7.0,3.2,4.7,1.4,versicolor
53 | 6.4,3.2,4.5,1.5,versicolor
54 | 6.9,3.1,4.9,1.5,versicolor
55 | 5.5,2.3,4.0,1.3,versicolor
56 | 6.5,2.8,4.6,1.5,versicolor
57 | 5.7,2.8,4.5,1.3,versicolor
58 | 6.3,3.3,4.7,1.6,versicolor
59 | 4.9,2.4,3.3,1.0,versicolor
60 | 6.6,2.9,4.6,1.3,versicolor
61 | 5.2,2.7,3.9,1.4,versicolor
62 | 5.0,2.0,3.5,1.0,versicolor
63 | 5.9,3.0,4.2,1.5,versicolor
64 | 6.0,2.2,4.0,1.0,versicolor
65 | 6.1,2.9,4.7,1.4,versicolor
66 | 5.6,2.9,3.6,1.3,versicolor
67 | 6.7,3.1,4.4,1.4,versicolor
68 | 5.6,3.0,4.5,1.5,versicolor
69 | 5.8,2.7,4.1,1.0,versicolor
70 | 6.2,2.2,4.5,1.5,versicolor
71 | 5.6,2.5,3.9,1.1,versicolor
72 | 5.9,3.2,4.8,1.8,versicolor
73 | 6.1,2.8,4.0,1.3,versicolor
74 | 6.3,2.5,4.9,1.5,versicolor
75 | 6.1,2.8,4.7,1.2,versicolor
76 | 6.4,2.9,4.3,1.3,versicolor
77 | 6.6,3.0,4.4,1.4,versicolor
78 | 6.8,2.8,4.8,1.4,versicolor
79 | 6.7,3.0,5.0,1.7,versicolor
80 | 6.0,2.9,4.5,1.5,versicolor
81 | 5.7,2.6,3.5,1.0,versicolor
82 | 5.5,2.4,3.8,1.1,versicolor
83 | 5.5,2.4,3.7,1.0,versicolor
84 | 5.8,2.7,3.9,1.2,versicolor
85 | 6.0,2.7,5.1,1.6,versicolor
86 | 5.4,3.0,4.5,1.5,versicolor
87 | 6.0,3.4,4.5,1.6,versicolor
88 | 6.7,3.1,4.7,1.5,versicolor
89 | 6.3,2.3,4.4,1.3,versicolor
90 | 5.6,3.0,4.1,1.3,versicolor
91 | 5.5,2.5,4.0,1.3,versicolor
92 | 5.5,2.6,4.4,1.2,versicolor
93 | 6.1,3.0,4.6,1.4,versicolor
94 | 5.8,2.6,4.0,1.2,versicolor
95 | 5.0,2.3,3.3,1.0,versicolor
96 | 5.6,2.7,4.2,1.3,versicolor
97 | 5.7,3.0,4.2,1.2,versicolor
98 | 5.7,2.9,4.2,1.3,versicolor
99 | 6.2,2.9,4.3,1.3,versicolor
100 | 5.1,2.5,3.0,1.1,versicolor
101 | 5.7,2.8,4.1,1.3,versicolor
102 | 6.3,3.3,6.0,2.5,virginica
103 | 5.8,2.7,5.1,1.9,virginica
104 | 7.1,3.0,5.9,2.1,virginica
105 | 6.3,2.9,5.6,1.8,virginica
106 | 6.5,3.0,5.8,2.2,virginica
107 | 7.6,3.0,6.6,2.1,virginica
108 | 4.9,2.5,4.5,1.7,virginica
109 | 7.3,2.9,6.3,1.8,virginica
110 | 6.7,2.5,5.8,1.8,virginica
111 | 7.2,3.6,6.1,2.5,virginica
112 | 6.5,3.2,5.1,2.0,virginica
113 | 6.4,2.7,5.3,1.9,virginica
114 | 6.8,3.0,5.5,2.1,virginica
115 | 5.7,2.5,5.0,2.0,virginica
116 | 5.8,2.8,5.1,2.4,virginica
117 | 6.4,3.2,5.3,2.3,virginica
118 | 6.5,3.0,5.5,1.8,virginica
119 | 7.7,3.8,6.7,2.2,virginica
120 | 7.7,2.6,6.9,2.3,virginica
121 | 6.0,2.2,5.0,1.5,virginica
122 | 6.9,3.2,5.7,2.3,virginica
123 | 5.6,2.8,4.9,2.0,virginica
124 | 7.7,2.8,6.7,2.0,virginica
125 | 6.3,2.7,4.9,1.8,virginica
126 | 6.7,3.3,5.7,2.1,virginica
127 | 7.2,3.2,6.0,1.8,virginica
128 | 6.2,2.8,4.8,1.8,virginica
129 | 6.1,3.0,4.9,1.8,virginica
130 | 6.4,2.8,5.6,2.1,virginica
131 | 7.2,3.0,5.8,1.6,virginica
132 | 7.4,2.8,6.1,1.9,virginica
133 | 7.9,3.8,6.4,2.0,virginica
134 | 6.4,2.8,5.6,2.2,virginica
135 | 6.3,2.8,5.1,1.5,virginica
136 | 6.1,2.6,5.6,1.4,virginica
137 | 7.7,3.0,6.1,2.3,virginica
138 | 6.3,3.4,5.6,2.4,virginica
139 | 6.4,3.1,5.5,1.8,virginica
140 | 6.0,3.0,4.8,1.8,virginica
141 | 6.9,3.1,5.4,2.1,virginica
142 | 6.7,3.1,5.6,2.4,virginica
143 | 6.9,3.1,5.1,2.3,virginica
144 | 5.8,2.7,5.1,1.9,virginica
145 | 6.8,3.2,5.9,2.3,virginica
146 | 6.7,3.3,5.7,2.5,virginica
147 | 6.7,3.0,5.2,2.3,virginica
148 | 6.3,2.5,5.0,1.9,virginica
149 | 6.5,3.0,5.2,2.0,virginica
150 | 6.2,3.4,5.4,2.3,virginica
151 | 5.9,3.0,5.1,1.8,virginica
152 |
--------------------------------------------------------------------------------
/scripts/main.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 |
4 | var Catalyst = require('react-catalyst');
5 | var ScatterPlot = require('./scatterPlot');
6 | var Bar = require('./barChart');
7 |
8 | /*
9 | * App
10 | */
11 | var App = React.createClass({
12 | mixins: [Catalyst.LinkedStateMixin],
13 | getInitialState: function(){
14 | var domain = {
15 | 'Sepal Width': [1.0, 5.0],
16 | 'Sepal Length': [3.5, 8.5],
17 | 'Petal Width': [0.0, 3.5],
18 | 'Petal Length': [0.0, 7.5]
19 | }
20 | var dimensions= Object.keys(domain);
21 | var features = {};
22 | for(var j=0; j < dimensions.length; j++){
23 | var key = dimensions[j];
24 | features[key] = 0.5 * (domain[key][0] + domain[key][1]) ;
25 | }
26 | return {
27 | domain: domain,
28 | features: features
29 | }
30 | },
31 | render: function(){
32 | return (
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
43 |
44 |
45 |
46 |
47 | )}
48 | });
49 |
50 | /*
51 | * Header
52 | */
53 | var Header = React.createClass({
54 | render: function(){
55 | return(
56 |
57 |
58 |
Interactive Models with Flask and ReactJS
59 |
Illustrating how to display an interactive model using the Python Flask framework and ReactJS . We use the famous iris dataset to train a random forest in scikit-learn , and put up an interactive dashboard giving predictions.
60 |
61 |
62 |
63 | )
64 | }
65 | });
66 |
67 | /*
68 | * Features
69 | */
70 | var Features = React.createClass({
71 | renderFeature: function(key){
72 | var domain = this.props.domain[key];
73 | var linkState = this.props.linkState;
74 | return (
75 |
76 |
{key}:
77 |
78 |
81 |
82 |
83 | )
84 | },
85 | render: function(){
86 | return(
87 |
88 |
Features
89 |
92 |
93 | )
94 | }
95 | });
96 |
97 | /*
98 | * BarChart
99 | */
100 | var BarChart = React.createClass({
101 | componentDidMount: function() {
102 | var el = ReactDom.findDOMNode(this);
103 | Bar.create(el, {
104 | features: this.props.features,
105 | width: '450',
106 | height: '400'});
107 | },
108 | componentDidUpdate: function() {
109 | var el = ReactDom.findDOMNode(this);
110 | Bar.update(el, {
111 | features: this.props.features,
112 | width: '450',
113 | height: '400'});
114 | },
115 | render: function(){
116 | return(
117 |
118 |
Class Probabilities
119 |
120 | )
121 | }
122 | });
123 |
124 |
125 | /*
126 | * ScatterNav
127 | */
128 | var ScatterNav = React.createClass({
129 | render: function(){
130 | return(
131 |
132 |
135 |
136 |
139 |
140 | )
141 | }
142 | });
143 |
144 | /*
145 | * Scatter
146 | */
147 | var Scatter = React.createClass({
148 | componentDidMount: function() {
149 | var el = ReactDom.findDOMNode(this);
150 | ScatterPlot.create(
151 | el, {
152 | width: this.props.width,
153 | height: this.props.height,
154 | domain: this.props.domain,
155 | xVal: this.props.xVal,
156 | yVal: this.props.yVal,
157 | legend: this.props.legend,
158 | features: this.props.features
159 | })
160 | },
161 | componentDidUpdate: function() {
162 | var el = ReactDom.findDOMNode(this);
163 | ScatterPlot.update(
164 | el, {
165 | width: this.props.width,
166 | height: this.props.height,
167 | domain: this.props.domain,
168 | xVal: this.props.xVal,
169 | yVal: this.props.yVal,
170 | features: this.props.features
171 | })
172 | },
173 | render: function(){
174 | return
175 | }
176 | })
177 |
178 | ReactDom.render( , document.querySelector('#main'))
179 |
--------------------------------------------------------------------------------
/scripts/scatterPlot.js:
--------------------------------------------------------------------------------
1 | var d3 = require('d3');
2 | var ns = {};
3 |
4 | var plotPosition = function(el, features, xMap, yMap){
5 | d3.select(el).select('svg')
6 | .selectAll('.position')
7 | .data([features])
8 | .enter().append('circle')
9 | .attr('class', 'position')
10 | .attr('r', 2.5)
11 | .attr('cx', xMap)
12 | .attr('cy', yMap)
13 | .style('fill', 'black');
14 | }
15 |
16 | var updatePosition = function(el, features, xMap, yMap){
17 | d3.select(el).select('svg')
18 | .selectAll('.position')
19 | .data([features])
20 | .attr('class', 'position')
21 | .attr('r', 2.5)
22 | .attr('cx', xMap)
23 | .attr('cy', yMap)
24 | .style('fill', 'black');
25 | }
26 |
27 | ns.create = function(el, props) {
28 | var svg = d3.select(el)
29 | .append('svg')
30 | .attr('width', props.width)
31 | .attr('height', props.height)
32 | .attr('class', 'scatterChart');
33 |
34 | var h = this._helpers(el, {
35 | domain: props.domain,
36 | xVal: props.xVal,
37 | yVal: props.yVal,
38 | width: props.width,
39 | height: props.height
40 | });
41 |
42 | this._drawAxis(svg, {
43 | scales: h.scales,
44 | width: h.width,
45 | height: h.height,
46 | xVal: props.xVal,
47 | yVal: props.yVal
48 | });
49 |
50 | this._drawPoints(svg, {
51 | scales: h.scales,
52 | xVal: props.xVal,
53 | yVal: props.yVal,
54 | x: h.x,
55 | y: h.y,
56 | legend: props.legend
57 | });
58 |
59 | plotPosition(el, props.features, h.x, h.y)
60 | }
61 |
62 | ns.update = function(el, props){
63 | var h = this._helpers(el, props)
64 | updatePosition(el, props.features, h.x, h.y)
65 | }
66 |
67 | ns._helpers = function(el, props) {
68 | var margin = {top: 20, right: 30, bottom: 30, left: 40};
69 |
70 | var xVal = props.xVal,
71 | yVal = props.yVal,
72 | domain = {
73 | x: props.domain[xVal],
74 | y: props.domain[yVal]},
75 | height = props.height - margin.top - margin.bottom,
76 | width = props.width - margin.left - margin.right,
77 | x = d3.scale.linear()
78 | .domain(domain.x)
79 | .range([0, width]),
80 | y = d3.scale.linear()
81 | .domain(domain.y)
82 | .range([height, 0]),
83 | c = d3.scale.category10(),
84 | xMap = function(d){ return x(d[xVal])},
85 | yMap = function(d){ return y(d[yVal])};
86 |
87 | return {
88 | scales: {
89 | x: x,
90 | y: y,
91 | c: c},
92 | x: xMap,
93 | y: yMap,
94 | height: height,
95 | width: width
96 | }
97 | }
98 |
99 | ns._drawAxis = function(svg, props) {
100 | var xAxis = d3.svg.axis().scale(props.scales.x).orient('bottom'),
101 | yAxis = d3.svg.axis().scale(props.scales.y).orient('right');
102 |
103 | // x-axis
104 | svg.append('g')
105 | .attr('class', 'x axis')
106 | .attr('transform', 'translate(0,' + props.height + ')')
107 | .call(xAxis)
108 | .append('text')
109 | .attr('class', 'label')
110 | .attr('x', props.width - 5)
111 | .attr('y', -2)
112 | .style('text-anchor', 'end')
113 | .text(props.xVal);
114 |
115 | // y-axis
116 | svg.append('g')
117 | .attr('class', 'y axis')
118 | .attr('transform', 'translate(' + props.width + ', 0)')
119 | .call(yAxis)
120 | .append('text')
121 | .attr('class', 'label')
122 | .attr('transform', 'rotate(-90)')
123 | .attr('y', 6)
124 | .attr('dy', '-1.2em')
125 | .style('text-anchor', 'end')
126 | .text(props.yVal);
127 | }
128 |
129 | ns._drawPoints = function(svg, props) {
130 | d3.csv('/build/iris.csv', function(data) {
131 |
132 | data.forEach(function(d) {
133 | d[props.xVal] = +d[props.xVal];
134 | d[props.yVal] = +d[props.yVal];
135 | });
136 |
137 | // draw dots
138 | svg.selectAll('.dot')
139 | .data(data)
140 | .enter().append('circle')
141 | .attr('class', 'dot')
142 | .attr('r', 2.5)
143 | .attr('cx', props.x)
144 | .attr('cy', props.y)
145 | .style('opacity', 0.6)
146 | .style('fill', function(d) { return props.scales.c(d.Species);});
147 |
148 | if(props.legend){
149 | var legend = svg.selectAll('.legend')
150 | .data(props.scales.c.domain())
151 | .enter().append('g')
152 | .attr('class', 'legend')
153 | .attr('transform', function(d, i) {
154 | return 'translate(0,' + i * 13 + ')';
155 | });
156 |
157 | legend.append('rect')
158 | .attr('x', 10)
159 | .attr('width', 10)
160 | .attr('height', 10)
161 | .style('fill', props.scales.c);
162 |
163 | legend.append('text')
164 | .attr('x', 24)
165 | .attr('y', 5)
166 | .attr('dy', '.25em')
167 | .style('text-anchor', 'begin')
168 | .text(function(d) { return d;})
169 | }
170 | });
171 | }
172 |
173 | export default ns;
174 |
--------------------------------------------------------------------------------