From 36a81897abe3172c215a1e15e66a972bc94acecf Mon Sep 17 00:00:00 2001 From: Dylan Kruyff <d.l.w.kruyff@students.uu.nl> Date: Sat, 28 Nov 2020 18:51:33 +0100 Subject: [PATCH] Refactor code Former-commit-id: a63b132756f09dfeb25d2cce191947dca121d2f6 --- AngularApp/prototype/src/app/api.service.ts | 71 ++- .../progress-view/progress-view.component.ts | 108 +--- AngularApp/prototype/src/app/state.service.ts | 112 ++-- .../table-overview.component.ts | 6 +- Flaskserver/.idea/workspace.xml | 19 +- Flaskserver/__pycache__/DBA.cpython-38.pyc | Bin 4285 -> 0 bytes Flaskserver/__pycache__/DBA.cpython-39.pyc | Bin 4269 -> 0 bytes Flaskserver/__pycache__/bigwig.cpython-38.pyc | Bin 7806 -> 0 bytes Flaskserver/__pycache__/main.cpython-38.pyc | Bin 14695 -> 3405 bytes .../__pycache__/preprocessing.cpython-38.pyc | Bin 0 -> 4100 bytes Flaskserver/__pycache__/pseudo.cpython-38.pyc | Bin 0 -> 7381 bytes Flaskserver/__pycache__/utils.cpython-38.pyc | Bin 1180 -> 0 bytes Flaskserver/{ => data}/.gitattributes | 0 Flaskserver/{ => data}/21.csv | 0 .../{ => data}/NW_Ground_Stations_2016.csv | 0 Flaskserver/{ => data}/chip_w-3000_r-25.h5 | 0 Flaskserver/{ => data}/data.pkl | 0 Flaskserver/{ => data}/parameters.npy | 0 Flaskserver/{ => data}/processed-data | Bin Flaskserver/{ => data}/processed-data.npy | 0 Flaskserver/{ => data}/query | 0 Flaskserver/{ => data}/test.bigWig | 0 Flaskserver/{ => libs}/DBA.py | 0 Flaskserver/{ => libs}/DBA_multivariate.py | 0 .../DBA_multivariate.cpython-38.pyc | Bin 6075 -> 6080 bytes .../__pycache__/bigwig.cpython-38.pyc} | Bin 7782 -> 7811 bytes Flaskserver/{ => libs}/bigwig.py | 0 Flaskserver/{ => libs}/setup.py | 0 Flaskserver/{ => libs}/utils.py | 0 Flaskserver/main.py | 590 +++++------------- Flaskserver/preprocessing.py | 118 ++++ Flaskserver/pseudo.py | 243 ++++++++ Flaskserver/topk.npy | 3 - 33 files changed, 653 insertions(+), 617 deletions(-) delete mode 100644 Flaskserver/__pycache__/DBA.cpython-38.pyc delete mode 100644 Flaskserver/__pycache__/DBA.cpython-39.pyc delete mode 100644 Flaskserver/__pycache__/bigwig.cpython-38.pyc create mode 100644 Flaskserver/__pycache__/preprocessing.cpython-38.pyc create mode 100644 Flaskserver/__pycache__/pseudo.cpython-38.pyc delete mode 100644 Flaskserver/__pycache__/utils.cpython-38.pyc rename Flaskserver/{ => data}/.gitattributes (100%) rename Flaskserver/{ => data}/21.csv (100%) rename Flaskserver/{ => data}/NW_Ground_Stations_2016.csv (100%) rename Flaskserver/{ => data}/chip_w-3000_r-25.h5 (100%) rename Flaskserver/{ => data}/data.pkl (100%) rename Flaskserver/{ => data}/parameters.npy (100%) rename Flaskserver/{ => data}/processed-data (100%) rename Flaskserver/{ => data}/processed-data.npy (100%) rename Flaskserver/{ => data}/query (100%) rename Flaskserver/{ => data}/test.bigWig (100%) rename Flaskserver/{ => libs}/DBA.py (100%) rename Flaskserver/{ => libs}/DBA_multivariate.py (100%) rename Flaskserver/{ => libs}/__pycache__/DBA_multivariate.cpython-38.pyc (97%) rename Flaskserver/{__pycache__/bigwig.cpython-39.pyc => libs/__pycache__/bigwig.cpython-38.pyc} (71%) rename Flaskserver/{ => libs}/bigwig.py (100%) rename Flaskserver/{ => libs}/setup.py (100%) rename Flaskserver/{ => libs}/utils.py (100%) create mode 100644 Flaskserver/preprocessing.py create mode 100644 Flaskserver/pseudo.py delete mode 100644 Flaskserver/topk.npy diff --git a/AngularApp/prototype/src/app/api.service.ts b/AngularApp/prototype/src/app/api.service.ts index 80573ff4..9b23e33d 100644 --- a/AngularApp/prototype/src/app/api.service.ts +++ b/AngularApp/prototype/src/app/api.service.ts @@ -7,11 +7,12 @@ export interface RawData { export interface LshData { candidates: number[][][]; - tables: {[bucket: string]: number[]}[]; + distances: number[][][]; average_candidates: number[]; average_distances: number[]; + tables: {[bucket: string]: number[]}[]; + average_table: {[bucket: string]: number[]}; samples: number[]; - distances: number[][][]; hash_functions: number[][]; parameters?: number[]; } @@ -25,23 +26,37 @@ export interface TableInfoData { distances: number[][]; } +export interface Parameters { + windowsize: number; + hashsize: number; + tablesize: number; + stepsize: number; +} + @Injectable({ providedIn: 'root' }) +/** + * This service acts as the interface between the client and server side. + */ export class ApiService { constructor() { } - // Read input data + /** + * Read input data. The format is a list of channels, where each channel is an object of type RawData + */ async readFile(): Promise<RawData[]> { - const response = await fetch('http://127.0.0.1:5000/read-mts-data'); + const response = await fetch('http://127.0.0.1:5000/read-data'); return await response.json(); } - // Split data into windows and normalize - async createWindows(parameters): Promise<any> { + /** + * Split the data into windows (server side) + */ + async createWindows(parameters: Parameters): Promise<any> { const postData = {parameters}; - const response = await fetch('http://127.0.0.1:5000/create-mts-windows', { + await fetch('http://127.0.0.1:5000/create-windows', { method: 'POST', headers: { 'Accept': 'application/json', @@ -51,32 +66,39 @@ export class ApiService { }); } - // Calculate parameters for LSH + find candidates using LSH - async lshInitial(query): Promise<LshData> { - const response = await fetch('http://127.0.0.1:5000/initialize', { + /** + * Get weights which will be applied to the LSH hash functions + */ + async getWeights(query: number[][], labels: {[index: number]: boolean}, weights: number[], hash_functions: number[][]): Promise<number[]> { + const response = await fetch('http://127.0.0.1:5000/weights', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: new Blob( [ JSON.stringify({query}) ], { type: 'text/plain' } ) + body: new Blob( [ JSON.stringify({query, labels, weights, hash_functions}) ], { type: 'text/plain' } ) }); return await response.json(); } - async getWeights(query: number[][], labels: {[index: number]: boolean}, weights: number[], hash_functions: number[][]): Promise<number[]> { - const response = await fetch('http://127.0.0.1:5000/weights', { + /** + * Do the first iteration of LSH and return important information + */ + async lshInitial(query: number[][]): Promise<LshData> { + const response = await fetch('http://127.0.0.1:5000/initialize', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: new Blob( [ JSON.stringify({query, labels, weights, hash_functions}) ], { type: 'text/plain' } ) + body: new Blob( [ JSON.stringify({query}) ], { type: 'text/plain' } ) }); return await response.json(); } - // Find candidates using LSH with weights + /** + * Do another iteration of LSH, with weights, and return important information + */ async lshUpdate(query, weights, parameters): Promise<LshData> { const response = await fetch('http://127.0.0.1:5000/update', { method: 'POST', @@ -89,20 +111,24 @@ export class ApiService { return await response.json(); } - // Get query window based on windows labeled correct - async getQueryWindow(window): Promise<number[][]> { + /** + * Get query window based on windows labeled correct + */ + async getQueryWindow(indices: number | {[index: number]: boolean}): Promise<number[][]> { const response = await fetch('http://127.0.0.1:5000/query', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: JSON.stringify({window}) + body: JSON.stringify({indices}) }); return await response.json(); } - // Get data of a window by indices + /** + * Get data of a window by indices + */ async getWindowByIndices(indices: number[]): Promise<number[][][]> { const response = await fetch('http://127.0.0.1:5000/window', { method: 'POST', @@ -115,14 +141,17 @@ export class ApiService { return await response.json(); } - async getTableInfo(windows): Promise<TableInfoData> { + /** + * Get additional information for a given table + */ + async getTableInfo(table: number[][]): Promise<TableInfoData> { const response = await fetch('http://127.0.0.1:5000/table-info', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: JSON.stringify({windows}) + body: JSON.stringify({table}) }); return await response.json(); } diff --git a/AngularApp/prototype/src/app/progress-view/progress-view.component.ts b/AngularApp/prototype/src/app/progress-view/progress-view.component.ts index 69e5c657..c379c9e5 100644 --- a/AngularApp/prototype/src/app/progress-view/progress-view.component.ts +++ b/AngularApp/prototype/src/app/progress-view/progress-view.component.ts @@ -1,6 +1,7 @@ import {Component, OnInit, ViewChild} from '@angular/core'; import {StateService} from '../state.service'; import * as d3 from 'd3'; +import {TableInfoData} from '../api.service'; @Component({ selector: 'app-progress-view', @@ -21,12 +22,14 @@ export class ProgressViewComponent implements OnInit { constructor(private state: StateService) { } ngOnInit(): void { - this.state.onNewTableInfo.subscribe(() => { this.showgraph(); }); - this.state.onNewTableInfo.subscribe(() => { this.showHistogram(); }); + this.state.onNewLshData.subscribe(() => { + this.showgraph(); + this.showHistogram(); + }); } showHistogram() { - const table = this.state._averageTable; + const table = this.state.lshData.average_table; this.hist = { data: [{ x: Object.keys(table), @@ -162,7 +165,7 @@ export class ProgressViewComponent implements OnInit { d3.selectAll('circle').transition().style('stroke', undefined); d3.select('#node-' + v.value).transition().style('stroke', 'black').style('stroke-width', 20); const data = this.hist; - data.data[0].marker.line.width = Object.keys(this.state._averageTable).map((key) => { + data.data[0].marker.line.width = Object.keys(this.state.lshData.average_table).map((key) => { return Number(key) === v.value ? 4 : 0; }); this.hist = data; @@ -177,102 +180,15 @@ export class ProgressViewComponent implements OnInit { } public get table() { - return this.state._averageTable; + return this.state.lshData.average_table; } async showgraph() { - const nodes = []; - const links = []; - const keys = Object.keys(this.table); - 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; - // } + const tableInfo: TableInfoData = await this.state.getTableInfo(Object.values(this.state.lshData.average_table)); + this.hoverPlot(tableInfo.prototypes); + const distances = tableInfo.distances; } + getColor(value) { const hue=((1-value)*120).toString(10); return ["hsl(",hue,",100%,50%)"].join(""); diff --git a/AngularApp/prototype/src/app/state.service.ts b/AngularApp/prototype/src/app/state.service.ts index c711c42c..4745c9eb 100644 --- a/AngularApp/prototype/src/app/state.service.ts +++ b/AngularApp/prototype/src/app/state.service.ts @@ -1,38 +1,42 @@ import {EventEmitter, Injectable} from '@angular/core'; -import {ApiService, LshData, RawData, TableInfoData} from './api.service'; +import {ApiService, LshData, Parameters, RawData, TableInfoData} from './api.service'; @Injectable({ providedIn: 'root' }) +/** + * This service acts as the state of the entire application. Components can subscribe to EventEmitters within this state to update their + * contents. + */ export class StateService { - public loadingProgress: number = 0; - + /** + * These are all LSH specific variables. The variables can be accessed using the getters and setters + */ private _rawData: RawData[]; private _lshData: LshData; - private _tableInfo: TableInfoData; private _queryWindow: number[][]; - private _table: {[bucket: string]: number[]}[]; - public _averageTable: {[bucket: string]: number[]}; private _weights: number[]; - - private _currentTab: number; private _labels = {}; - private _sliderValue; private _lshParameters: number[]; - - private states = []; - public windowSize = 120; public nrOfTables = 5; public hashSize = 5; public stepSize = 200; + + /** + * These are all GUI variables + */ + public loadingProgress = 0; public querySelectionMode = true; + private _currentTab: number; + private _sliderValue; + /** + * These are all EventEmitters. Subscribe to these if you want to be informed about an update in state. + */ public onNewData: EventEmitter<void> = new EventEmitter<void>(); public onNewWindows: EventEmitter<void> = new EventEmitter<void>(); public onNewQuery: EventEmitter<void> = new EventEmitter<void>(); - public onNewTable: EventEmitter<void> = new EventEmitter<void>(); - public onNewTableInfo: EventEmitter<void> = new EventEmitter<void>(); public onNewLshData: EventEmitter<void> = new EventEmitter<void>(); public onNewLabels: EventEmitter<void> = new EventEmitter<void>(); @@ -43,6 +47,9 @@ export class StateService { this.initialize(); } + /** + * This function initializes the application. It retrieves the raw data and creates windows. + */ async initialize(): Promise<void> { this.loadingProgress = 0; await this.getRawData(); @@ -51,72 +58,73 @@ export class StateService { this.loadingProgress = 100; } + /** + * This function resets the application. It re-creates the windows + */ async reset(): Promise<void> { this.loadingProgress = 50; await this.createWindows(); this.loadingProgress = 100; } + /** + * This function retrieves the raw data + */ async getRawData(): Promise<void> { this.rawData = await this.api.readFile(); } + /** + * This function creates the windows on the server side + */ async createWindows(): Promise<void> { await this.api.createWindows(this.parameters); this.onNewWindows.emit(); } + /** + * This function performs the first iteration of LSH + */ async lshInitial(): Promise<void> { + this._weights = Array(this._queryWindow.length).fill(1); this.lshData = await this.api.lshInitial(this._queryWindow); - console.log('data loaded'); this._lshParameters = this.lshData.parameters; - this._weights = [1, 1, 1]; - this.createTable(); } + /** + * This function performs every other iteration of LSH + */ async update(labels, hashFunctions): Promise<void> { this._weights = await this.api.getWeights(this._queryWindow, labels, this._weights, hashFunctions); - console.log(this._weights); this.lshData = await this.api.lshUpdate(this._queryWindow, this._weights, this._lshParameters); - this.createTable(); } + /** + * This function retrieves additional information given a table + */ async getTableInfo(table: number[][]): Promise<TableInfoData> { - // console.log(this.tableInfo); return await this.api.getTableInfo(table); } + /** + * This function retrieves the query + */ async getQueryWindow(windowIndex: number | {[index: number]: boolean}): Promise<number[][]> { this.queryWindow = await this.api.getQueryWindow(windowIndex); console.log(this.queryWindow); return this._queryWindow; } + /** + * This function retrieves the window given the window index + */ async getWindow(indices: number[]): Promise<number[][][]> { return await this.api.getWindowByIndices(indices); } - async createTable() { - console.log('setting table param'); - this.table = this.lshData.tables; - console.log('table param set'); - const averageTable = {}; - const length = this.lshData.average_distances.length; - const median = this.lshData.average_distances[Math.ceil(length / 2)]; - const stepsize = median / 10; - const indices: number[] = this.lshData.average_distances.map((x) => x > median * 2 ? 19 : Math.floor(x / stepsize)); - this.lshData.average_candidates.forEach((candidate: number, index: number) => { - if (averageTable[indices[index]] === undefined) - { - averageTable[indices[index]] = []; - } - averageTable[indices[index]].push(candidate); - }); - this._averageTable = averageTable; - console.log('table created'); - this.tableInfo = await this.getTableInfo(Object.values(this._averageTable)); - } - + /** + * These are all setters and getters + */ public set rawData(v: RawData[]) { this._rawData = v; console.log(this._rawData); @@ -137,26 +145,6 @@ export class StateService { return this._lshData; } - public set tableInfo(v: TableInfoData) { - this._tableInfo = v; - this.onNewTableInfo.emit(); - } - - public get tableInfo(): TableInfoData { - return this._tableInfo; - } - - public set table(v: {[bucket: string]: number[]}[]) { - console.log(v); - this._table = v; - console.log('emitting onNewTable'); - this.onNewTable.emit(); - } - - public get table(): {[bucket: string]: number[]}[] { - return this._table; - } - public set labels(v) { this._labels = v; this.onNewLabels.emit(); @@ -197,7 +185,7 @@ export class StateService { return this._lshParameters; } - public get parameters(): {[parameter: string]: number} { + public get parameters(): Parameters { return { windowsize: this.windowSize, hashsize: this.hashSize, diff --git a/AngularApp/prototype/src/app/table-overview/table-overview.component.ts b/AngularApp/prototype/src/app/table-overview/table-overview.component.ts index e7894e53..0aef1ba5 100644 --- a/AngularApp/prototype/src/app/table-overview/table-overview.component.ts +++ b/AngularApp/prototype/src/app/table-overview/table-overview.component.ts @@ -15,7 +15,7 @@ export class TableOverviewComponent implements OnInit { constructor(private state: StateService) { } ngOnInit(): void { - this.state.onNewTable.subscribe(() => { + this.state.onNewLshData.subscribe(() => { this.createHistograms(); this.createPrototypes(); }); @@ -132,7 +132,7 @@ export class TableOverviewComponent implements OnInit { console.log('creating table histograms'); this.subplots = []; this.averages = []; - const tables = this.state.table; + const tables = this.state.lshData.tables; console.log('start of table histograms'); tables.forEach((table, index) => { console.log(index); @@ -181,7 +181,7 @@ export class TableOverviewComponent implements OnInit { // } public get tables() { - return this.state.table; + return this.state.lshData.tables; } public get visible() { diff --git a/Flaskserver/.idea/workspace.xml b/Flaskserver/.idea/workspace.xml index 88459681..25b40fe7 100644 --- a/Flaskserver/.idea/workspace.xml +++ b/Flaskserver/.idea/workspace.xml @@ -20,18 +20,23 @@ </component> <component name="ChangeListManager"> <list default="true" id="556080ba-825c-4b55-a92a-867a4df4fb32" name="Default Changelist" comment=""> - <change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/api.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/api.service.ts" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/overview-window/overview-window.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/overview-window/overview-window.component.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/state.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/state.service.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/pseudo.py" beforeDir="false" afterPath="$PROJECT_DIR$/pseudo.py" afterDir="false" /> </list> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="LAST_RESOLUTION" value="IGNORE" /> </component> + <component name="FileTemplateManagerImpl"> + <option name="RECENT_TEMPLATES"> + <list> + <option value="Python Script" /> + </list> + </option> + </component> <component name="Git.Settings"> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." /> </component> @@ -50,6 +55,10 @@ <property name="nodejs_npm_path_reset_for_default_project" value="true" /> </component> <component name="RecentsManager"> + <key name="MoveFile.RECENT_KEYS"> + <recent name="$PROJECT_DIR$/data" /> + <recent name="$PROJECT_DIR$/libs" /> + </key> <key name="CopyFile.RECENT_KEYS"> <recent name="$PROJECT_DIR$" /> </key> @@ -144,6 +153,10 @@ <screen x="72" y="27" width="1848" height="1053" /> </state> <state x="779" y="311" width="424" height="491" key="FileChooserDialogImpl/72.27.1848.1053@72.27.1848.1053" timestamp="1606260652750" /> + <state x="687" y="162" width="618" height="783" key="find.popup" timestamp="1606586473850"> + <screen x="72" y="27" width="1848" height="1053" /> + </state> + <state x="687" y="162" width="618" height="783" key="find.popup/72.27.1848.1053@72.27.1848.1053" timestamp="1606586473850" /> <state x="659" y="259" width="672" height="678" key="search.everywhere.popup" timestamp="1604929652702"> <screen x="72" y="27" width="1848" height="1053" /> </state> diff --git a/Flaskserver/__pycache__/DBA.cpython-38.pyc b/Flaskserver/__pycache__/DBA.cpython-38.pyc deleted file mode 100644 index ee756e972d4a8aac20fcbf3c7ac617a69c472ca0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4285 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 diff --git a/Flaskserver/__pycache__/DBA.cpython-39.pyc b/Flaskserver/__pycache__/DBA.cpython-39.pyc deleted file mode 100644 index 368c5be97531cb8f6473dc6bef50b905d96221c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4269 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 diff --git a/Flaskserver/__pycache__/bigwig.cpython-38.pyc b/Flaskserver/__pycache__/bigwig.cpython-38.pyc deleted file mode 100644 index 00c1792d524e86a06d07155df2a61697b8296f77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7806 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 diff --git a/Flaskserver/__pycache__/main.cpython-38.pyc b/Flaskserver/__pycache__/main.cpython-38.pyc index 0a689a6143fa33130b1d62a5e9afc3f056bdce18..d7e1394f0ec8adca440344b7cccf0ad1393d6745 100644 GIT binary patch literal 3405 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 literal 14695 zcmb_jd2n3GS%25OIW-y`mL=QkoxL1uV@qo%Y>2bj^|Bl1s72YeHwiovhOzod8fh-| zd-5^7fh2N>H(*$jKtWhwM7YddfeJ?fwNM2G#ZeSc91n^Lp^D(5;1Bo%aB=;9-<w0S z?1U{MRe!Jh>wew+y1%=7-ZM6qGw@gVr&lXe?=+0RV`2B7gTh&);Fx6?%1~zAXqcYa zusq9T*{a)~gQs0jHC)dXoKsIX(q6h>pYgKNF6ZUsnfLPYEO-TZj(KD9EP6$Gj(g+s zEHx&)$$lSG-ZXHj`hmubHzPQ=ey}m?%}PF9pKBcQ4oN;!KioLt9g%#teqZCLcU1DZ z`u&ZvSC)Lf{y^hF??K5I>JK#@@*YBd%zM~3{YSP<@0joU$F@z=@Q<y(P8HSos;Nph zEH$AfuNkO6F8HY-ep($6{Jh|2hWLYOR`8Dseoh_g_j6bs5&Y`~f8P*)RNXK56M`=f z@eim61%FcT4-N4Tt49RCAoycL{Oi<l!7mDaeu#fmy<YH-3I4<oe^M<7{*>Sshxo_T zDZ!r>{OKY74eE`8e}g)s9#?O=W_oW_U#Z@F&G5cLSr?4?x7;EH&0BHmT)i4>#A)qs zZuvpDG;hYvGw(isfzPnk@bPqmYNK6eF4y+;N=rAM{i>%?vQ>B)kFmD)+q$*v2SKg5 z3PfJ{E7h%fsA|h0iWxMkG*zvEeAeGuuGf@bZK9A~sRkh`&)qf`WBcuIdy%+?AFj1j zu*~W0{xeZ{1}Px5D-)k!EydPa?WUn8ff<3@?_FGLHT*^8UpiT@w!>C?@!6~OYICvP zTCUb>;nkCY-wbMD?UH|Tts1Oh&L=O`f~{)(WVKnXUkz)^!J;fm;Ok4iUTjor&4u>W zI8|#Z|H^%w1Nh7^EIkd_$t`NXs!pnESlu(}6e_bwGU;DMCN#E<t`V9&OPM>SvgVAg zsqC&5+C4|1tenV<tesTX4pWi6?vlF0beJJN%(Ct<D0|!9HY2;2+X1h=V*P*XSM9D7 z=6i+6*)fqzDK{MBJR@TkV;w{?jmW`br<ZQ$L(s><#oFroYOArcyrxgpdf#Finmcnd zS&Z1dRIP)IZs(uHa+J9+XUg+M&9#W_ix+EN4)3)ZL9OElv5USgqc3~a593r@*P3_) zYt^<NJM~(Cg041K{n%!O8@2@Jw)A??YQ`zG)o2H?9fW#56<gtBvBO?H=VGlH#16M3 z&S*bqx0=|;5s6H3Dp}5~!Zr_}7(5JMSgu(#9dpvm*)yj5-%e^5{5PkVGiOZwDB5+3 z$%Z$=VE?AuXh2~toBmT+8D(P2jdg5#WbBy1heKOgva!y3O4-U$SaOwCnbnla-mtnR z>T|N_=DHi1%9%5IX_Z$6HAW$)N@`*?y<-BKQIps*wx3Gc_p&=6sL1T)I1)(5R?{*P zw-*@Jt&b@e0`7pCxdGLHa(@h!-vBwTyAa^UM=X_EVX4>~4@)~{Z(`@(E!}=gv{46N zvAzsFroZexX7=_7k_XGFSy`LO-V|8Q>{=0p;@r~h42fc)y-^1xhE*MQ4llj0@^;<Y zYO2bGuo~7tPL<P-oqBv>Ik*((np=&FzOJCDkenZzA%xNv=uyp^UV1C=ms|CqBlK2Y zsnz{kFYNr8QM>usKR0Sm|Knd6wVmhw%BcPA><eb?pVtBY^3@kiVW>_nXhT?)b?b8A zWy4mbU2Upr@FFC(8*E(+{Lo9+n(caZ+1Ce!DhISKTz7STI<{KigLXAsi``nlu{C8^ zY$-*?0R8g|Y)Hk}sX=nbF51+aRWH5VYP7dPKN+^RyixaKtF7X^ywnzi=PKQ5H>-M< zUAmCtSD_jnK|w!A@DKsnYTk}B9J^U<pcl2G=UH<HJB~@uU?^e9GiVW<1~4qgESMQ{ z7U?)r#>`17*^inJgF$yeFeq#F(|D^hwYZE9L;oaM^fIJ2)-ZOSfA@tKm)Yy!&qXGK zM4k<P4!k-L(Cb#urigyo48B{DC!Jm@LK$@9ZW}Kd*TIvKwVsJAWzQMP5#Gt{=AB+v zxjW{1u9x2tUi_kO2*tmqH-w_^d?PHV?0(^`axa;v$?vcEsPR#A#iC@nmI;dx{NvXR z<<3FmUpHqBRou3pMIOqipvL#Vy=p*R6vOcd>S)`1*7(5V-BgrPr5W_GVZChX_lAWi zC6dYMrNh$tL}W)PtkwkN*yIhH66$4h^9l4mwZHcee5t0Plpv+h;=ukEY6kpzaOw7B z-}3Mi4ZP57U)8V2hU$|9q$0gYfbBK(V+5xN<^XP80q7K-ADS*_%FApv_+y@miy9*+ z1FS+Veqi<`11o1?<sjRyWv=7fx6SXgHmM}7J@3;<AdSiUM+WatQx0DT4PA$N{Rrf2 z^6rQwz~R=yW5QU-nzcBekZ^_SEomk?EcXQUA$j%5HFPd^k!x5+=Tt)d74lP``WK!M z<0p6$nZZlDveN6kso#cvZdzVud9B)P`t?A+9SGph;h{OGeh0wb(T@G!M!Ut;?2MhS zUM~0Nbf#Rpjy~tDTmOSU{Vvqsgw0vKQVTl8=Z7Yx4-4XUaqlj7CNKCfD4VO0b%}yM zQ;yA3^V4Ez=r?kvUqSFV!4m{;CAc2|!m8cI+xpGKyoG==ED>82kckv4BM0H=H#KcI zuh6J98`Ue7045~lShEdlqk74ocl9y8@)W^Yg5v;jy8nsE!ilhZCo5&&CigFxhsxjt zz`zK}nXY+S{%0TqXCVD%V49R*pk&OW$QNLo==Y#~XM7;*`a|xKaHQT0(lbcG9|GJd z;euaC<Xd8kgf2}@8kxv}cg0qKZF0kf<V*J;?_h?)dV!q5Q{^ZtR_q;1YICAT?J&Jg zdqeme?<82KkA;y*lXiV9vLYKClvR1yJm_06&TdSvs0!$j{6DV7!qS~asVais#+Nid zO+QO;0HF4{1+pps&i>$8J%gt>6)@v3wqOR5$P!WT9CJ$mw<q40XozyMFCaJlZs6x9 z?n+FDQ5C0_*S4A)`T_QFo(OnPVz=rSSaOzIa2H${ud3CE)4|%-%1Yh$^2@E}GSpHN zNnhZ*Lvou>C3Hl76B6=Lv_noOk2j2jLqbBuJ_+^r{3_lGXqu9I3bq4MvG7kKLhiPk zIf{IN$(l4LtpY9C51@5tdO$q1><60it`uZ*h(89{M?t?dpdeVQ2r<Zhy3~(5=1T^h z7wUkI8Y?C(U1*xDN?$kDb1Ds8keM~Yyvow4Np#J}_=ecOp}P($g5{f5g&TBIGLf?` zHQ!{1Zf`7t_KDCpXai67Hl#(+Y0?ty##OOTMHy(Z{Zv!}6-^{m)NxOrqEQDY)Lnyl z<m*nJn^3N|P*C?4?KE>lH5}<eu)|NoLQu5Rr~3#L#X!W*+X<mk65b+fCSunTu`8nD zrs=uOHU}Nac@eoiv>plg*Y&rbyY|H-d_w7Dj)WzQg|nT4d{Up(Z*g1aEf3_|hQSo* zuSQu{2*?`xeFR^G8AP&vB^wDBq^eq1uO?JoCiW-+xdhB0Bj~RshD?$$z$3`@Y2Ope zTg+6-P;a31IA5>WZ1+F3GFqK)U^gOCr|z;K--X(Mwx}?L=~zXZ*5h$7Mh22JhiAeT zU<QR&o%~|0iKqZV1RZ}5574~MBMA@u5w!Y%2YT=$)*;hiA*OmREk?+)6lovMyuAZI zBk|zq&;x6O11w-|&@!ZG*8$zaB7)uINHD;~6ZT{`9cC#vX)i@?l-^0b-|l9**={b( zN8lA&MBy0T&x`$)>DyoL?-r6W`|RV+b-^95(K5vC!k_3CqkNQ!3Ufv@hE$w`p@V03 zH|9<F;3tgDS2urr^ON*ZuNh$}%F`QE8I`?esN4-pxOP08jK+IYI~cWFlKiyfCtxE^ zh6i9Vn^C4W1Md`eCTG*1U3`}r|7LgWy4gE8nj6+EPb*0@M<JY)?;an`6y>Q=DV*)i zMN`T?jP`S4sjI?a<0UJc3=j3KwlT~-GkRm2J28FNRXWa93J*uqT%T?F+($<9B93!{ z4g2@eBx>^gk_XH=?Du^b^C&EA6ZS4gEO8`w(_DuGQLcA?vJ!*c**2%4=}03Y$sj%1 z65~xqS>~{=2cVbHZ$zJ*80IJX`7&+oupA+11eo47S8X-^N~Sx5aMJ_P3@CF3lC~5c zzEh7zw4?hHP}JisnyqmP!9aag1jru2M;>sHsJ;TK*BWi&;``wxk=i5c6JiOE6xK~! z#9M4`#O9?DI^o;g6ATfG3Hw{@kk+z`e;BbxN6fg(cpWz1GbsvG&RQl#dnbZvK8NB> zYbjCrG{I<G)<VB==Oi*u)T@n)s`@0S@UKWl=g3_L?>9p~_J)5|#N;i;diKqQ3q-jT zQ2s?Q^%XXE!_g&1UPGg20tYi|2SLo6_LBC|>K5DIv|^`0e?7HQZ?!ZAGOqyVHhfj9 zHseeX`t7|=fNtWoKS9zhfPE9;qR2#&MY(QlxERu0N|u1`55~wR(cPeem_ky)SET2^ zV1_-<XW}AbrInSf=CZ^<;@mRKn;N7s19@TfVja({f^$)AA`%R%M`Ff7JP9*dS6BVY zuJ#iH+^Bu7UDXH?z%mMM=bpRpj<RYseZ&qQOsuenFo`%tO&n;_pcqMs)^Ui%;#{t_ zt5+~`ZWNA=ok~4egBc&JC0dEwrNwM3Gbh%X8?<!jD};AJ96{nEIN0DIiEY?imIu)~ zcDJaS5ff&?S@vu7*ltwYiNEkX`@1O3pu^BgRGsW(HB2HQ2gSxi?4^1+(PGqddWGPl ze9_dgS&gkg=?@XNO`Ns1g1{1(BsS}MjcEE}*fW`Pg*EykM1Kpxhgs*sX;}pkAv}a? zKhW1$H4M9eA-|D`ZzhNcxZQDXMDjn4a`5#4iRR3iMJvMuh<^*w801RkG~zM~(4hsG zl@3hJ1b0ZDgk{tpgSlCDsB<l}fN_fd>3?@~5={LxM(AV~FZ;FCwQz61e;id*xz8X4 z4}*-8Flx^Xt#!B-bfHCMQo&MX+I>X}_k{wqn_|dDAq3NPBMGlwGa{?!?if^9tB?&) zvJ6YWsH9NtW}-Ag7+=+eV*^}TWrr&CL^lT^oTGXS5zdL62yPG52cXKS{EnlbMEk1p z%BJGmbdWY|gg`PFqY!1Jf9A)SzmICr=@kcKM=*jh{+@c2hO>Bc(jID){Q!@EOTYi; zLI;kM4L52GW_MmjgBt4Qp|&_0@`sIXAu8~#<eSi&l%OVLrUwo5#c)kKEFt6rg%+i{ zW6e(u$6?(Mp>AC24D>q@rFJ0C;GW@(Aj(X`*2NoZNq_y`{uFPN?(s&U|Hk8JGqJDD zWYX_+zh^Zn_uqpN0^<ipkkOlr#%Olni;EH6!KtOyPkk@{e)lPGLG>ad7#{=$36H@k zPY6w1#yz5v0%fSw$|xj(p1xAEl(CV~P6*3>5a_vSzIf9VInq~eauO7$<nZGT{UY}J zzo65hcFP!@!~MDJQf<Syj40jyhunPXT%y3}u6DA`RymPDokFWwzdDGdbn;S4gm9;n zJj=%h)qUgtb59{Y@(}vbFA?ygM^aROjK!A$;uNAxtspVd%S5CrTgw{9be=nS2Af+| zjdPh`tKm72(}=DHn>tKnEQO#5Jdv97<8i85Z?9GL_YnWX1Ro@z-1G9Sx~dHJh2qdF zRqOT2YO4iw;+A_wDUCFRn^cG(^bTMOP>+?p%E_B+;*e4-`F&OSrhj=j<WMT^QhYyx zY1~2zxaNZpw1_@94YL+QFw^D<gkm5VaavTQGXZQ;E?G0E=Sfn=Jd!{N%LNF}qN&+) zC%u?#;WB$4{4sWsLE@$apMkvl|GF5Eg9zoq#W3j_Lmp;&2-(o3pnL>`Neu|ui6E;B z2gB{AAzk2Nz#Puf!JsKAf!+7R@sRQ^2jU#0%@DT>e*)4b3#WxLGLHn=D2_#XH>NKw zKfCz@n;%PD3Apgt-Z&f!NK`l$g}!5fun#zVLY#@B<R>LR4#%PtPKjgT_TX%E-90mo z?=s`x?B?NE92m_Fj>Xu}vB-sxR`~9*XgE`pCq|`k22RPu9>=06jzuY)?K>9Zm^)<Z z;7i4^n7r#M9p@^Ab1?0(KH^v$8qJHizAN$9QK@$*%JxgoOSnAMJB%>K5pgU^GGejs zSa2Pt;Aq{K>>XjUGjJ@ZB}R?~T4KBsRuo5va3c;!MYs{bN`5@aM`>cz#4BmIM`rJ6 zGz~dB9p0}d!?}G<#I#7?BpCND(6HFFiD^UQTqs|RoO{dN|BExBDa!7Yuj{~_z$ioc z3h&`i!ehI{>udBJ^u}<c`Y=Z?LaZe*jya|n$DP@IqxD?{-R2t-PtnNj`FO6&Yj6~F zz^=9kXt(PQ!QKg-8BA#3I1niN{Q%t^b|F1})37+S|DPK4IG;|0_J`Q!!vtbfjx5h@ z;;sYCmq!Nsy%o*3viS{yZzs5mS+2i>6;w!Kk?ZedZkFKV1P>B?g5VOtCkYtwxx*OO z-%XqZogQY60oVBhVuvUCh&m^sQwnDx(UJA7Um@=M34VZJjez#I{y~D#*UO6Zh}$8M zwHc|FA0kfHloOuAng;Jh<__x`R`oF`jn`mT+XK6rR<-*dZdR;nj?r-!#ed(UW74Qd zAw7c>{0zW;9RqDa+ZaJCg!<iWOM49aBWMN39(WsaAe-Dhfh@qi5rxCSeX;-tVZ7EL z7WY_UaYO$M`k{61Lf%8WyoUbZ%g?(@`e!j~5i++6FEBpQcM|u|J5?z8>H<<w2KZ9O zoc>WX(mzJ<;{?J~BhGpUa9_(kbCF?i0_NMCtnM!9GBn=i$L~r0aItX8p7_U!4|<-% ziQy&0DKt$AO^8@%pPL5eJ4P%GC~qvWe%_h*cCp*q3UOASxOYN)LeY18!wTM9;x`;Y zCZRi?meXj#xe^WqXs$3XtN;@w8DtOnkaULR^E?V)jC>Fc-Ay!zefBb9s5^-N+b}ck zcE!k9O4x@4s(%h(K7Y@}6xaWrYd1Jf;ff`FeulXbOOCdeB=rqknj$iTrAZb>$PHm( zv8PYgnk%h63sOLPZV#s$`~<+4UXZTMWAn&@uX!F}UpWv5H!wKtB4BL6D^1~;Ey>fC z$iTPC@(CX?7dhZQ9H~KjBR~w8lUrcUD{|Pjc>>%C|8!OEh0zPd!TH^8bLGKH=_UOl zW}GN&d_Aver29b`1~sk#)S5VcMlhunws^TCkbp$zz|%=MCn)12ba|_efXL{I*OzcF zejWqy!j{scpG1Za#j+6nd|?-<=jjUcy@141`7B@K#Jys5ImC^<K{d+#C{uF(%*$bv z${^2!+hM-e?C0-B4slL4yTZ+0CsmN~22_#5X&U_iid#Mz^uYgr<=hfk=a<m(3v6x} z51(DOlAOR9zzmX5%Fmz%RA33U;F<}K0YXFsx6KVh|22;SaD~F$u=Kk^L^s6K#W8~% zb=e1TsS9lpNFLnHQx52e^mH6_gezq|H-beE%NrKn9Q<=gZuWZ~<D^VuGuk{4s~;Cu z^tU4(lIE>Hc>IoXKxc9sftp`QM!+==L;ni>2gDFe&X{xU*JTjREXqk2YVq!{ezO?j zSQqmh&Jb^=kr=tayfM?Cf?w^x*0%Uf@Z$&gj{~1W>>?ZCG8&=|kHX%a9)1%dGWaI= z6#OQ{FnB~#kSjW06BW2#jA6j~O{<)IQ+kkNFT}I8$vDy&j#0otgZ^+erl1NuNW9^6 zVgIA$7+dna&Ht1*3NGBq6A>7!MG^TTj%Cd5ICA5XD+!JfnhC)%Hz_z+*4-(=F*hCY zK(ZtekOR?yXeye<5oorD_zGhxTxsgy4dZ1?FQ9FS7_8_7RumDK<US_KlPJSu8J4F| zhAlrVPos>>f5Y+tlyQ78EYF}k+#&RL5OhV6Z11}DX>xWeHl)SulEfrxU$AjpZk~~d zM6L8X@S+A)C-LjwL9XNI@>|QRC!aa;kDq`1&{K>oZ?q8jU8`NJ_BmtrEbf;~tkzo> z!Mr#R$L&qrt_{7(T}>;xs%l%o>JPWyn)~xlJFm*A=`F<p+^@#X;WOn<`a+27y~q^K z^GaU1k)UzCgeCC4RHk2C^TR4y*&1Mabf<(jM|igx@#X$0zZ^-9bnkf#h7eK>_knTM zmB9oNS-8H83x5b_52C=STF|UEHJLk6=@*gn98O37B9V-x&d(&$>wAfK1A(aXbI8dh z!w|Oy16;7jE$LyD!z=ikyGo4Z`d2EaVso9>{+K@<n|M!oxMqNF4!jHptw@+Az|~fS zYjBks7=P1C0pV}zPjTq)lTkx1%$=6bx1s7o6ndACF;Wn3i0dZ8?g(-WXe4po96}n? zA^?+mVC@p>cta&QTXhiooq>fxk%?p-u}jvpl!@bi0f7>n$66)4Gx&!P<s9~C*$E)H zydXUz$Ut`T{6vmg4M-4U9nnyU1v(MDS4vSrMmLy7rT*mrBZPcZ_O^wH2M(-Yfu^?& zNCk%%3~)Oe?^1f=z|z3=I>b3~c{hoPAi{u%D$Z?q$c5`IJd{npM7d@`%OaW!?n#Zf zCmhE-5DD%_P_jumnUmnoAx0r>MspSX2}nR3`igw&M^P9-!My@XtUjh6LHV1_-`@NT zFAvMntSd*eS(QTs1=niiXch;iQK5e{o5p&jaWGwA)FY9+{9=hD;Bqly7ZM%ErHvA2 zb{%cDO}V%+jy^MTRc8!eTe{2FTG7}&dd79XLC=t!lwAAP3KvMmNAE*J%yvsrDN1j+ zkm;X?6r6gK(H)08{ye@w;?Rozz;yre56zcVB*XaNv9q;5hV75bsjV#>NOcZBGqh34 zxb>x5S86z4^UF2Jkb64~RM%hXSo5Bdh|iIMT=dXC$9z!v7q?a`a#aJzP`iAFFMmXW zV<!=b@;yi09htZ_jd|`_2U;!Uwkf2b43Ica&_RfP;~W#eUQnP4W!-T4R~~Kst7xWw zjo{Y-=B<%5#E$~rMm%k7#LHKhA6ee-K=BJ)E$mow=6mn88v0jzX3L%DjAgc*Hn;@5 za={pdHgI`y4HbB7<FRXW9-RKeaJ}Ys5sH5Wwq+RtbU&GJFz&4kNMA~p2RAzVH~g_g zG*5Qj=)|2L>Jt4b!EX@!9>MPu>|Kg~+Orh1`zBOBE%OoOi$K2gD!!v=t)uL4bh(CN zRIYd>4%Cw@<_I1j7zJJ)VX;hbmv9So<8fAr-Wn<oFD=e*rI~24?`Mk-5lFD)E^!hs zgP-7Rh(^lSPmeTQrn<bmrK`(V<?uvS1H>cSB$UEMj*EK<`46~AXHj7w=AJVjh5Fzf zHy%Fnau1cxt^kTvvRpW~M~KDExJh#cN{v^43a0)8^Z}~)Wj-F~F)e-srMbUyFyf`4 z=W&ymtL){Z64r_iG-*}V)N_*y`N{3lhlu9YKz)edCkbSgsY>yiIds0ji%Tm!<Hm38 z23h?k)W_*ou)urz$;H*L<IAKnuY{jjRT}sqRqaw$*JyO4i5?Gwx?k1J1ss=a{8&jC z&PxZlrw^>$SB-JkuwM6*qk7qS{E#oK)&{Gu^XzCQSgy6NE(91G*Pa%Jl?BdV41m|R zlGF7{g%|iMmDq+H#qV+ucJPyZdz6izCU_6Q2MB1Z=}!~<2*D=^WKSi4Lq|yzy=+3l z2Eq$NhKMsyG+Jt_?mvnBHiC-)v@8aWS8^DWxMB&GHS%xaEE?7ck6Us!(k#;bNJo(l WBOOF~3(_Le8<8fEvPd(z=l&OL7snU? diff --git a/Flaskserver/__pycache__/preprocessing.cpython-38.pyc b/Flaskserver/__pycache__/preprocessing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7235ecbfce8b71c221b4345ca9ee868b8370c8b GIT binary patch literal 4100 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 Wi5-;aQQBvU@XmYBdR6b7H~%mGXTnDS literal 0 HcmV?d00001 diff --git a/Flaskserver/__pycache__/pseudo.cpython-38.pyc b/Flaskserver/__pycache__/pseudo.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59cff614cd0a683799a6b40d1e3c54bddacc3b8a GIT binary patch literal 7381 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 literal 0 HcmV?d00001 diff --git a/Flaskserver/__pycache__/utils.cpython-38.pyc b/Flaskserver/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 53aa8e25f8cca63b270439f74b880d04a1d5e23d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1180 zcmY*Z!EW0|5G5%}wh5~!Q1p<T2EMccBr(k?XoDb%?W9Cw8Zhm;(M4Eum(s$^U3PaV zS%q>+ej-H=@v$G#AK|r!BA?KnIx7pVx&W7$o#D)zH*a~-?M5E1`YS&9qvd&jI%9i< zXgtTq^l_u!*rPrTgg*}O-4d-b9EZL~cUhaZfAeXF?!0f0J2c3=-X3`scwTg<s-@=B znT5xXzIg(vniFlgfkYb1ZEcx>LTPxx)d{0KCj!oxVMOOM49SPlYo0S{7=>C=roqk_ zj4G1jYvbv|k4zh`Bs?BIg0Ee`!Nzsa`z~6l8cMPRsVvlnVFD~(aKV7x<gBv5CFH8C z1Sc|Qu;6yKF0x56jNWV#s4Gh_mSA+X1XXND0kP4{+UnalUMv<vvIaO*dK!y6_$Gdx z93H3H@c=<a7g8`|K(n7~uCcYNC6Edc<m3u*39<lWgiJL<pH&XwLUYUIv=2rVc0n|Y zC^uH~D<p86nhk2e9=8LK93tUhltGdmz>86qWc}zeIsg9j;v6nVXJ@1IJUPzb^b8J9 z)1%}(IZe@e38VB4{E(zaePBoo^3QH6?XV+2?nq;F7-h`SdOwi$Je_SSmh*z=*pZyp zWXfQw=1fa$1S+OWZrs=mLZgx3CAY+`?f)iyKZH-HJ}>S0&Hf3OC*&qWp)mc|uOD#z z{nK-FM3@nkVsWxL>h69y0q@{*h>scLX1$O83T3?Vi{LhR8=xJmyj%ZmYlWhI;H_GC z(pqh-YxF$z&=dU6gVuHEJT`pieQd8<w?0a~m1-X=d~#c_(*&~8u6z?Z$u#W+63Daz z*K`YRCsq?_Q2b>tY(h#bxr^@PwN*ti-g8G2iT>X`y^ml0a~aQ6$zsap13@aQs`zM$ z+{Qv79o#Ml2CoVVWzGgOVrIzmV9rfV#DGX5mX_xxekq8#HcaCn;u_g9Lxi^Hs$hy= z*cVvGtbYmq=YHfr^dAQLGd%108h;O7Z>QOrOsL8ylcp`}67O~gkH=q{Yk`+m4pAMI qRyj%ptKaq7E`%a>on_s|*4D*uo870SqP1Yp9Bsy>3%k4ie)un$PG7qK diff --git a/Flaskserver/.gitattributes b/Flaskserver/data/.gitattributes similarity index 100% rename from Flaskserver/.gitattributes rename to Flaskserver/data/.gitattributes diff --git a/Flaskserver/21.csv b/Flaskserver/data/21.csv similarity index 100% rename from Flaskserver/21.csv rename to Flaskserver/data/21.csv diff --git a/Flaskserver/NW_Ground_Stations_2016.csv b/Flaskserver/data/NW_Ground_Stations_2016.csv similarity index 100% rename from Flaskserver/NW_Ground_Stations_2016.csv rename to Flaskserver/data/NW_Ground_Stations_2016.csv diff --git a/Flaskserver/chip_w-3000_r-25.h5 b/Flaskserver/data/chip_w-3000_r-25.h5 similarity index 100% rename from Flaskserver/chip_w-3000_r-25.h5 rename to Flaskserver/data/chip_w-3000_r-25.h5 diff --git a/Flaskserver/data.pkl b/Flaskserver/data/data.pkl similarity index 100% rename from Flaskserver/data.pkl rename to Flaskserver/data/data.pkl diff --git a/Flaskserver/parameters.npy b/Flaskserver/data/parameters.npy similarity index 100% rename from Flaskserver/parameters.npy rename to Flaskserver/data/parameters.npy diff --git a/Flaskserver/processed-data b/Flaskserver/data/processed-data similarity index 100% rename from Flaskserver/processed-data rename to Flaskserver/data/processed-data diff --git a/Flaskserver/processed-data.npy b/Flaskserver/data/processed-data.npy similarity index 100% rename from Flaskserver/processed-data.npy rename to Flaskserver/data/processed-data.npy diff --git a/Flaskserver/query b/Flaskserver/data/query similarity index 100% rename from Flaskserver/query rename to Flaskserver/data/query diff --git a/Flaskserver/test.bigWig b/Flaskserver/data/test.bigWig similarity index 100% rename from Flaskserver/test.bigWig rename to Flaskserver/data/test.bigWig diff --git a/Flaskserver/DBA.py b/Flaskserver/libs/DBA.py similarity index 100% rename from Flaskserver/DBA.py rename to Flaskserver/libs/DBA.py diff --git a/Flaskserver/DBA_multivariate.py b/Flaskserver/libs/DBA_multivariate.py similarity index 100% rename from Flaskserver/DBA_multivariate.py rename to Flaskserver/libs/DBA_multivariate.py diff --git a/Flaskserver/__pycache__/DBA_multivariate.cpython-38.pyc b/Flaskserver/libs/__pycache__/DBA_multivariate.cpython-38.pyc similarity index 97% rename from Flaskserver/__pycache__/DBA_multivariate.cpython-38.pyc rename to Flaskserver/libs/__pycache__/DBA_multivariate.cpython-38.pyc index eaac5b3ae6a624df5de796f13360e27285d379c7..c05779e917b5cab3b52eb89a19ee2a3f8aa6e2ff 100644 GIT binary patch delta 23 fcmdn3e?Wi3PFBYB$-7wnS#mOyiZ^qxZ59LoZ@UPD delta 18 acmX@0zgvI9PFBXG$-7wnH}kP=5(EH9cm~!0 diff --git a/Flaskserver/__pycache__/bigwig.cpython-39.pyc b/Flaskserver/libs/__pycache__/bigwig.cpython-38.pyc similarity index 71% rename from Flaskserver/__pycache__/bigwig.cpython-39.pyc rename to Flaskserver/libs/__pycache__/bigwig.cpython-38.pyc index d7b5cc2d9c3f2b82802447276d67013e31df8328..53394d44f75209bebbfc7b6452b784f4de06c190 100644 GIT binary patch delta 395 zcmaE6(`?Hax{+}Q3uEl$T`c}AIhjetlQ~#Jn6f!0$FUkQWwULb#45+cQOv@?z`(}9 zG<geqHKX=q1CED`w>ArL&SzoNoV<y5C)YKgbPY=i15jHjpEl#H$qV`N<>XixO1QJR ziuN%wfM5yB0-hS~g^aa)kqmiE5tDWJ4S6+y`ZW2AjDb{<_T(&nB}Sdez5MqW`6lNG zWC0zq`G&v&CPuBvGleU;irFA;VBp#;CUTes<iy(&T};{Ro6{vHFakCHklMf)HF>49 zqF5AXaY<rPNqka4aTI%MUJ8g&WH$MutS}F2az<%h_Gh3`#W9orOS{T&F);n-W8q<B z0zwWT6#K)%&c?{b#Ku%qH91Zun_CCuI4=-U4KiW!a~T1~n#n(8tW>}<x<Hv*ta+um z1(ikIAXUO30k(p~yp+V^B7@0cvVOo2TPo|wRt1u+nfyjp4d_f^xj-H!1~~i=0I*|b AN&o-= delta 349 zcmZp+eP+X$xRG%O3uDCOT`c~S`B;OQN*N|6vl=m#F>IdBD#yg~nSp^}@?Q38MxDuq zY$B6=InFXhZI<So&%&rRc{lG)t}8(K8kQ6Wptd?bZN?dsSMlY`$gnV!a98eOWB|bu zmIXXD+zT0N`63zem<%SH^BeMN0(EKf7a0SoBAv;l{7Q_vlV|YXW8|4!E|3Lu!sf>U z2bdVOCod7MWGT{MVA!lKa+n3=v^Nr6Ol6FlD<mf{0<~~TZ(zJNd6%>z&n?d4lEk8t z_@sj3Ta%wlYcNJl7Lak3;`z_V!o$b}gd9L9_J@U?jggIsjj5=1a-mE%w=T$jFAz~T z`J=S(<X<uZjP;YbWvx`eGI~InTdaAdxdoL)+#prLKq88*ATci`vAD=^a;B^wFj#iT YI<nP*Wa}sYmsMj7n5-rj$i(v>09y)OMgRZ+ diff --git a/Flaskserver/bigwig.py b/Flaskserver/libs/bigwig.py similarity index 100% rename from Flaskserver/bigwig.py rename to Flaskserver/libs/bigwig.py diff --git a/Flaskserver/setup.py b/Flaskserver/libs/setup.py similarity index 100% rename from Flaskserver/setup.py rename to Flaskserver/libs/setup.py diff --git a/Flaskserver/utils.py b/Flaskserver/libs/utils.py similarity index 100% rename from Flaskserver/utils.py rename to Flaskserver/libs/utils.py diff --git a/Flaskserver/main.py b/Flaskserver/main.py index 713ce99f..fbfcdbad 100644 --- a/Flaskserver/main.py +++ b/Flaskserver/main.py @@ -2,24 +2,12 @@ from flask import Flask, request import numpy as np from flask_cors import CORS from time import time -import pandas as pd import orjson -import bigwig -import bbi -import _ucrdtw -import _lsh -import math -import dask.dataframe as dd import os.path -from random import sample -from DBA_multivariate import performDBA -from tslearn.metrics import dtw -from sklearn import preprocessing -from collections import defaultdict -from dtaidistance import dtw_ndim -from scipy.spatial.distance import euclidean +import pseudo +import preprocessing -from fastdtw import fastdtw +data_path = 'data/processed-data.npy' reload = False @@ -30,478 +18,222 @@ CORS(app) def index(): return "hi" + +""" +Returns raw data + +Output: [{ + index: 1d array [x] + values: 1d array [x] +}] +""" @app.route('/read-data', methods=['GET']) def read_data(): t0 = time() - size = bbi.chromsizes('test.bigWig')['chr1'] - bins = 100000 - data = bigwig.get('test.bigWig', 'chr1', 0, size, bins) - print(data.shape) - response = [ - { - "index": list(range(0, size, int(size/(bins)))), - "values": data.tolist() - }, - { - "index": list(range(0, size, int(size / (bins)))), - "values": data.tolist() - }, - { - "index": list(range(0, size, int(size / (bins)))), - "values": data.tolist() - } - ] + response = preprocessing.read_mts_data() response = orjson.dumps(response) print('Data read: ' + str(time()-t0)) return response -@app.route('/read-mts-data', methods=['GET']) -def read_mts_data(): - filename = 'data.pkl' - if (not os.path.isfile(filename)): - print("start") - df = dd.read_csv("NW_Ground_Stations_2016.csv", usecols=['number_sta', 'date', 't', 'hu', 'td']) - print("read file") - df = df.loc[df['number_sta'].isin([14066001, 14137001, 14216001, 14372001, 22092001, 22113006, 22135001])].fillna(0) - print("split rows") - df = df.compute() - df.to_pickle(filename) - print("to_pandas") - df = pd.read_pickle(filename) - df.dropna(subset=['t'], inplace=True) - response = [ - { - "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), - "values": df.loc[df['number_sta'] == 14066001].loc[:, 't'].values.tolist() - }, - { - "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), - "values": df.loc[df['number_sta'] == 14066001].loc[:, 'hu'].values.tolist() - }, - { - "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), - "values": df.loc[df['number_sta'] == 14066001].loc[:, 'td'].values.tolist() - } - ] - print("response ready") - response = orjson.dumps(response) - return response -@app.route('/create-mts-windows', methods=['POST']) -def create_mts_windows(): - t0 = time() - if (not os.path.isfile('processed-data.npy')): - filename = 'data.pkl' - df = pd.read_pickle(filename) - channels = list() - channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 't'].fillna(0).values.tolist()) - channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 'hu'].fillna(0).values.tolist()) - channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 'td'].fillna(0).values.tolist()) - print("Data read: " + str(time()-t0)) - # raw_data = request.json - window_size = 120 #int(raw_data['parameters']["windowsize"]) - print("Processing: " + str(time()-t0)) - data = [([values[i:i+window_size] for values in channels]) for i in range(0, len(channels[0]) - window_size, 1)] - print("Raw windows: " + str(time()-t0)) - windows = [] - for i in range(len(data)): - if i % 5000 == 0: - print(i) - windows.append(preprocessing.minmax_scale(data[i], (-1, 1), axis=1)) - print("Preprocessed: " + str(time()-t0)) - np.save('processed-data', windows) - # data = np.load('processed-data.npy') - # data = np.reshape(data, (len(data), len(data[0][0]), len(data[0]))) - # r, a, sd = preprocess(data, 11.5) - # np.save('parameters', np.array([r, a, sd])) - print("Sending response: " + str(time()-t0)) - return '1' +""" +Creates windows +Input: { + parameters: { + windowssize: int + } +} +Output: '1' +""" @app.route('/create-windows', methods=['POST']) def create_windows(): t0 = time() - if (not os.path.isfile('processed-data.npy')): - # raw_data = request.json - # window_size = int(raw_data['parameters']["windowsize"]) - window_size = 120 - data = bigwig.chunk( - 'test.bigWig', - 12000, - int(12000 / window_size), - int(12000 / 6), - ['chr1'], - verbose=True, - ) - data = np.reshape(data, (len(data), 1, len(data[0]))) - data2 = np.copy(data) - np.random.shuffle(data2) - data3 = np.copy(data) - np.random.shuffle(data3) - - data = np.concatenate((data, data2), axis=1) - data = np.concatenate((data, data3), axis=1) - # data = np.repeat(data, repeats=3, axis=1) - np.save('processed-data', data) - print('Windows created: ' + str(time()-t0)) - return '1' - -@app.route('/create-test-windows', methods=['POST']) -def create_test_windows(): - t0 = time() - if (not os.path.isfile('processed-data.npy')): - datafile = '21.csv' - - data = pd.read_csv(datafile, header=None) - - # and convert it to numpy array: - npdata = np.array(data) - print('data loaded') - window_data = [npdata[i:i + 120, 0:5] for i in range(0, npdata.shape[0] - 120, int(120 / 8))] - del npdata - print('data created') - np_window_data = np.repeat(window_data, repeats=3, axis=0) - print(np_window_data.shape) - del window_data - data = np.reshape(np_window_data, (len(np_window_data), 5, len(np_window_data[0]))) - print(data.shape) - np.save('processed-data', data) + if (not os.path.isfile(data_path)): + raw_data = request.json + window_size = int(raw_data['parameters']["windowsize"]) + preprocessing.create_eeg_windows(window_size, 5) print('Windows created: ' + str(time()-t0)) return '1' +""" +Does first iteration of LSH and returns a bunch of useful information + +Input: { + query: 2d array [d][t] +} + +Output: { + hash_functions: 3d array [k][l][d] + candidates: 3d array [k][l][i] + distances: 3d array [k][l][i] + average_candidates: 1d array [i] + average_distances: 1d array [i] + tables: [{ + bucket: 1d array + }] + average_table: { + bucket: 1d array + } + samples: 1d array + parameters: 1d array +} +""" @app.route('/initialize', methods=['POST']) def initialize(): t0 = time() raw_data = orjson.loads(request.data) - data = np.load('processed-data.npy') + data = np.load(data_path) data = np.swapaxes(data, 1, 2) - # data = np.reshape(data, (len(data), len(data[0][0]), len(data[0]))) query = raw_data["query"] query = np.swapaxes(query, 0, 1) - # query = np.reshape(query, (len(query[0]), len(query))) - parameters = preprocess(data) # parameters = np.load('parameters.npy') - r = parameters[0] - a = parameters[1] - sd = parameters[2] - - candidates, distances, hf = _lsh.lsh(data, query, r, a, sd) - print(distances) - - dict = defaultdict(int) - for l in range(len(candidates)): - for k in range(len(candidates[0])): - for i in range(len(candidates[0][0])): - dict[candidates[l][k][i]] += distances[l][k][i] - sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])} - average_candidates = list(sorted_dict.keys()) - average_distances = list(sorted_dict.values()) - - tables = [] - samples_set = set() - candidates = candidates.tolist() - for l in range(len(candidates)): - for k in range(len(candidates[0])): - samples_set.update(candidates[l][k][0:5]) - dict = defaultdict(list) - length = len(distances[l][k]) - median = distances[l][k][math.ceil(length/2)] - stepsize = median / 10 - indices = list(map(lambda x: 19 if x > median * 2 else math.floor(x / stepsize), distances[l][k])) - for i in range(len(candidates[0][0])): - dict[str(indices[i])].append(candidates[l][k][i]) - tables.append(dict) - - samples = np.array(list(filter(lambda x: x in samples_set, average_candidates))).tolist() - - - response = { - "hash_functions": hf.reshape((len(candidates) * len(candidates[0]), len(query[0]))).tolist(), - "candidates": candidates, - "tables": tables, - "distances": distances.tolist(), - "samples": list(samples), - "average_candidates": np.array(average_candidates).tolist(), - "average_distances": np.array(average_distances).tolist(), - "parameters": [float(r), float(a), float(sd)] - } - response = orjson.dumps(response) - print('LSH done: ' + str(time()-t0)) - return response -@app.route('/weights', methods=['POST']) -def weights(): - alpha = 0.2 - raw_data = orjson.loads(request.data) - labels = raw_data["labels"] - hash_functions = raw_data["hash_functions"] - query = raw_data["query"] - old_weights = raw_data["weights"] - data = np.load('processed-data.npy') - all_good_windows = data[[[int(index) for index, value in labels.items() if value is True]]] - - good_distances = np.zeros(len(query)) - for window in all_good_windows: - for i in range(len(all_good_windows[0])): - good_distances[i] += _ucrdtw.ucrdtw(query[i], window[i], 0.05, False)[1] - if len(all_good_windows) != 0: - good_distances = np.square(good_distances) - good_distances /= np.sum(good_distances) - good_distances = np.ones(len(query)) - good_distances - good_distances /= np.sum(good_distances) - good_distances *= len(all_good_windows[0]) - good_distances = np.sqrt(good_distances) - - if len(hash_functions) != 0: - summed_hash_functions = np.sum(hash_functions, axis=0) - summed_hash_functions = np.square(summed_hash_functions) - normalized_hash_functions = summed_hash_functions / np.sum(summed_hash_functions) - normalized_hash_functions *= len(hash_functions[0]) - - if len(hash_functions) + len(all_good_windows) == 0: - print("no update") - new_weights = old_weights - elif len(hash_functions) == 0: - print("only windows") - new_weights = alpha * np.array(old_weights) + (1 - alpha) * good_distances - elif len(all_good_windows) == 0: - print("only tables") - new_weights = alpha * np.array(old_weights) + (1 - alpha) * normalized_hash_functions - else: - print("tables & windows") - new_weights = alpha * np.array(old_weights) + 0.5 * (1-alpha) * good_distances + 0.5 * (1-alpha) * normalized_hash_functions - - print(new_weights) - - response = orjson.dumps(new_weights.tolist()) + lsh_data = pseudo.lsh(data, query) + + response = orjson.dumps(lsh_data) + print('LSH done: ' + str(time()-t0)) return response +""" +Does LSH and returns a bunch of useful information + +Input: { + query: 2d array [d][t] +} + +Output: { + hash_functions: 3d array [k][l][d] + candidates: 3d array [k][l][i] + distances: 3d array [k][l][i] + average_candidates: 1d array [i] + average_distances: 1d array [i] + tables: [{ + bucket: 1d array + }] + average_table: { + bucket: 1d array + } + samples: 1d array +} +""" @app.route('/update', methods=['POST']) def update(): t0 = time() raw_data = orjson.loads(request.data) - data = np.load('processed-data.npy') + data = np.load(data_path) data = np.swapaxes(data, 1, 2) - # data = np.reshape(data, (len(data), len(data[0][0]), len(data[0]))) query = raw_data["query"] query = np.swapaxes(query, 0, 1) - # query = np.reshape(query, (len(query[0]), len(query))) weights = raw_data["weights"] parameters = raw_data["parameters"] - candidates, distances, hf = _lsh.lsh(data, query, parameters[0], parameters[1], parameters[2], weights) - dict = defaultdict(int) - for l in range(len(candidates)): - for k in range(len(candidates[0])): - for i in range(len(candidates[0][0])): - dict[candidates[l][k][i]] += distances[l][k][i] - sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])} - average_candidates = list(sorted_dict.keys()) - average_distances = list(sorted_dict.values()) - - tables = [] - samples_set = set() - candidates = candidates.tolist() - for l in range(len(candidates)): - for k in range(len(candidates[0])): - samples_set.update(candidates[l][k][0:5]) - dict = defaultdict(list) - length = len(distances[l][k]) - median = distances[l][k][math.ceil(length/2)] - stepsize = median / 10 - indices = list(map(lambda x: 19 if x > median * 2 else math.floor(x / stepsize), distances[l][k])) - for i in range(len(candidates[0][0])): - dict[str(indices[i])].append(candidates[l][k][i]) - tables.append(dict) - - samples = np.array(list(filter(lambda x: x in samples_set, average_candidates))).tolist() - - response = { - "hash_functions": hf.reshape((len(candidates) * len(candidates[0]), len(query[0]))).tolist(), - "candidates": candidates, - "tables": tables, - "samples": list(samples), - "average_candidates": np.array(average_candidates).tolist(), - "average_distances": np.array(average_distances).tolist(), - "distances": distances.tolist(), - } - response = orjson.dumps(response) + lsh_data = pseudo.lsh(data, query, parameters=parameters, weights=weights) + + response = orjson.dumps(lsh_data) print('LSH done: ' + str(time()-t0)) return response + +""" +Calculates new weights for LSH algorithm + +Input: { + labels: 1d array [?] + hash_functions: 2d array [?][d] + query: 2d array [d][t] + weights: 1d array [d] +} + +Output: 1d array [d] +""" +@app.route('/weights', methods=['POST']) +def weights(): + raw_data = orjson.loads(request.data) + labels = raw_data["labels"] + hash_functions = raw_data["hash_functions"] + query = raw_data["query"] + old_weights = raw_data["weights"] + data = np.load(data_path) + + new_weights = pseudo.weights(data, query, old_weights, labels, hash_functions) + + response = orjson.dumps(new_weights) + return response + + +""" +Calculates query based on given indices + +Input: { + indices: 1d array [?] +} + +Output: 2d array [d][t] +""" @app.route('/query', methods=['POST']) def query(): t0 = time() raw_data = orjson.loads(request.data) - windowIndices = raw_data['window'] - if isinstance(windowIndices, int): - output = np.load('processed-data.npy')[windowIndices] - response = orjson.dumps(output.tolist()) - print("Query done: " + str(time() - t0)) - return response - else: - indices = [int(index) for index, value in windowIndices.items() if value is True] - data = np.load('processed-data.npy')[indices] - output = performDBA(data) - response = orjson.dumps(output.tolist()) - print("Query done: " + str(time()-t0)) - return response + window_indices = raw_data['indices'] + data = np.load(data_path) + + response = pseudo.query(data, window_indices) + + response = orjson.dumps(response) + print("Query done: " + str(time() - t0)) + return response + + +""" +Returns values of windows on given indices +Input: { + indices: 1d array [x] +} + +Output: 3d array [x][d][t] +""" @app.route('/window', methods=['POST']) def window(): t0 = time() raw_data = orjson.loads(request.data) indices = raw_data['indices'] - output = np.load('processed-data.npy')[indices] + + output = np.load(data_path)[indices] + response = orjson.dumps(output.tolist()) - print("Query done: " + str(time() - t0)) + print("Window(s) done: " + str(time() - t0)) return response + +""" +Returns additional information on given table + +Input: { + table: 2d array [x][?] +} + +Output: { + prototypes: { + average: 1d array [t] + max: 1d array [t] + min: 1d array [t] + } + distances: 2d array [x][x] +} +""" @app.route('/table-info', methods=['POST']) def table_info(): t0 = time() raw_data = orjson.loads(request.data) - all_windows = raw_data['windows'] - data = np.load('processed-data.npy') - prototypes = [] - for windows in all_windows: - actual_windows = data[windows] - average_values = np.average(actual_windows, 0) - std_values = np.std(actual_windows, 0) - max_values = average_values + std_values - min_values = average_values - std_values - prototypes.append({ - 'average': average_values.tolist(), - 'max': max_values.tolist(), - 'min': min_values.tolist() - }) - # distances = [[dtw(np.array(v["average"]), np.array(w["average"]), global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05 * 120)) for j, w in enumerate(prototypes)] for i, v in enumerate(prototypes)] - response = orjson.dumps({'prototypes': prototypes, 'distances': []}) - print("Averages calculated: " + str(time() - t0)) - return response - -def preprocess(data, r=10.0): - # return 0.10882589134534404, 3.1202154563478928, 0.9705780396843037 - # data = np.load('processed-data.npy') - # data = np.reshape(data, (59999, 20, 120)) - # data = np.repeat(data, repeats=1, axis=1) - subset = [] - t0 = time() - - i = 0 - while i < len(data): - if i % 999 == 0: - print(r) - print(str(i) + ':' + str(len(subset))) - - state = 1 - for s in subset: - if np.linalg.norm(data[i] - data[s]) < r: - state = 0 - break - if state == 1: - subset.append(i) - - i = i + 1 - if i == 10000 and len(subset) < 10: - r = r / 2 - subset = [] - i = 0 - if len(subset) > 200: - r = r + r / 2 - subset = [] - i = 0 - - # subset = sample(list(range(len(data))), 200) - print("r = " + str(r)) - dtw_distances = [] - eq_distances = [] - for i, index_1 in enumerate(subset): - print(i) - for j, index_2 in enumerate(subset): - if index_1 == index_2: - continue - e = np.linalg.norm(data[index_1] - data[index_2]) - if (math.isnan(e) or e == 0): - eq_distances.append(0.0001) - dtw_distances.append(0.0001) - continue - eq_distances.append(e) - d = 0 - # d, _ = fastdtw(data[index_1], data[index_2], dist=euclidean) - d = dtw(data[index_1], data[index_2], global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05*120)) - # d = _ucrdtw.ucrdtw(data[index_1], data[index_2], 0.05, False)[1] - # d = dtw.dtw(data[index_1], data[index_2], dist_method="Euclidean", window_type="sakoechiba", window_args={"window_size": 120}).distance - dtw_distances.append(d) - - ratios = np.array(dtw_distances)/np.array(eq_distances) - mean_dtw = np.mean(dtw_distances) - sd_dtw = np.std(dtw_distances) - mean_eq = np.mean(eq_distances) - sd_eq = np.std(eq_distances) - a = np.mean(ratios) - sd = np.std(ratios) - theta = mean_dtw + -2.58 * sd_dtw - # theta = mean_eq + -2.58 * sd_eq - r = theta / ((a-sd)*math.sqrt(120)) - if r < 0: - r = mean_dtw / 100 - # r = theta / (math.sqrt(120)) - print('Mean: ' + str(mean_dtw)) - print('Stdev: ' + str(sd_dtw)) - print('Ratio mean: ' + str(a)) - print('Ratio stdev: ' + str(sd)) - print('Theta: ' + str(theta)) - print('r: ' + str(r)) - print('Preprocessing time: ' + str(time() - t0)) - return r, a, sd - -def debug_test_lsh(): - data = np.load('processed-data.npy') - # data = np.repeat(data, repeats=7, axis=1) - print(data.shape) - data = np.reshape(data, (len(data), len(data[0][0]), len(data[0]))) - - r, a, sd = preprocess(data, 11.25) - create_windows() - query_n = 1234 - t0 = time() - query = data[query_n] - data = data.astype('double') - dict = defaultdict(int) - candidates, distances, hf = _lsh.lsh(data, query, r, a, sd) - print("Calculated approximate in: " + str(time()-t0)) - for l in range(len(candidates)): - for k in range(len(candidates[0])): - for i in range(len(candidates[0][0])): - dict[candidates[l][k][i]] += distances[l][k][i] - sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])} - candidates = list(sorted_dict.keys()) - - print(candidates[0:20]) + table = raw_data['table'] + data = np.load(data_path) - t0 = time() - # distances = [dtw_ndim.distance_fast(window, query) for window in data] - distances = [dtw(window, query, global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05*120)) for window in data] - topk_dtw = sorted(range(len(distances)), key=lambda k: distances[k]) - print("Calculated exact dtw in: " + str(time()-t0)) - print(topk_dtw[0:20]) + response = pseudo.table_info(data, table) - t0 = time() - l2distances = [np.linalg.norm(window - query) for window in data] - print("Calculated exact l2 in: " + str(time()-t0)) - - # # distances_ed = [distance.euclidean(query, window) for window in data] - # # topk_ed = sorted(range(len(distances_ed)), key=lambda k: distances_ed[k]) - # - accuracy = 0 - for index in topk_dtw[0:20]: - if index in candidates: - accuracy += 1 - print(accuracy) - -# read_mts_data() -# create_mts_windows() -# debug_test_lsh() \ No newline at end of file + print("Averages calculated: " + str(time() - t0)) + return response \ No newline at end of file diff --git a/Flaskserver/preprocessing.py b/Flaskserver/preprocessing.py new file mode 100644 index 00000000..fe389268 --- /dev/null +++ b/Flaskserver/preprocessing.py @@ -0,0 +1,118 @@ +import numpy as np +import pandas as pd +from libs import bigwig +import bbi +import dask.dataframe as dd +import os.path +from sklearn import preprocessing + +def read_data(): + size = bbi.chromsizes('test.bigWig')['chr1'] + bins = 100000 + data = bigwig.get('data/test.bigWig', 'chr1', 0, size, bins) + print(data.shape) + response = [ + { + "index": list(range(0, size, int(size/(bins)))), + "values": data.tolist() + }, + { + "index": list(range(0, size, int(size / (bins)))), + "values": data.tolist() + }, + { + "index": list(range(0, size, int(size / (bins)))), + "values": data.tolist() + } + ] + return response + +def read_mts_data(): + filename = 'data/data.pkl' + if (not os.path.isfile(filename)): + print("start") + df = dd.read_csv("NW_Ground_Stations_2016.csv", usecols=['number_sta', 'date', 't', 'hu', 'td']) + print("read file") + df = df.loc[df['number_sta'].isin([14066001, 14137001, 14216001, 14372001, 22092001, 22113006, 22135001])].fillna(0) + print("split rows") + df = df.compute() + df.to_pickle(filename) + print("to_pandas") + df = pd.read_pickle(filename) + df.dropna(subset=['t'], inplace=True) + response = [ + { + "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), + "values": df.loc[df['number_sta'] == 14066001].loc[:, 't'].values.tolist() + }, + { + "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), + "values": df.loc[df['number_sta'] == 14066001].loc[:, 'hu'].values.tolist() + }, + { + "index": df.loc[df['number_sta'] == 14066001].loc[:, 'date'].values.astype(str).tolist(), + "values": df.loc[df['number_sta'] == 14066001].loc[:, 'td'].values.tolist() + } + ] + return response + +def create_peax_windows_12kb(window_size): + data = bigwig.chunk( + 'test.bigWig', + 12000, + int(12000 / window_size), + int(12000 / 6), + ['chr1'], + verbose=True, + ) + data = np.reshape(data, (len(data), 1, len(data[0]))) + np.save(data_path, data) + return '1' + +def create_peax_windows_12kb_mts(window_size): + data = bigwig.chunk( + 'test.bigWig', + 12000, + int(12000 / window_size), + int(12000 / 6), + ['chr1'], + verbose=True, + ) + data = np.reshape(data, (len(data), 1, len(data[0]))) + data2 = np.copy(data) + np.random.shuffle(data2) + data3 = np.copy(data) + np.random.shuffle(data3) + + data = np.concatenate((data, data2), axis=1) + data = np.concatenate((data, data3), axis=1) + np.save(data_path, data) + return '1' + +def create_eeg_windows(window_size, nr_of_channels): + datafile = '21.csv' + data = pd.read_csv(datafile, header=None) + npdata = np.array(data) + window_data = [npdata[i:i + window_size, 0:nr_of_channels] for i in range(0, npdata.shape[0] - window_size, int(window_size / 8))] + del npdata + np_window_data = np.repeat(window_data, repeats=3, axis=0) + del window_data + data = np.reshape(np_window_data, (len(np_window_data), nr_of_channels, len(np_window_data[0]))) + np.save(data_path, data) + return '1' + +def create_weather_windows(window_size): + filename = 'data/data.pkl' + df = pd.read_pickle(filename) + channels = list() + channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 't'].fillna(0).values.tolist()) + channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 'hu'].fillna(0).values.tolist()) + channels.append(df.loc[df['number_sta'] == 14066001].loc[:, 'td'].fillna(0).values.tolist()) + data = [([values[i:i+window_size] for values in channels]) for i in range(0, len(channels[0]) - window_size, 1)] + windows = [] + for i in range(len(data)): + if i % 5000 == 0: + print(i) + windows.append(preprocessing.minmax_scale(data[i], (-1, 1), axis=1)) + np.save('processed-data', windows) + return '1' \ No newline at end of file diff --git a/Flaskserver/pseudo.py b/Flaskserver/pseudo.py new file mode 100644 index 00000000..9adf51db --- /dev/null +++ b/Flaskserver/pseudo.py @@ -0,0 +1,243 @@ +import numpy as np +from time import time +import _ucrdtw +import _lsh +import math +from libs.DBA_multivariate import performDBA +from tslearn.metrics import dtw +from collections import defaultdict + +def lsh(data, query, parameters = None, weights = None): + if parameters is None: + parameters = preprocess(data) + r = parameters[0] + a = parameters[1] + sd = parameters[2] + + if weights is None: + candidates, distances, hf = _lsh.lsh(data, query, r, a, sd) + else: + candidates, distances, hf = _lsh.lsh(data, query, r, a, sd, weights) + + dict = defaultdict(int) + for l in range(len(candidates)): + for k in range(len(candidates[0])): + for i in range(len(candidates[0][0])): + dict[candidates[l][k][i]] += distances[l][k][i] + sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])} + average_candidates = np.array(list(sorted_dict.keys())).tolist() + average_distances = np.array(list(sorted_dict.values())).tolist() + + tables = [] + samples_set = set() + candidates = candidates.tolist() + for l in range(len(candidates)): + for k in range(len(candidates[0])): + samples_set.update(candidates[l][k][0:5]) + dict = defaultdict(list) + length = len(distances[l][k]) + median = distances[l][k][math.ceil(length/2)] + stepsize = median / 10 + indices = list(map(lambda x: 19 if x > median * 2 else math.floor(x / stepsize), distances[l][k])) + for i in range(len(candidates[0][0])): + dict[str(indices[i])].append(candidates[l][k][i]) + tables.append(dict) + + length = len(average_distances) + median = average_distances[math.ceil(length/2)] + stepsize = median / 10 + indices = list(map(lambda x: 19 if x > median * 2 else math.floor(x / stepsize), average_distances)) + average_table = defaultdict(list) + for i in range(len(average_candidates)): + average_table[str(indices[i])].append(average_candidates[i]) + + samples = np.array(list(filter(lambda x: x in samples_set, average_candidates))).tolist() + + + response = { + "hash_functions": hf.reshape((len(candidates) * len(candidates[0]), len(query[0]))).tolist(), + "candidates": candidates, + "distances": distances.tolist(), + "average_candidates": average_candidates, + "average_distances": average_distances, + "tables": tables, + "average_table": average_table, + "samples": list(samples), + "parameters": [float(r), float(a), float(sd)] + } + return response + +def preprocess(data, r=10.0): + subset = [] + t0 = time() + + i = 0 + while i < len(data): + if i % 999 == 0: + print(r) + print(str(i) + ':' + str(len(subset))) + + state = 1 + for s in subset: + if np.linalg.norm(data[i] - data[s]) < r: + state = 0 + break + if state == 1: + subset.append(i) + + i = i + 1 + if i == 10000 and len(subset) < 10: + r = r / 2 + subset = [] + i = 0 + if len(subset) > 200: + r = r + r / 2 + subset = [] + i = 0 + + # subset = sample(list(range(len(data))), 200) + print("r = " + str(r)) + dtw_distances = [] + eq_distances = [] + for i, index_1 in enumerate(subset): + print(i) + for j, index_2 in enumerate(subset): + if index_1 == index_2: + continue + e = np.linalg.norm(data[index_1] - data[index_2]) + if (math.isnan(e) or e == 0): + eq_distances.append(0.0001) + dtw_distances.append(0.0001) + continue + eq_distances.append(e) + d = dtw(data[index_1], data[index_2], global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05*120)) + dtw_distances.append(d) + + ratios = np.array(dtw_distances)/np.array(eq_distances) + mean_dtw = np.mean(dtw_distances) + sd_dtw = np.std(dtw_distances) + mean_eq = np.mean(eq_distances) + sd_eq = np.std(eq_distances) + a = np.mean(ratios) + sd = np.std(ratios) + theta = mean_dtw + -2.58 * sd_dtw + # theta = mean_eq + -2.58 * sd_eq + r = theta / ((a-sd)*math.sqrt(120)) + if r < 0: + r = mean_dtw / 100 + # r = theta / (math.sqrt(120)) + print('Mean: ' + str(mean_dtw)) + print('Stdev: ' + str(sd_dtw)) + print('Ratio mean: ' + str(a)) + print('Ratio stdev: ' + str(sd)) + print('Theta: ' + str(theta)) + print('r: ' + str(r)) + print('Preprocessing time: ' + str(time() - t0)) + return r, a, sd + +def weights(data, query, old_weights, labels, hash_functions): + alpha = 0.2 + all_good_windows = data[[[int(index) for index, value in labels.items() if value is True]]] + + good_distances = np.zeros(len(query)) + for window in all_good_windows: + for i in range(len(all_good_windows[0])): + good_distances[i] += _ucrdtw.ucrdtw(query[i], window[i], 0.05, False)[1] + if len(all_good_windows) != 0: + good_distances = np.square(good_distances) + if np.sum(good_distances) != 0: + good_distances /= np.sum(good_distances) + good_distances = np.ones(len(query)) - good_distances + good_distances /= np.sum(good_distances) + good_distances *= len(all_good_windows[0]) + good_distances = np.sqrt(good_distances) + + if len(hash_functions) != 0: + summed_hash_functions = np.sum(hash_functions, axis=0) + summed_hash_functions = np.square(summed_hash_functions) + normalized_hash_functions = summed_hash_functions / np.sum(summed_hash_functions) + normalized_hash_functions *= len(hash_functions[0]) + + if len(hash_functions) + len(all_good_windows) == 0: + print("no update") + new_weights = old_weights + elif len(hash_functions) == 0: + print("only windows") + new_weights = alpha * np.array(old_weights) + (1 - alpha) * good_distances + elif len(all_good_windows) == 0: + print("only tables") + new_weights = alpha * np.array(old_weights) + (1 - alpha) * normalized_hash_functions + else: + print("tables & windows") + new_weights = alpha * np.array(old_weights) + 0.5 * (1-alpha) * good_distances + 0.5 * (1-alpha) * normalized_hash_functions + + print(new_weights) + return new_weights.tolist() + +def table_info(data, table): + prototypes = [] + for cluster in table: + windows = data[cluster] + average_values = np.average(windows, 0) + std_values = np.std(windows, 0) + max_values = average_values + std_values + min_values = average_values - std_values + prototypes.append({ + 'average': average_values.tolist(), + 'max': max_values.tolist(), + 'min': min_values.tolist() + }) + # distances = [[dtw(np.array(v["average"]), np.array(w["average"]), global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05 * 120)) for j, w in enumerate(prototypes)] for i, v in enumerate(prototypes)] + return {'prototypes': prototypes, 'distances': []} + +def query(data, window_indices): + if isinstance(window_indices, int): + output = data[window_indices] + else: + indices = [int(index) for index, value in window_indices.items() if value is True] + indices_windows = data[indices] + output = performDBA(indices_windows) + return output.tolist() + +def debug_test_lsh(): + data = np.load('processed-data.npy') + # data = np.repeat(data, repeats=7, axis=1) + print(data.shape) + data = np.reshape(data, (len(data), len(data[0][0]), len(data[0]))) + + r, a, sd = preprocess(data, 11.25) + query_n = 1234 + t0 = time() + query = data[query_n] + data = data.astype('double') + dict = defaultdict(int) + candidates, distances, hf = _lsh.lsh(data, query, r, a, sd) + print("Calculated approximate in: " + str(time()-t0)) + for l in range(len(candidates)): + for k in range(len(candidates[0])): + for i in range(len(candidates[0][0])): + dict[candidates[l][k][i]] += distances[l][k][i] + sorted_dict = {k: v for k, v in sorted(dict.items(), key=lambda item: item[1])} + candidates = list(sorted_dict.keys()) + + print(candidates[0:20]) + + t0 = time() + # distances = [dtw_ndim.distance_fast(window, query) for window in data] + distances = [dtw(window, query, global_constraint='sakoe_chiba', sakoe_chiba_radius=int(0.05*120)) for window in data] + topk_dtw = sorted(range(len(distances)), key=lambda k: distances[k]) + print("Calculated exact dtw in: " + str(time()-t0)) + print(topk_dtw[0:20]) + + t0 = time() + l2distances = [np.linalg.norm(window - query) for window in data] + print("Calculated exact l2 in: " + str(time()-t0)) + + # # distances_ed = [distance.euclidean(query, window) for window in data] + # # topk_ed = sorted(range(len(distances_ed)), key=lambda k: distances_ed[k]) + # + accuracy = 0 + for index in topk_dtw[0:20]: + if index in candidates: + accuracy += 1 + print(accuracy) diff --git a/Flaskserver/topk.npy b/Flaskserver/topk.npy deleted file mode 100644 index cd3b20f8..00000000 --- a/Flaskserver/topk.npy +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:76c9d862591f8291da412257fb4eff58ec5f567d7c7b14a46de3d5269958c863 -size 997096 -- GitLab