diff --git a/.gitignore b/.gitignore index fea7c45..6a3e68d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ - -**/.DS_Store +**/.DS_Store \ No newline at end of file diff --git a/classes/Datasets/Transforms.sc b/classes/Datasets/Transforms.sc new file mode 100644 index 0000000..f2daee0 --- /dev/null +++ b/classes/Datasets/Transforms.sc @@ -0,0 +1,122 @@ +/***************************************** +Data Transforms +(C) 2018 Jonathan Reus + +Tools for analyzing and transforming datasets: scaling, normalization, standardization, PCA, etc.. + + +******************************************/ + +// TO NORMALIZE: +// find min / max of features +// normalized_value = (val - min) / (max - min) + + +/* +T for Transform +Normalization: +1. find min/max of each feature +2. normalized_value = (val - min) / (max - min) +*/ +TNormalizer { + var highlightColor, highlightSize, <>isHighlight, <>drawAxis, <>drawValues; +and later... + +(C) 2018 Jonathan Reus / IEM Graz + +Views for Cartesian point / scatterplots + + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +For more details see: https://www.gnu.org/licenses/ +**********************************************************************/ + + + +/* +@class ScatterPlotter + +A higher level view abstraction that manages multiple scatterplots on a single graph. +All scatterplots share a single axis specification and commands for drawing / showing axes. + +@usage + +n = 100; +x = Array.series(n, 0, 100 / n) + Array.fill(n, {rrand(-15.0, 15.0)}); +y = Array.series(n, 0, 100 / n) + Array.fill(n, {rrand(-15.0, 15.0)}); +p = x.collect {|item,i| [item, y[i]] }; +x = Array.series(n, 0, 100 / n) + Array.fill(n, {rrand(-15.0, 15.0)}); +y = Array.series(n, 0, 100 / n).reverse + Array.fill(n, {rrand(-15.0, 15.0)}); +q = x.collect {|item,i| [item, y[i]] }; + +w = Window.new("X Scatter", 800@600); +j = ScatterPlotter.new(w, 800@600, p, [0,100].asSpec, [0,100].asSpec); +j.drawAxes_(true).drawGrid_(true).drawMethod_(\fillOval).symbolSize_(5@5); +w.front; + +j.addPlot(q); +j.addPlot([[0,0],[100,100]]); +j.backgroundColor_(Color.black).axisColor_(Color.gray); +j.symbolColor_(Color.blue,0); +j.symbolColor_(Color.green,1); +j.symbolColor_(Color.red,2).drawMethod_(\lineTo,2).symbolSize_(1,2); +j.setAxes([1,100,\exp].asSpec, [1,100,\exp].asSpec); + +// different random distributions +o = ScatterPlotter.new(nil, 800@400, Array.fill(100, {|i| [0.5.gauss(0.17), 0.3]})); +o.drawMethod_(\fillOval).drawAxes_(true).symbolSize_(3@3); +o.addPlot(Array.fill(100, {|i| [(0.5.bilinrand + 0.5), 0.5]})); +o.addPlot(Array.fill(100, {|i| [(0.5.sum3rand + 0.5), 0.7]})); +o.front; +*/ +ScatterPlotter : CompositeView { + var constrainToAxes; // boolean, if true points are constrained to visible axes + + *new {|parent, bounds, data, specX, specY, constrain=false| + ^super.new(parent, bounds).init(data, specX, specY, constrain); + } + + init {|data, specX, specY, constrain| + var firstplot; + constrainToAxes = constrain; + scatterviews = List.new; + firstplot = ScatterView.new(this, this.bounds, data, specX, specY); + scatterviews.add(firstplot); + ^this + } + + drawAxes_ {|bool| scatterviews.do {|plot| plot.drawAxes_(bool).refresh }; ^this; } + drawAxes { ^scatterviews[0].drawAxes; } + + drawGrid_ {|bool| scatterviews[0].drawGrid_(bool).refresh; ^this; } + drawGrid { ^scatterviews[0].drawGrid; } + + gridResolution_ {|dim| scatterviews[0].gridResolution_(dim).refresh; ^this; } + gridResolution { ^scatterviews[0].gridResolution; } + + axisColor_ {|color| scatterviews[0].axisColor_(color).refresh; ^this; } + axisColor { ^scatterviews[0].axisColor; } + + + gridColor_ {|color| scatterviews[0].gridColor_(color).refresh; ^this; } + gridColor { ^scatterviews[0].gridColor; } + + xAxisName_ {|name| scatterviews[0].xAxisName_(name).refresh; ^this; } + xAxisName { ^scatterviews[0].xAxisName; } + + yAxisName_ {|name| scatterviews[0].yAxisName_(name).refresh; ^this; } + yAxisName { ^scatterviews[0].yAxisName; } + + + drawGridValues_ {|bool| scatterviews[0].drawGridValues_(bool).refresh; ^this; } + drawGridValues { ^scatterviews[0].drawGridValues; } + + backgroundColor_ {|bgcolor| scatterviews[0].backgroundColor_(bgcolor).refresh; ^this; } + backgroundColor { ^scatterviews[0].backgroundColor; } + + getPlot {|idx=0| ^scatterviews.at(idx); } + deletePlot {|idx| if(idx != 0) { scatterviews.removeAt(idx); } } + + addPlot {|data| + var newplot; + newplot = ScatterView.new(this, this.bounds, data, scatterviews[0].specX, scatterviews[0].specY); + newplot.backgroundColor_(Color.clear).drawMethod_(scatterviews[0].drawMethod); + newplot.symbolColor_(Color.rand(0.0, 0.95)).symbolSize_(scatterviews[0].symbolSize); + newplot.drawAxes_(scatterviews[0].drawAxes).axisColor_(Color.clear); + scatterviews.add(newplot); + this.refresh; + ^newplot; + } + + setAxes {|xspec, yspec| + scatterviews.do {|plot, idx| + plot.setAxes(xspec, yspec, constrainToAxes); + plot.refresh; + }; + } + + setAxesLabels {|xlabel, ylabel| + scatterviews[0].xAxisName_(xlabel).yAxisName_(ylabel).refresh; + } + + data {|idx=0| ^scatterviews[idx].originalData } + data_ {|thedata, idx=0, constrainToBounds=false| + scatterviews[idx].data_(thedata, constrainToBounds).refresh; + ^this; + } + + drawValues_ {|bool, idx=0| scatterviews[idx].drawValues_(bool).refresh; ^this; } + valuesColor_ {|color, idx=0| scatterviews[idx].valuesColor_(color).refresh; ^this; } + symbolColor_ {|color, idx=0| scatterviews[idx].symbolColor_(color).refresh; ^this; } + symbolSize_ {|dim, idx=0| scatterviews[idx].symbolSize_(dim).refresh; ^this; } + drawMethod_ {|method, idx=0| scatterviews[idx].drawMethod_(method).refresh; ^this; } +} + + +/* +@class ScatterView + +A simple 2-dimensional scatterplot with coordinate axes. + +@usage + +( +n = 100; +x = Array.series(n, 0, 100 / n) + Array.fill(n, {rrand(-15.0, 15.0)}); +y = Array.series(n, 0, 100 / n) + Array.fill(n, {rrand(-15.0, 15.0)}); +p = x.collect {|item,i| [item, y[i]] }; + +w = Window.new("Linear Scatter", 800@600); +j = ScatterView.new(w, 800@600, p, [0,100].asSpec,[0,100].asSpec); +j.drawAxes_(true).drawGrid_(true).drawMethod_(\fillOval); +j.symbolSize_(5@5).symbolColor_(Color.blue); +w.front; +); + +// Show axis, grid and value labels +j.drawAxes_(true).drawGrid_(true).drawValues_(true).refresh; + +// Visual style +j.axisColor_(Color.gray(0.3)).gridColor_(Color.red).valuesColor_(Color.gray(0.7)).backgroundColor_(Color.black).refresh; + + +// Adjust axes & scale +j.setAxes([-50, 150, \lin].asSpec, [-50,150,\lin].asSpec).symbolSize_(3@3).drawValues_(false).refresh; +j.setAxes([1, 150, \exp].asSpec, [0, 100,\lin].asSpec).symbolSize_(5@5).drawGridValues_(true).refresh; + + +*/ +ScatterView : View { + var backgroundColor; + var <>highlightColor, highlightSize, <>isHighlight; + var <>drawAxes, <>drawGrid, <>drawGridValues, <>drawValues; + var <>gridResolution; + var <>axisColor,<>gridColor, <>valuesColor; var <>xAxisName, <>yAxisName; var symbolColor, <>drawMethod = \lineTo; - var adjustedData, specX, specY; + var <>constrainToBounds; - *new {|parent, bounds, data, specX, specY| - ^super.new.initPlot( - parent, bounds, data, specX, specY - ) + var background; var <>colorFunc; - var <>itemSize = 2, <>drawMethod = \lineTo; + var <>itemSize = 2; + // TODO: + //var <>drawMethod = \lineTo; //var <>highlightColor, highlightSize, <>isHighlight, <>drawAxis, <>drawValues; //var <>xAxisName, <>yAxisName; @@ -20,7 +62,7 @@ ScatterView2 { var selectionRect; var <>action; - var <>mouseDownAction, <>mouseUpAction, <>mouseMoveAction, mouseDownAction, <>mouseUpAction, <>mouseMoveAction, 0) { res.putRow(row, func.value(this.at(row))) }; + }); + ^res; + } + + // collect columns, where column length of the new matrix is arbitrary + collectCols {arg func; + var res, firstCol; + if(this.cols<1) { ^this }; + firstCol = func.value(this.getCol(0), 0); + res = Matrix.newClear(firstCol.size, this.cols); + res.putCol(0, firstCol); + this.cols.do({arg col; + if(col>0) { res.putCol(col, func.value(this.getCol(col))) }; + }); + ^res; + } + addRow { arg rowVals; var res; - if( (rowVals.flat.size == this.cols) - .and(rowVals.every({arg element; element.isNumber}) ),{ + if( (this.rows == 0).or((rowVals.flat.size == this.cols) + .and(rowVals.every({arg element; element.isNumber}))), { res = this.copy; res = res.add(rowVals); - ^res},{ + ^res }, { error("wrong type or size in Matrix-addRow");this.halt; }); } + insertRow { arg col, rowVals; var res; if( (rowVals.flat.size == this.cols) @@ -239,8 +268,8 @@ Matrix[slot] : Array { } addCol { arg colVals; var res; - if( (colVals.flat.size == this.rows) - .and(colVals.every({arg element; element.isNumber}) ),{ + if( (this.rows == 0).or((colVals.flat.size == this.rows) + .and(colVals.every({arg element; element.isNumber}))), { res = Matrix.newClear(this.rows, this.cols+1); res.rows.do({ arg row; var rowArray; @@ -248,7 +277,7 @@ Matrix[slot] : Array { rowArray = rowArray.add(colVals.at(row)); res.putRow( row, rowArray); }); - ^res},{ + ^res}, { error("wrong type or size in Matrix-addCol");this.halt; }); }