Commit 3b622bce authored by Kruyff,D.L.W. (Dylan)'s avatar Kruyff,D.L.W. (Dylan)
Browse files

Better visualisations + multivariate data


Former-commit-id: 4f8966d6
parent af9ce874
......@@ -6,17 +6,19 @@ export interface RawData {
}
export interface LshData {
candidates: number[];
distances: number[];
hash_functions: number[];// number[][][][];
candidates: number[][][];
average_candidates: number[];
average_distances: number[];
distances: number[][][];
hash_functions: number[][][][];
parameters?: number[];
}
export interface TableInfoData {
prototypes: {
average: number[];
min: number[];
max: number[];
average: number[][];
min: number[][];
max: number[][];
}[];
distances: number[][];
}
......
......@@ -2,11 +2,12 @@
margin-right: 20px;
display: flex;
justify-content: center;
border-right: 1px solid;
border-bottom: 1px solid;
}
.subplot-container {
display: flex;
flex-wrap: wrap;
overflow-x: scroll;
width: 95%;
}
......
......@@ -44,13 +44,13 @@ export class LabelingWindowComponent implements OnInit {
}
async showSamples() {
this.topk = this.state.lshData.candidates
this.topk = this.state.lshData.average_candidates
.filter((candidate) => this.state.labels[candidate] !== true)
.slice(0, this.k);
this.subplots = [];
const values: number[][][] = await this.state.getWindow(this.topk);
for (const idx in this.topk) {
const window = values[idx].slice(0, 1);
const window = values[idx];
const data = [];
window.forEach((channel: number[], index: number) => {
data.push({
......@@ -70,7 +70,7 @@ export class LabelingWindowComponent implements OnInit {
data: data,
layout: {
grid: {
rows: 1, //this.state.queryWindow.length,
rows: this.state.queryWindow.length,
columns: 1,
subplots: subplots,
},
......@@ -85,7 +85,7 @@ export class LabelingWindowComponent implements OnInit {
b: 0,
pad: 4
},
height: 100,
height: 100 * values[0].length,
width: 300,
titlefont: {
size: 9
......
......@@ -20,7 +20,7 @@ export class LabelsComponent implements OnInit {
async createSubplots() {
this.goodLabels = [];
this.badLabels = [];
const labelWindows: number[][] = await this.state.getWindow(Object.keys(this.state.labels).map(Number));
const labelWindows: number[][][] = await this.state.getWindow(Object.keys(this.state.labels).map(Number));
Object.keys(this.state.labels).forEach((key, i) => {
const index = Number(key);
const plot =
......
......@@ -3,8 +3,8 @@
<app-overview-window style="z-index: 10"></app-overview-window>
<mat-tab-group animationDuration="0ms" (selectedTabChange)="changeTab($event)">
<mat-tab label="Training">
<app-labeling-window></app-labeling-window>
<app-table-overview></app-table-overview>
<app-labeling-window></app-labeling-window>
</mat-tab>
<mat-tab label="Labeled data">
<app-labels></app-labels>
......
<zingchart-angular #chart [id]="id" [config]="config" (mousewheel)="zoom($event)" (click)="clicked($event)" [height]="150"></zingchart-angular>
<zingchart-angular #chart [id]="id" [config]="config" (mousewheel)="zoom($event)" (click)="clicked($event)" [height]="300"></zingchart-angular>
......@@ -16,6 +16,7 @@ export class OverviewWindowComponent implements OnInit {
public series = [];
public goodLabels = [];
public badLabels = [];
public candidateLabels = [];
public data;
public layout;
......@@ -93,9 +94,6 @@ export class OverviewWindowComponent implements OnInit {
});
// Initialize channels
this.state.rawData.forEach((channel, index) => {
if (index !== 0) {
return;
}
const data = [];
for (let i = 0; i < channel.values.length; i++) {
data.push([i, channel.values[i]]);
......@@ -150,37 +148,59 @@ export class OverviewWindowComponent implements OnInit {
};
console.log(this.config);
console.log("showing plot");
await this.debugClicked();
}
async updatePlot() {
console.log('updating plot');
if (!this.state.queryWindow) {
return;
}
this.goodLabels = [];
this.badLabels = [];
this.markers = [];
for (const index in this.state.labels) {
if (this.state.labels[index]) {
this.goodLabels.push([Number(index) * (12000 / 6), 0]);
this.goodLabels.push([Number(index), 0]);
this.markers.push({
type: 'area',
// BUG: For some reason the range values are multiplied by 10
range: [Number(index) * (12000 / 6) / 10, (Number(index) * (12000 / 6) + this.state.windowSize) / 10],
backgroundColor: '#4caf50',
range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
backgroundColor: "#4caf50",
});
} else {
this.badLabels.push([Number(index) * (12000 / 6), 0]);
this.badLabels.push([Number(index), -1]);
this.markers.push({
type: 'area',
// BUG: For some reason the range values are multiplied by 10
range: [Number(index) * (12000 / 6) / 10, (Number(index) * (12000 / 6) + this.state.windowSize) / 10],
backgroundColor: '#f44336',
range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
backgroundColor: "#f44336",
});
}
}
this.series[1].values = this.goodLabels;
this.series[2].values = this.badLabels;
this.config.scaleX.markers = this.markers;
zingchart.exec('zingchart-ng-1', 'setdata', {
// for (const index of this.state.lshData.average_candidates) {
// this.candidateLabels.push([Number(index), 1]);
// this.markers.push({
// type: 'area',
// // BUG: For some reason the range values are multiplied by 10
// range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
// backgroundColor: '#b1a343',
// });
// }
console.log(this.markers);
for (const channel of this.config.graphset) {
if (channel.type === 'line') {
channel.scaleX.markers = this.markers;
} else {
channel.series[0].values = this.goodLabels;
channel.series[1].values = this.badLabels;
channel.series[2].values = this.candidateLabels;
}
}
this.chart.setdata({
data: this.config
});
}
async updateCandidates(sliderIndex) {
......
......@@ -27,5 +27,11 @@
justify-content: center;
}
mat-slider {
display: flex;
justify-content: center;
width: 400px;
}
line { stroke: #5e4646; }
circle { stroke: #fff; stroke-width: 1.5px; }
<svg id="visual" width='500' height='300'></svg>
<!--<svg id="visual" width='500' height='300'></svg>-->
<div *ngIf="data" class="histogram">
<div class="container">
<plotly-plot (hover)="onHover($event)" [data]="hist.data" [layout]="hist.layout"></plotly-plot>
</div>
<div class="slider">
<mat-slider [min]="0" [max]="maxLength" step="1" [value]="sliderValue" (input)="setSliderValue($event)" thumbLabel tickInterval="5"></mat-slider>
</div>
</div>
<div *ngIf="data" class="container">
<div class="window">
<div class="plots">
<plotly-plot *ngFor="let data of this.data; index as i;" [class.hide]="i != sliderValue" [data]="data" [layout]="layout"></plotly-plot>
</div>
</div>
<mat-slider vertical min="0" [max]="maxLength" step="1" [(value)]="sliderValue" thumbLabel tickInterval="5"></mat-slider>
</div>
<script src='https://d3js.org/d3.v4.min.js'></script>
import { Component, OnInit } from '@angular/core';
import {Component, OnInit, ViewChild} from '@angular/core';
import {StateService} from '../state.service';
import * as d3 from 'd3';
......@@ -8,9 +8,11 @@ import * as d3 from 'd3';
styleUrls: ['./progress-view.component.css']
})
export class ProgressViewComponent implements OnInit {
@ViewChild('chart') chart;
public plot;
public data;
public layout;
public hist;
public amountOfCandidates;
public hover = 0;
......@@ -20,23 +22,60 @@ export class ProgressViewComponent implements OnInit {
ngOnInit(): void {
this.state.onNewTableInfo.subscribe(() => { this.showgraph(); });
this.state.onNewTableInfo.subscribe(() => { this.showHistogram(); });
}
showHistogram() {
const table = this.state._averageTable;
this.hist = {
data: [{
x: Object.keys(table),
y: Object.values(table).map((values: number[]) => values.length), // / (this.service.rawValues.length - this.service.windowSize)),
type: 'bar',
opacity: 0.5,
marker: {
color: Object.keys(table).map((key) => {
return this.getColor(Number(key) / Number(Object.keys(table)[Object.keys(table).length - 1]));
})
}
}],
layout: {
hovermode: 'closest',
autosize: true,
margin: {
l: 10,
r: 10,
t: 10,
b: 10,
pad: 4
},
xaxis: {
showticklabels: false
},
yaxis: {
showticklabels: false
},
height: 200,
width: 400,
}
};
}
onHover(data) {
console.log(data);
this.setSliderValue({value: data.points[0].x});
}
hoverPlot(averages) {
const subplots = [];
this.data = averages.map((prototype) => {
return [
{
x: [...Array(prototype.average.length).keys()],
y: prototype.average,
type: 'line',
line: {
color: 'red',
width: 3
}
},
{
x: [...Array(prototype.average.length).keys()],
y: prototype.max,
const channelData = [];
prototype.max.forEach((channel, index) => {
channelData.push({
x: [...Array(channel.length).keys()],
y: channel,
xaxis: 'x',
yaxis: `y${index + 2}`,
type: 'scatter',
fill: null,
mode: 'lines',
......@@ -44,10 +83,14 @@ export class ProgressViewComponent implements OnInit {
color: 'rgb(55, 128, 191)',
width: 3
}
},
{
x: [...Array(prototype.average.length).keys()],
y: prototype.min,
});
});
prototype.min.forEach((channel, index) => {
channelData.push({
x: [...Array(channel.length).keys()],
y: channel,
xaxis: 'x',
yaxis: `y${index + 2}`,
type: 'scatter',
fill: 'tonexty',
mode: 'lines',
......@@ -55,10 +98,32 @@ export class ProgressViewComponent implements OnInit {
color: 'rgb(55, 128, 191)',
width: 3
}
},
];
});
});
prototype.average.forEach((channel, index) => {
channelData.push({
x: [...Array(channel.length).keys()],
y: channel,
xaxis: 'x',
yaxis: `y${index + 2}`,
type: 'line',
line: {
color: 'red',
width: 3
}
});
});
return channelData;
});
for (let index = 0; index < this.state.queryWindow.length; index++) {
subplots.push([`xy${index + 2}`]);
}
this.layout = {
grid: {
rows: this.state.queryWindow.length,
columns: 1,
subplots: subplots,
},
showlegend: false,
hovermode: 'closest',
autosize: true,
......@@ -77,15 +142,21 @@ export class ProgressViewComponent implements OnInit {
zeroline: false,
showticklabels: false,
},
height: 300,
width: 300,
height: 400,
width: 400,
};
this.state.queryWindow.forEach((channel: number[], index: number) => {
this.layout[`yaxis${index + 2}`] = {
zeroline: false,
showticklabels: false,
};
});
}
public set sliderValue(v: number) {
this._sliderValue = v;
public setSliderValue(v) {
this._sliderValue = v.value;
d3.selectAll('circle').transition().style('stroke', undefined);
d3.select('#node-' + v).transition().style('stroke', 'black').style('stroke-width', 20);
d3.select('#node-' + v.value).transition().style('stroke', 'black').style('stroke-width', 20);
}
public get sliderValue(): number {
......@@ -93,11 +164,11 @@ export class ProgressViewComponent implements OnInit {
}
public get maxLength(): number {
return Object.keys(this.table).length;
return Object.keys(this.table).length - 1;
}
public get table() {
return this.state.table;
return this.state._averageTable;
}
async showgraph() {
......@@ -107,91 +178,91 @@ export class ProgressViewComponent implements OnInit {
this.hoverPlot(this.state.tableInfo.prototypes);
const distances = this.state.tableInfo.distances;
for (const key in this.table) {
const size = this.table[key].length;
nodes.push({id: key, group: Number(key), size: size});
}
for (const key in this.table) {
for (const key2 in this.table) {
if (key === key2) {
continue;
}
links.push({source: key, target: key2, value: 0.001 * (100 - 5 * distances[keys.indexOf(key)][keys.indexOf(key2)])});
}
}
const graph = {nodes, links};
const svg = d3.select('#visual');
const width = +svg.attr('width');
const height = +svg.attr('height');
svg.selectAll('*').remove();
const simulation = d3.forceSimulation()
.force('link', d3.forceLink().id((d: any) => d.id))
.force('charge', d3.forceManyBody().strength(100)) // Gravity force
.force('collide', d3.forceCollide().radius(25).iterations(3)) // Repulsion force
.force('center', d3.forceCenter(width / 2, height / 2)); // Position force
const link = svg.append('g')
.selectAll('line')
.data(graph.links)
.enter().append('line')
.attr('stroke', 'grey')
.attr('stroke-width', (d: any) => d.value);
const node = svg.append('g')
.selectAll('circle')
.data(graph.nodes)
.enter().append('circle')
.attr('r', (d: any) => 5 * Math.log(d.size) / Math.log(10))
.attr('fill', (d: any) => this.getColor(d.group / graph.nodes.length))
.attr('id', (d: any) => 'node-' + d.group)
.on('mouseover', (d: any) => {this.sliderValue = d.group; })
.call(d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended));
simulation
.nodes(graph.nodes as any)
.on('tick', ticked);
simulation.force<any>('link')
.links(graph.links);
function ticked() {
link
.attr('x1', (d: any) => d.source.x)
.attr('y1', (d: any) => d.source.y)
.attr('x2', (d: any) => d.target.x)
.attr('y2', (d: any) => d.target.y);
node
.attr('cx', (d: any) => d.x)
.attr('cy', (d: any) => d.y);
}
function dragstarted(d) {
if (!d3.event.active) {
simulation.alphaTarget(0.1).restart();
}
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) {
simulation.alphaTarget(0);
}
d.fx = null;
d.fy = null;
}
// for (const key in this.table) {
// const size = this.table[key].length;
// nodes.push({id: key, group: Number(key), size: size});
// }
// for (const key in this.table) {
// for (const key2 in this.table) {
// if (key === key2) {
// continue;
// }
// links.push({source: key, target: key2, value: 0.001 * (100 - 5 * distances[keys.indexOf(key)][keys.indexOf(key2)])});
// }
// }
// const graph = {nodes, links};
//
// const svg = d3.select('#visual');
// const width = +svg.attr('width');
// const height = +svg.attr('height');
//
// svg.selectAll('*').remove();
//
// const simulation = d3.forceSimulation()
// .force('link', d3.forceLink().id((d: any) => d.id))
// .force('charge', d3.forceManyBody().strength(100)) // Gravity force
// .force('collide', d3.forceCollide().radius(25).iterations(3)) // Repulsion force
// .force('center', d3.forceCenter(width / 2, height / 2)); // Position force
//
// const link = svg.append('g')
// .selectAll('line')
// .data(graph.links)
// .enter().append('line')
// .attr('stroke', 'grey')
// .attr('stroke-width', (d: any) => d.value);
//
// const node = svg.append('g')
// .selectAll('circle')
// .data(graph.nodes)
// .enter().append('circle')
// .attr('r', (d: any) => 5 * Math.log(d.size) / Math.log(10))
// .attr('fill', (d: any) => this.getColor(d.group / graph.nodes.length))
// .attr('id', (d: any) => 'node-' + d.group)
// .on('mouseover', (d: any) => {this.sliderValue = d.group; })
// .call(d3.drag()
// .on('start', dragstarted)
// .on('drag', dragged)
// .on('end', dragended));
//
// simulation
// .nodes(graph.nodes as any)
// .on('tick', ticked);
//
// simulation.force<any>('link')
// .links(graph.links);
//
// function ticked() {
// link
// .attr('x1', (d: any) => d.source.x)
// .attr('y1', (d: any) => d.source.y)
// .attr('x2', (d: any) => d.target.x)
// .attr('y2', (d: any) => d.target.y);
//
// node
// .attr('cx', (d: any) => d.x)
// .attr('cy', (d: any) => d.y);
// }
//
// function dragstarted(d) {