Commit 3363a745 authored by Sascha Herzinger's avatar Sascha Herzinger
Browse files

Added boxplot skeleton

parent 799734be
import Vue from 'vue'
import CorrelationAnalysis from '../vue/charts/CorrelationAnalysis.vue'
import Boxplot from '../vue/charts/Boxplot.vue'
export default class {
constructor () {
this.AVAILABLE_CHARTS = {
[CorrelationAnalysis.name]: CorrelationAnalysis
[CorrelationAnalysis.name]: CorrelationAnalysis,
[Boxplot.name]: Boxplot
}
}
......
<template>
<div :class="`fjs-boxplot fjs-vm-uid-${this._uid}`">
<div class="fjs-data-box-container">
<data-box class="fjs-data-box"
header="Numerical Variables"
dataType="numerical"
v-on:update="update_numData">
</data-box>
<data-box class="fjs-data-box"
header="Categorical Variables"
dataType="categorical"
v-on:update="update_catData">
</data-box>
</div>
<div class="fjs-parameter-container">
</div>
<div class="fjs-vis-container">
<svg :width="width"
:height="height">
<g :transform="`translate(${margin.left}, ${margin.top})`">
</g>
</svg>
</div>
</div>
</template>
<script>
import DataBox from '../components/DataBox.vue'
import store from '../../store/store'
import requestHandling from '../methods/run-analysis'
import * as d3 from 'd3'
import { TweenLite } from 'gsap'
import svgtooltip from '../directives/v-svgtooltip'
import TaskView from '../components/TaskView.vue'
import deepFreeze from 'deep-freeze-strict'
export default {
name: 'boxplot',
data () {
return {
width: 0,
height: 0,
numData: [],
catData: [],
results: {}
}
},
computed: {
args () {
return {
variables: this.numData.map(d => `$${d}$`),
categories: this.catData.map(d => `$${d}$`),
subsets: store.getters.subsets
}
},
validArgs () {
return this.numData.length > 0
},
margin () {
const left = 10
const top = 10
const right = 10
const bottom = 10
return { left, top, right, bottom }
},
padded () {
const width = this.width - this.margin.left - this.margin.right
const height = this.height - this.margin.top - this.margin.bottom
return { width, height }
},
boxplotWidth () {
return this.padded.width / this.results.numOfBoxplots - this.results.numOfBoxplots
}
},
watch: {
'args': {
handler: function (newArgs, oldArgs) {
// if our data selection change we will want to re-initialize the current view
const init = JSON.stringify(newArgs.variables) !== JSON.stringify(oldArgs.variables) ||
JSON.stringify(newArgs.categories) !== JSON.stringify(oldArgs.categories)
if (this.validArgs) {
this.runAnalysisWrapper({init, args: this.args})
}
}
}
},
methods: {
update_numData (ids) {
this.numData = ids
},
update_catData (ids) {
this.catData = ids
},
handleResize () {
const container = this.$el.querySelector(`.fjs-vm-uid-${this._uid} .fjs-vis-container svg`)
// noinspection JSSuspiciousNameCombination
this.height = container.getBoundingClientRect().width
this.width = container.getBoundingClientRect().width
},
runAnalysisWrapper ({args}) {
// function made available via requestHandling mixin
this.runAnalysis({task_name: 'compute-boxplot', args})
.then(response => {
const results = JSON.parse(response)
const data = JSON.parse(results.data)
results.data = Object.keys(data).map(key => data[key])
deepFreeze(results) // massively improve performance by telling Vue that the objects properties won't change
this.results = results
})
.catch(error => console.error(error))
}
},
components: {
DataBox,
TaskView
},
mixins: [
requestHandling,
svgtooltip
],
mounted () {
window.addEventListener('resize', this.handleResize)
this.handleResize()
},
beforeDestroy () {
window.removeEventListener('resize', this.handleResize)
}
}
</script>
<style lang="sass" scoped>
@import './src/assets/base.sass'
*
font-family: Roboto, sans-serif
.fjs-boxplot
height: 100%
width: 100%
display: flex
flex-direction: column
.fjs-data-box-container
height: 160px
display: flex
justify-content: space-around
.fjs-vis-container
flex: 1
display: flex
svg
flex: 1
</style>
......@@ -39,14 +39,14 @@
y="-10"
text-anchor="middle"
font-size="16">
{{ shownAnalysisResults.x_label }}
{{ shownResults.x_label }}
</text>
<text :x="padded.width + 10"
:y="padded.height / 2"
text-anchor="middle"
font-size="16"
:transform="`rotate(90 ${padded.width + 10} ${padded.height / 2})`">
{{ shownAnalysisResults.y_label }}
{{ shownResults.y_label }}
</text>
<circle class="fjs-scatterplot-point"
:cx="scales.x(point.x)"
......@@ -73,15 +73,15 @@
<caption>Selected points</caption>
<tr>
<td>Coefficient</td>
<td>{{ tmpAnalysisResults.coef }}</td>
<td>{{ tmpResults.coef }}</td>
</tr>
<tr>
<td>p-value</td>
<td>{{ tmpAnalysisResults.p_value }}</td>
<td>{{ tmpResults.p_value }}</td>
</tr>
<tr>
<td>Method</td>
<td>{{ tmpAnalysisResults.method }}</td>
<td>{{ tmpResults.method }}</td>
</tr>
<tr>
<td>#Points</td>
......@@ -89,7 +89,7 @@
</tr>
</table>
<table class="fjs-stats-table"
v-for="(stats, i) in tmpAnalysisResults.subsets">
v-for="(stats, i) in tmpResults.subsets">
<caption>Subset: {{ i + 1 }}</caption>
<tr>
<td>Coefficient</td>
......@@ -101,11 +101,11 @@
</tr>
<tr>
<td>Method</td>
<td>{{ tmpAnalysisResults.method }}</td>
<td>{{ tmpResults.method }}</td>
</tr>
<tr>
<td>#Points</td>
<td>{{ tmpPoints.subsets.filter(d => d === i).length }}</td>
<td>{{ tmpPoints.subsets.filter(function(d) { return d === i}).length }}</td>
</tr>
</table>
</div>
......@@ -138,7 +138,7 @@
params: {
method: 'pearson'
},
shownAnalysisResults: {
shownResults: { // initially computed
init: true, // will disappear after being initially set
coef: 0,
p_value: 0,
......@@ -155,7 +155,7 @@
}
}
},
tmpAnalysisResults: {
tmpResults: { // on-the-fly computed
init: true, // will disappear after being initially set
coef: 0,
p_value: 0,
......@@ -182,8 +182,8 @@
idFilter () {
return store.getters.filter('ids')
},
disabled () {
return this.xyData.length !== 2
validArgs () {
return this.xyData.length === 2
},
args () {
return {
......@@ -217,16 +217,16 @@
const subsets = []
const annotations = []
let all = []
if (!this.shownAnalysisResults.init) {
all = this.shownAnalysisResults.data.map(d => {
const x = d[this.shownAnalysisResults.x_label]
const y = d[this.shownAnalysisResults.y_label]
if (!this.shownResults.init) {
all = this.shownResults.data.map(d => {
const x = d[this.shownResults.x_label]
const y = d[this.shownResults.y_label]
const id = d.id
const subset = d.subset
const annotation = d.annotation
const tooltip = {
[this.shownAnalysisResults.x_label]: x,
[this.shownAnalysisResults.y_label]: y,
[this.shownResults.x_label]: x,
[this.shownResults.y_label]: y,
subset
}
if (typeof annotation !== 'undefined') {
......@@ -249,10 +249,10 @@
const subsets = []
const annotations = []
let all = []
if (!this.tmpAnalysisResults.init) {
all = this.tmpAnalysisResults.data.map(d => {
const x = d[this.tmpAnalysisResults.x_label]
const y = d[this.tmpAnalysisResults.y_label]
if (!this.tmpResults.init) {
all = this.tmpResults.data.map(d => {
const x = d[this.tmpResults.x_label]
const y = d[this.tmpResults.y_label]
const id = d.id
const subset = d.subset
const annotation = d.annotation
......@@ -295,15 +295,15 @@
return { x1, x2, y1, y2 }
},
regLine () {
if (this.tmpAnalysisResults.init) {
if (this.tmpResults.init) {
return { x1: 0, x2: 0, y1: 0, y2: 0 }
}
const minX = d3.min(this.tmpPoints.xs)
const maxX = d3.max(this.tmpPoints.xs)
let x1 = this.scales.x(minX)
let y1 = this.scales.y(this.tmpAnalysisResults.intercept + this.tmpAnalysisResults.slope * minX)
let y1 = this.scales.y(this.tmpResults.intercept + this.tmpResults.slope * minX)
let x2 = this.scales.x(maxX)
let y2 = this.scales.y(this.tmpAnalysisResults.intercept + this.tmpAnalysisResults.slope * maxX)
let y2 = this.scales.y(this.tmpResults.intercept + this.tmpResults.slope * maxX)
x1 = x1 < 0 ? 0 : x1
x1 = x1 > this.width ? this.width : x1
......@@ -317,7 +317,7 @@
y2 = y2 < 0 ? 0 : y2
y2 = y2 > this.height ? this.height : y2
const tooltip = {Slope: this.tmpAnalysisResults.slope, Intercept: this.tmpAnalysisResults.intercept}
const tooltip = {Slope: this.tmpResults.slope, Intercept: this.tmpResults.intercept}
return { x1, x2, y1, y2, tooltip }
},
......@@ -347,7 +347,7 @@
const BINS = 14
let xBins = []
let yBins = []
if (!this.tmpAnalysisResults.init) {
if (!this.tmpResults.init) {
const [xMin, xMax] = d3.extent(this.tmpPoints.xs)
const [yMin, yMax] = d3.extent(this.tmpPoints.ys)
const xThresholds = d3.range(xMin, xMax, (xMax - xMin) / BINS)
......@@ -410,7 +410,7 @@
JSON.stringify(newArgs.annotations) !== JSON.stringify(oldArgs.annotations)
const args = this.args
args.id_filter = init ? [] : args.id_filter
if (!this.disabled) {
if (this.validArgs) {
this.runAnalysisWrapper({init, args})
}
}
......@@ -464,10 +464,10 @@
results.data = Object.keys(data).map(key => data[key])
deepFreeze(results) // massively improve performance by telling Vue that the objects properties won't change
if (init) {
this.shownAnalysisResults = results
this.tmpAnalysisResults = results
this.shownResults = results
this.tmpResults = results
} else {
this.tmpAnalysisResults = results
this.tmpResults = results
}
})
.catch(error => console.error(error))
......
<!doctype html>
<head>
<script src="http://localhost:8080/credentials.js"></script>
<script src="http://localhost:8080/fractal.js"></script>
</head>
<body>
<div style="height: 100%; width: 70%">
<div id="placeholder1"></div>
</div>
<div style="height: 1200px; width: 1200px">
<div id="placeholder2"></div>
</div>
</body>
<script>
/* eslint-disable no-undef */
const fjs = fractal.init({
handler: 'ada',
thisBaseURL: 'https://ada.parkinson.lu',
fractalisBaseURL: 'http://127.0.0.1:5000',
getAuth () {
return credentials1
}
})
fjs.clearCache()
.then(() => {
fjs.loadData([
{
dictionary: {
name: 'VITALHTCM',
projection: 'VITALHTCM',
label: 'Height',
fieldType: 'Integer',
isArray: false
},
data_set: 'ppmi.clinical_visit'
},
{
dictionary: {
name: 'VITALWGTKG',
projection: 'VITALWGTKG',
label: 'Weight',
fieldType: 'Double',
isArray: false
},
data_set: 'ppmi.clinical_visit'
}
])
})
fjs.setChart({chart: 'boxplot', selector: '#placeholder1'})
</script>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment