diff --git a/release-packaging/HelpSource/Classes/FluidGrid.schelp b/release-packaging/HelpSource/Classes/FluidGrid.schelp new file mode 100644 index 0000000..470df16 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidGrid.schelp @@ -0,0 +1,201 @@ +TITLE:: FluidGrid +summary:: Constrain a 2D DataSet into a Grid. +categories:: Libraries>FluidCorpusManipulation +related:: Classes/FluidMDS, Classes/FluidPCA, Classes/FluidDataSet + +DESCRIPTION:: + +Hello. I put stuff in a 2-dimension link::Classes/FluidDataSet:: in the most even grid possible by minimising the distance I need to move each item around, using some clever algorithms. The grid space can be oversampled to allow for a sparser representation. The resulting grid shape can be constraint in one axis. + +Please refer to a webpage and an article for more information on the algorithm. + +CLASSMETHODS:: + +METHOD:: new +Make a new instance + +ARGUMENT:: server +The server on which to run this model + +ARGUMENT:: oversample +A factor to oversample the destination grid. The default is 1, so the most compact grid possible will be yield. Factors of 2 or more will allow a larger destination grid, which will respect the original shape a little more, but will therefore be sparser. + +ARGUMENT:: extent +The size to which the selected axis will be constraint to. The default is 0, which turns the constraints off. + +ARGUMENT:: axis +The axis on which the constraint size is applied to. The default (0) is horizontal, and (1) is vertical. + +INSTANCEMETHODS:: + +PRIVATE:: init + +METHOD:: fitTransform +Fit the model to a link::Classes/FluidDataSet:: and write the new projected data to a destination FluidDataSet. +ARGUMENT:: sourceDataSet +Source data, or the DataSet name +ARGUMENT:: destDataSet +Destination data, or the DataSet name +ARGUMENT:: action +Run when done + +EXAMPLES:: + +STRONG::A didactic example:: + +code:: + +/// make a simple grid of numbers +~simple = Dictionary.newFrom(36.collect{|i|[i.asSymbol, [i.mod(9), i.div(9)]]}.flatten(1)); + +//look at it +( +w = Window("the source", Rect(128, 64, 230, 100)); +w.drawFunc = { + Pen.use { + ~simple.keysValuesDo{|key, val| + Pen.stringCenteredIn(key, Rect((val[0] * 25), (val[1] * 25), 25, 25), Font( "Helvetica", 12 ), Color.black) + } + } +}; +w.refresh; +w.front; +) + + +//load it in a dataset +~raw = FluidDataSet(s); +~raw.load(Dictionary.newFrom([\cols, 2, \data, ~simple])); + +// make a grid out of it +~grid = FluidGrid(s); +~gridified = FluidDataSet(s); +~grid.fitTransform(~raw, ~gridified, action:{~gridified.dump{|x|~gridifiedDict = x["data"];\gridded.postln;}}) + +// watch the grid +( +w = Window("a perspective", Rect(358, 64, 350, 230)); +w.drawFunc = { + Pen.use { + ~gridifiedDict.keysValuesDo{|key, val| + Pen.stringCenteredIn(key, Rect((val[0] * 25), (val[1] * 25), 25, 25), Font( "Helvetica", 12 ), Color.black) + } + } +}; +w.refresh; +w.front; +) + +// change the constraints and draw again +( +~grid.axis_(0).extent_(4).fitTransform(~raw, ~gridified, action:{ + ~gridified.dump{|x| + ~gridifiedDict = x["data"];\gridded.postln; + {w.refresh;}.defer; +}}) +) + +// here we constrain in the other dimension +( +~grid.axis_(1).extent_(3).fitTransform(~raw, ~gridified, action:{ + ~gridified.dump{|x| + ~gridifiedDict = x["data"];\gridded.postln; + {w.refresh;}.defer; +}}) +) + +//oversampling yields the shape...ish! +( +~grid.oversample_(2).extent_(0).fitTransform(~raw, ~gridified, action:{ + ~gridified.dump{|x| + ~gridifiedDict = x["data"];\gridded.postln; + {w.refresh;}.defer; +}}) +) +:: + +STRONG::A more colourful example:: + +code:: + +// make all dependencies +( +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); +~normalized = FluidDataSet(s); +~standardizer = FluidStandardize(s); +~normalizer = FluidNormalize(s, 0.05, 0.95); +~umap = FluidUMAP(s).numDimensions_(2).numNeighbours_(5).minDist_(0.2).iterations_(50).learnRate_(0.2); +~grid = FluidGrid(s); +~gridified = FluidDataSet(s); +) + +// build a dataset of 400 points in 3D (colour in RGB) +~colours = Dictionary.newFrom(400.collect{|i|[("entry"++i).asSymbol, 3.collect{1.0.rand}]}.flatten(1)); +~raw.load(Dictionary.newFrom([\cols, 3, \data, ~colours])); + +//First standardize our DataSet, then apply the UMAP to get 2 dimensions, then normalise these 2 for drawing in the full window size +( +~standardizer.fitTransform(~raw,~standardized,action:{"Standardized".postln}); +~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); +~normalizer.fitTransform(~reduced,~normalized,action:{"Normalized Output".postln}); +) +//we recover the reduced dataset +~normalized.dump{|x| ~normalizedDict = x["data"]}; + +//Visualise the 2D projection of our original 4D data +( +w = Window("a perspective", Rect(128, 64, 200, 200)); +w.drawFunc = { + Pen.use { + ~normalizedDict.keysValuesDo{|key, val| + Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); + Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); + ~colours[key.asSymbol].flat; + } + } +}; +w.refresh; +w.front; +) + +//Force the UMAP-reduced dataset into a grid, normalise for viewing then print in another window +( +~grid.fitTransform(~reduced,~gridified,action:{"Gridded Output".postln; + ~normalizer.fitTransform(~gridified,~normalized,action:{"Normalized Output".postln; + ~normalized.dump{|x| + ~normalizedDict = x["data"]; + { + y = Window("a grid", Rect(328, 64, 200, 200)); + y.drawFunc = { + Pen.use { + ~normalizedDict.keysValuesDo{|key, val| + Pen.fillColor = Color.new(~colours[key.asSymbol][0], ~colours[key.asSymbol][1],~colours[key.asSymbol][2]); + Pen.fillOval(Rect((val[0] * 200), (val[1] * 200), 5, 5)); + ~colours[key.asSymbol].flat; + } + } + }; + y.refresh; + y.front; + }.defer; + }; + }); +}); +) + +// This looks ok, but let's improve it with oversampling +( +~grid.oversample_(3).fitTransform(~reduced,~gridified,action:{"Gridded Output".postln; + ~normalizer.fitTransform(~gridified,~normalized,action:{"Normalized Output".postln; + ~normalized.dump{|x| + ~normalizedDict = x["data"]; + { + y.refresh; + }.defer; + }; + }); +}); +) +::