CorrelationAnalysis.vue 6.89 KB
Newer Older
1
<template>
2
3
4
  <div style="height: 100%; width: 100%">

    <div id="data-box-section" style="height: 25%;">
5
6
7
8
9
10
11
12
      <data-box header="X and Y variables"
                dataType="numerical"
                v-on:update="update_xyData">
      </data-box>
      <data-box header="Annotations"
                dataType="categorical"
                v-on:update="update_annotationData">
      </data-box>
13
    </div>
14
15
16
17
18
19
20
21
22

    <input id="run-analysis-btn"
           type="button"
           @click="runAnalysisWrapper"
           value="Run Analysis"
           :disabled="disabled"/>

    <div id="visualisation-section" style="height: 75%;">
      <svg width="100%" height="100%">
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        <g :style="{ transform: `translate(${margin.left}px, ${margin.top}px)` }">
          <circle :cx="scales.x(point.x)"
                  :cy="scales.y(point.y)"
                  r="4"
                  :data-idx="idx"
                  v-for="(point, idx) in points">
          </circle>
          <line id="lin-reg-line"
                :x1="linRegLine.x1"
                :x2="linRegLine.x2"
                :y1="linRegLine.y1"
                :y2="linRegLine.y2">
          </line>
          <g id="x-axis-1" class="fjs-corr-axis" :style="{ transform: `translate(0px, ${padded.height}px)` }"></g>
          <g id="x-axis-2" class="fjs-corr-axis"></g>
          <g id="y-axis-1" class="fjs-corr-axis"></g>
          <g id="y-axis-2" class="fjs-corr-axis" :style="{ transform: `translate(${padded.width}px, 0px)` }"></g>
          <g id="brush"></g>
41
42
43
44
        </g>
      </svg>
    </div>

45
  </div>
46
47
48
49
</template>


<script>
50
51
  import DataBox from '../DataBox.vue'
  import requestHandling from '../mixins/request-handling'
52
  import * as d3 from 'd3'
53
54
  export default {
    name: 'correlation-analysis',
55
    data () {
56
      return {
57
58
        width: 0,
        height: 0,
59
60
61
62
63
64
65
        margin: {
          left: 50,
          top: 50,
          right: 50,
          bottom: 50
        },

66
67
68
69
70
        xyData: [],
        annotationData: [],
        get args () {
          return {
            x: `$${this.xyData[0]}$`,
71
72
            y: `$${this.xyData[1]}$`,
            ids: this.selectedPoints.map(d => d.id)
73
          }
74
        },
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

        analysisResults: {
          coef: 0,
          p_value: 0,
          slope: 0,
          intercept: 0,
          method: '',
          x_label: '',
          y_label: '',
          get data() {
            return {
              id: [],
              [this.x_label]: [],
              [this.y_label]: []
            }
          }
91
        },
92
        selectedPoints: []
93
94
      }
    },
95
96
97
    computed: {
      disabled () {
        return this.xyData.length !== 2
98
99
100
101
102
      },
      padded () {
        const width = this.width - this.margin.left - this.margin.right
        const height = this.height - this.margin.top - this.margin.bottom
        return { width, height }
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
      },
      points () {
        return Object.keys(this.analysisResults.data.id).map(key => {
          return {
            x: this.analysisResults.data[this.analysisResults.x_label][key],
            y: this.analysisResults.data[this.analysisResults.y_label][key],
            id: this.analysisResults.data.id[key]
          }
        })
      },
      scales () {
        const x = d3.scaleLinear()
          .domain(d3.extent(this.points.map(d => d.x)))
          .range([0, this.padded.width])
        const y = d3.scaleLinear()
          .domain(d3.extent(this.points.map(d => d.y)))
          .range([this.padded.height, 0])
        return { x, y }
      },
      axis () {
        const x1 = d3.axisTop(this.scales.x)
        const y1 = d3.axisRight(this.scales.y)
        const x2 = d3.axisBottom(this.scales.x)
          .tickSizeInner(this.padded.height - 23)
          .tickFormat('')
        const y2 = d3.axisLeft(this.scales.y)
          .tickSizeInner(this.padded.width - 23)
          .tickFormat('')
        return { x1, x2, y1, y2 }
      },
      linRegLine () {
        const xarr = this.points.map(d => d.x)
        const minX = Math.min.apply(null, xarr)
        const maxX = Math.max.apply(null, xarr)
        let x1 = this.scales.x(minX)
        let y1 = this.scales.y(this.analysisResults.intercept + this.analysisResults.slope * minX)
        let x2 = this.scales.x(maxX)
        let y2 = this.scales.y(this.analysisResults.intercept + this.analysisResults.slope * maxX)

        x1 = x1 < 0 ? 0 : x1;
        x1 = x1 > this.width ? this.width : x1;

        x2 = x2 < 0 ? 0 : x2;
        x2 = x2 > this.width ? this.width : x2;

        y1 = y1 < 0 ? 0 : y1;
        y1 = y1 > this.height ? this.height : y1;

        y2 = y2 < 0 ? 0 : y2;
        y2 = y2 > this.height ? this.height : y2;

        return { x1, x2, y1, y2 }
      },
      brush () {
        return d3.brush()
          .extent([[0, 0], [this.padded.width, this.padded.height]])
          .on('end', function() {
            const [[x0, y0], [x1, y1]] = d3.event.selection
            this.selectedPoints = this.points.filter(d => {
              return x0 <= d.x && d.x <= x1 && y1 <= d.y && d.y <= y0;
            })
            this.runAnalysisWrapper()
          })
166
167
168
      }
    },
    watch: {
169
170
171
172
173
174
175
176
177
178
179
180
181
      'axis': {
        handler: function(newAxis) {
          d3.select('#x-axis-1').call(newAxis.x1)
          d3.select('#x-axis-2').call(newAxis.x2)
          d3.select('#y-axis-1').call(newAxis.y1)
          d3.select('#y-axis-2').call(newAxis.y2)
        },
        deep: true
      },
      'brush': {
        handler: function(newBrush) {
          d3.select('#brush').call(newBrush)
        }
182
183
      }
    },
184
185
186
187
188
189
190
    mounted() {
      window.addEventListener('resize', this.onResize)
      this.onResize() // initial call
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.onResize)
    },
191
192
193
194
195
196
197
    components: {
      DataBox
    },
    mixins: [
      requestHandling
    ],
    methods: {
198
      runAnalysisWrapper () {
199
        // function made available via requestHandling mixin
200
        this.runAnalysis({job_name: 'compute-correlation', args: this.args})
201
          .then(response => {
202
203
204
            const results = JSON.parse(response)
            results.data = JSON.parse(results.data)
            this.analysisResults = results
205
            this.$nextTick()
206
          })
207
208
209
210
211
212
          .catch(error => console.error(error))
      },
      onResize () {
        const section = this.$el.querySelector('#visualisation-section')
        this.height = section.clientHeight
        this.width = section.clientWidth
213
214
215
216
217
218
      },
      update_xyData (ids) {
        this.xyData = ids
      },
      update_annotationData (ids) {
        this.annotationData = ids
219
      }
220
221
222
223
224
225
    }
  }
</script>


<style scoped>
226
  #data-box-section {
227
228
229
230
    display: flex;
    flex-direction: row;
    justify-content: space-around;
  }
231

232
233
234
235
  #run-analysis-btn {
    width: 100%;
    height: 20px;
  }
236
237
238
239
240
241
242
243
244
245
246
247

  #lin-reg-line {
    stroke: #ff5e00;
    stroke-width: 4px;
  }
</style>

<!--CSS for dynamically created components-->
<style>
  .fjs-corr-axis .tick {
    shape-rendering: crispEdges;
  }
248
</style>