From 103655929ce3b9664d16263b77d3cedbb14393a9 Mon Sep 17 00:00:00 2001 From: anquetil Date: Mon, 25 Dec 2023 19:08:46 +0100 Subject: [PATCH 1/3] migrated AI-Viz-Roassal-HierarchicalClustering from Roassal2 to Roassal3 --- .../AIDendrogram.extension.st | 19 -- .../AIDendrogramLeaf.extension.st | 45 ----- .../AIDendrogramNode.extension.st | 162 ----------------- .../package.st | 1 - .../AIR2ConfusionMatrixViz.class.st | 5 - .../AIR2DendrogramViz.class.st | 135 -------------- .../AIR2ScatterMatrixViz.class.st | 172 ------------------ .../AIR2ScatterPlotViz.class.st | 136 -------------- src/AI-Viz-Roassal2/AIR2ScatterViz.class.st | 80 -------- src/AI-Viz-Roassal2/AIRoassal2Viz.class.st | 93 ---------- src/AI-Viz-Roassal2/Array.extension.st | 7 - src/AI-Viz-Roassal2/package.st | 1 - .../AIClusterEngine.extension.st | 2 +- .../AIDendrogram.extension.st | 52 ++++++ .../AIDendrogramLeaf.extension.st | 52 ++++++ .../AIDendrogramNode.extension.st | 147 +++++++++++++++ .../TestDendrogramPlot.class.st | 33 ++++ .../package.st | 1 + src/AI-Viz/AIViz.class.st | 21 --- src/AI-Viz/package.st | 1 - 20 files changed, 286 insertions(+), 879 deletions(-) delete mode 100644 src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogram.extension.st delete mode 100644 src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramLeaf.extension.st delete mode 100644 src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramNode.extension.st delete mode 100644 src/AI-Viz-Roassal2-HierarchicalClustering/package.st delete mode 100644 src/AI-Viz-Roassal2/AIR2ConfusionMatrixViz.class.st delete mode 100644 src/AI-Viz-Roassal2/AIR2DendrogramViz.class.st delete mode 100644 src/AI-Viz-Roassal2/AIR2ScatterMatrixViz.class.st delete mode 100644 src/AI-Viz-Roassal2/AIR2ScatterPlotViz.class.st delete mode 100644 src/AI-Viz-Roassal2/AIR2ScatterViz.class.st delete mode 100644 src/AI-Viz-Roassal2/AIRoassal2Viz.class.st delete mode 100644 src/AI-Viz-Roassal2/Array.extension.st delete mode 100644 src/AI-Viz-Roassal2/package.st rename src/{AI-Viz-Roassal2-HierarchicalClustering => AI-Viz-Roassal3-HierarchicalClustering}/AIClusterEngine.extension.st (72%) create mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st create mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramLeaf.extension.st create mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st create mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st create mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/package.st delete mode 100644 src/AI-Viz/AIViz.class.st delete mode 100644 src/AI-Viz/package.st diff --git a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogram.extension.st b/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogram.extension.st deleted file mode 100644 index d9cf5e7..0000000 --- a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogram.extension.st +++ /dev/null @@ -1,19 +0,0 @@ -Extension { #name : #AIDendrogram } - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogram >> totalDepth [ - - ^ self isRoot - ifTrue: [ self nodeDepth * self depthFactor + self depthFactor ] - ifFalse: [ self parent totalDepth ] - -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogram >> withAllSubtrees [ - | stream | - - stream := Array new writeStream. - self inOrderDo: [ : e | stream nextPut: e ]. - ^ stream contents -] diff --git a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramLeaf.extension.st b/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramLeaf.extension.st deleted file mode 100644 index 83d1fe1..0000000 --- a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramLeaf.extension.st +++ /dev/null @@ -1,45 +0,0 @@ -Extension { #name : #AIDendrogramLeaf } - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramLeaf >> inOrderDo: aBlock [ - " Apply a block to the receiver's element " - - ^ aBlock value: self. -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramLeaf >> max: xStart sum: aFloat [ - - ^ xStart - - -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramLeaf >> nodeDepth [ - " Answer a with the receiver's depth " - - ^ 0.0 -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramLeaf >> nodeHeight [ - " Answer a with the receiver's height " - - ^ 1 -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramLeaf >> plotAt: aPoint scaling: aFloat view: aCanvas color: aColor [ - " Rrender the receiver's element (commonly a String) wrapped into a label with the receiver as a Roassal element " - | label | - - label := RTLabel new - text: self element; - elementOn: self. - aCanvas add: label. - label @ RTDraggable. - label translateTo: (aPoint x + self totalDepth) @ aPoint y. - ^ aColor - -] diff --git a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramNode.extension.st b/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramNode.extension.st deleted file mode 100644 index 003900b..0000000 --- a/src/AI-Viz-Roassal2-HierarchicalClustering/AIDendrogramNode.extension.st +++ /dev/null @@ -1,162 +0,0 @@ -Extension { #name : #AIDendrogramNode } - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> inOrderDo: aBlock [ - " Visit the elements of the binary tree in order: left, then root, then right, applying a block to each element " - - self left inOrderDo: aBlock. - aBlock value: self. - self right inOrderDo: aBlock. -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> leftHeight [ - " Answer a with the receiver's left children height " - - ^ self left nodeHeight -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> lineLengthThreshold [ - - ^ self isRoot - ifTrue: [ self max: 10 sum: self totalDepth ] - ifFalse: [ self parent lineLengthThreshold ] -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> max: xStart sum: aFloat [ - - | lineLength toPoint1 | - - lineLength := aFloat - self nodeDepth "self threshold * aFloat". - toPoint1 := xStart + lineLength. - - ^ (self left - max: toPoint1 - sum: aFloat) - max: - (self right - max: toPoint1 - sum: aFloat) -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> nodeDepth [ - " Answer a with the receiver's depth, the max of each child plus own distance " - - ^ (self left nodeDepth max: self right nodeDepth) + self threshold -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> nodeHeight [ - " Answer a with the receiver's height, the sum of each branch " - - ^ self left nodeHeight + self right nodeHeight -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> plotAt: aPoint scaling: aFloat view: aView color: aColor [ - " Private - Render the receiver's into aView using aPoint as starting point. - Assuming an horizontal orientation, this method calculates the distance for 3 lines: - The vertical line from the receiver to the leaves. - The horizontal lines to both leaves. - And then recursively render the leaves " - - | lHeightFactor rHeightFactor top bottom lineLength fromPoint1 toPoint2 y1 toPoint1 | - - lHeightFactor := self leftHeight * 20. - rHeightFactor := self rightHeight * 20. - - top := aPoint y - (lHeightFactor + (rHeightFactor / 2)). - bottom := aPoint y + (lHeightFactor + (rHeightFactor / 2)). - - lineLength := (aFloat - self nodeDepth)"self threshold * aFloat". - - fromPoint1 := aPoint x @ (top + (lHeightFactor / 2)). - toPoint1 := (aPoint x + lineLength) @ (top + (lHeightFactor / 2)). - - y1 := aPoint x @ (bottom - (rHeightFactor / 2)). - toPoint2 := (aPoint x + lineLength) @ (bottom - (rHeightFactor / 2)). - - self left - plotAt: toPoint1 - scaling: aFloat - view: aView - color: aColor. - self right - plotAt: toPoint2 - scaling: aFloat - view: aView - color: aColor. - - " Render vertical line from this node to the leaves " - self - plotLineIn: aView - from: fromPoint1 - to: y1 - color: aColor. - - " Render horizontal line to the left item " - self left isLeaf - ifTrue: [ toPoint1 := self lineLengthThreshold @ toPoint1 y ]. - self - plotLineIn: aView - from: fromPoint1 - to: toPoint1 - color: aColor. - - " Render horizontal line to the right item " - self right isLeaf - ifTrue: [ toPoint2 := self lineLengthThreshold @ toPoint2 y ]. - self plotLineIn: aView - from: y1 - to: toPoint2 - color: aColor. - - - -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> plotDendrogram [ - " Open a view of the receiver's. - Starting point to render the receiver using Roassal. Create a small line which is the root of the receiver's tree. Then recursively process the leaves. " - - | view | - - view := RTView new. - self - plotLineIn: view - from: 0 @ (self heightFactor / 2) - to: 10 @ (self heightFactor / 2) - color: Color black. - - self - plotAt: 10 @ (self heightFactor / 2) - scaling: self totalDepth - view: view - color: Color black. - view inspect -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> plotLineIn: view from: startPoint to: endPoint color: aColor [ - " Add a line from startPoint to endPoint into view " - - view canvas - addShape: - (TRLineShape new - from: startPoint; - to: endPoint; - color: aColor; - width: 2; - yourself). -] - -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } -AIDendrogramNode >> rightHeight [ - " Answer a with the receiver's right children height " - - ^ self right nodeHeight -] diff --git a/src/AI-Viz-Roassal2-HierarchicalClustering/package.st b/src/AI-Viz-Roassal2-HierarchicalClustering/package.st deleted file mode 100644 index c6ff3ea..0000000 --- a/src/AI-Viz-Roassal2-HierarchicalClustering/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #'AI-Viz-Roassal2-HierarchicalClustering' } diff --git a/src/AI-Viz-Roassal2/AIR2ConfusionMatrixViz.class.st b/src/AI-Viz-Roassal2/AIR2ConfusionMatrixViz.class.st deleted file mode 100644 index d9d6842..0000000 --- a/src/AI-Viz-Roassal2/AIR2ConfusionMatrixViz.class.st +++ /dev/null @@ -1,5 +0,0 @@ -Class { - #name : #AIR2ConfusionMatrixViz, - #superclass : #AIRoassal2Viz, - #category : #'AI-Viz-Roassal2' -} diff --git a/src/AI-Viz-Roassal2/AIR2DendrogramViz.class.st b/src/AI-Viz-Roassal2/AIR2DendrogramViz.class.st deleted file mode 100644 index 1a53572..0000000 --- a/src/AI-Viz-Roassal2/AIR2DendrogramViz.class.st +++ /dev/null @@ -1,135 +0,0 @@ -Class { - #name : #AIR2DendrogramViz, - #superclass : #AIRoassal2Viz, - #instVars : [ - 'orientation', - 'leafRotation', - 'leafFontSize', - 'leafColor' - ], - #category : #'AI-Viz-Roassal2' -} - -{ #category : #'instance creation' } -AIR2DendrogramViz class >> from: aClusterEngine [ - " Answer a new instance of the receiver using dendrogram data from aClusterEngine " - - ^ self new - initializeFrom: aClusterEngine; - yourself -] - -{ #category : #accessing } -AIR2DendrogramViz >> colorThreshold: aNumber [ - " Set the receiver's threshold at which the - - Colors all the descendent links below a cluster node the same color if is the first node below the cut threshold - " -] - -{ #category : #defaults } -AIR2DendrogramViz >> defaultLeafRotation [ - " Answer a used to set the default rotation of the receiver's leaves " - - self shouldBeImplemented -] - -{ #category : #defaults } -AIR2DendrogramViz >> defaultOrientation [ - " Answer a used to set the default direction of the receiver " - - ^ #top -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafColor [ - ^ leafColor - ifNil: [ leafColor := Color black ] -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafColor: anObject [ - leafColor := anObject -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafFontSize [ - ^ leafFontSize -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafFontSize: aNumber [ - " Set the receiver's leaves labels font size " - - leafFontSize := aNumber -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafRotation [ - " Answer a representing an angle of the receiver's leaf labels rotation " - - ^ leafRotation - ifNil: [ leafRotation := self defaultLeafRotation ] -] - -{ #category : #accessing } -AIR2DendrogramViz >> leafRotation: aNumber [ - " Set aNumber representing an angle of the receiver's leaf labels rotation " - - leafRotation := aNumber -] - -{ #category : #accessing } -AIR2DendrogramViz >> orientation [ - " Answer a representing the receiver's direction " - - ^ orientation - ifNil: [ orientation := self defaultOrientation ] -] - -{ #category : #accessing } -AIR2DendrogramViz >> rotate [ - " Rotate the receiver to the next orientation clock-wise " - - self shouldBeImplemented -] - -{ #category : #accessing } -AIR2DendrogramViz >> setBottomOrientation [ - " Set the receiver's direction placing the root at the bottom and nodes going downwards " - - orientation := #bottom -] - -{ #category : #accessing } -AIR2DendrogramViz >> setLeftOrientation [ - " Set the receiver's direction placing the root at the left and nodes going downwards " - - orientation := #left -] - -{ #category : #accessing } -AIR2DendrogramViz >> setRightOrientation [ - " Set the receiver's direction placing the root at the right and nodes going downwards " - - orientation := #right -] - -{ #category : #accessing } -AIR2DendrogramViz >> setTopOrientation [ - " Set the receiver's direction placing the root at the top and nodes going downwards " - - orientation := #top -] - -{ #category : #accessing } -AIR2DendrogramViz >> withBranchLengths [ - " Show branch length of each line in the receiver " - -] - -{ #category : #accessing } -AIR2DendrogramViz >> withoutLabels [ - " Omit plotting the leaves labels in the receiver " - -] diff --git a/src/AI-Viz-Roassal2/AIR2ScatterMatrixViz.class.st b/src/AI-Viz-Roassal2/AIR2ScatterMatrixViz.class.st deleted file mode 100644 index 13035e9..0000000 --- a/src/AI-Viz-Roassal2/AIR2ScatterMatrixViz.class.st +++ /dev/null @@ -1,172 +0,0 @@ -Class { - #name : #AIR2ScatterMatrixViz, - #superclass : #AIR2ScatterViz, - #instVars : [ - 'labelConversionBlock', - 'numberOfTicks' - ], - #category : #'AI-Viz-Roassal2' -} - -{ #category : #examples } -AIR2ScatterMatrixViz class >> example01 [ - - - AIR2ScatterMatrixViz - plot: AIDatasets loadIris - k: 3 -] - -{ #category : #examples } -AIR2ScatterMatrixViz class >> example02 [ - - - AIR2ScatterMatrixViz - plot: AIDatasets loadIris -] - -{ #category : #'instance creation' } -AIR2ScatterMatrixViz class >> plot: aDataFrame [ - - ^ self new - k: 1; - initializeWithDataFrame: aDataFrame; - plot -] - -{ #category : #'instance creation' } -AIR2ScatterMatrixViz class >> plot: aDataFrame k: predictedGroups [ - - ^ self new - k: predictedGroups; - initializeWithDataFrame: aDataFrame; - plot -] - -{ #category : #defaults } -AIR2ScatterMatrixViz >> defaultLabelConversionBlock [ - " Answer a which converts label to appropriate format for displaying into the receiver's canvas " - - ^ [ :n | - n == n asInteger - ifTrue: [ n asInteger ] - ifFalse: [ n asFloat ] ] - -] - -{ #category : #defaults } -AIR2ScatterMatrixViz >> defaultNumberOfTicks [ - " Answer a with defaults for the receiver's number of ticks " - - ^ 5 -] - -{ #category : #accessing } -AIR2ScatterMatrixViz >> graphBuilderMetrics [ - " Answer the receiver's metrics " - - ^ self graphBuilder metrics -] - -{ #category : #accessing } -AIR2ScatterMatrixViz >> graphObjects: aCollection [ - " Assume aCollection is a collection of Array. Set the builder's objects " - - self graphBuilder objects: (aCollection collect: #asArray) -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> initializeGraphAxis [ - - self graphBuilder axisX - numberOfTicks: self numberOfTicks; - rotateLabels; - labelConversion: self labelConversionBlock; - color: Color black. - - self graphBuilder axisY - numberOfTicks: 5; - labelConversion: self numberOfTicks; - color: Color black. - -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> initializeGraphColors [ - - | shape | - - shape := self graphBuilder shape. - shape circle - size: 3.5; - color: [ :a | self colorAt: a last ]. -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> initializeGraphMetrics [ - " Private - Discard last column from receiver's data frame. Set the grpah builder metrics " - - self dfColumnNames allButLast doWithIndex: [ : colName : index | - self graphBuilderMetrics - at: colName - put: (self numberToSelectorMapAt: index) ]. - - - -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> initializeGraphShape [ - - self graphBuilder lineShape: - (RTStyledMultiLine new - dashedLine; - width: 0.5; - yourself). - - -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> initializeWithDataFrame: aDataFrame [ - " Private - Initialize the receiver's internal state using data points in aDataFrame " - - self graphBuilder: RTScatterplotMatrix new. - self dataFrame: aDataFrame. - self graphObjects: self dataFrame asArrayOfRows. - self - initializeGraphAxis; - initializeGraphShape; - initializeGraphColors; - initializeGraphMetrics; - initializeLegendBuilder. -] - -{ #category : #accessing } -AIR2ScatterMatrixViz >> labelConversionBlock [ - - ^ labelConversionBlock - ifNil: [ labelConversionBlock := self defaultLabelConversionBlock ] - -] - -{ #category : #initialization } -AIR2ScatterMatrixViz >> numberOfTicks [ - " Answer a with the number of ticks to be displayed " - - ^ numberOfTicks - ifNil: [ numberOfTicks := self defaultNumberOfTicks ] -] - -{ #category : #private } -AIR2ScatterMatrixViz >> numberToSelectorMapAt: aNumber [ - " Private - There should be an easier way to do this " - - ^ { - 1 -> #first . - 2 -> #second . - 3 -> #third . - 4 -> #fourth . - 5 -> #fifth - } asDictionary at: aNumber -] diff --git a/src/AI-Viz-Roassal2/AIR2ScatterPlotViz.class.st b/src/AI-Viz-Roassal2/AIR2ScatterPlotViz.class.st deleted file mode 100644 index 9083403..0000000 --- a/src/AI-Viz-Roassal2/AIR2ScatterPlotViz.class.st +++ /dev/null @@ -1,136 +0,0 @@ -Class { - #name : #AIR2ScatterPlotViz, - #superclass : #AIR2ScatterViz, - #instVars : [ - 'featureASelector', - 'featureBSelector', - 'data', - 'colorMap' - ], - #category : #'AI-Viz-Roassal2' -} - -{ #category : #examples } -AIR2ScatterPlotViz class >> example01 [ - - " The first column is the Sepal Length feature " - " The second column is the Sepal Width feature " - - AIR2ScatterPlotViz - plot: AIDatasets loadIris -] - -{ #category : #examples } -AIR2ScatterPlotViz class >> example02 [ - - " The first column is the Sepal Length feature " - " The second column is the Sepal Width feature " - - AIR2ScatterPlotViz new - k: 3; - featureASelector: #second; - featureBSelector: #third; - colors: { - 'virginica' -> Color blue . - 'versicolor' -> Color green . - 'setosa' -> Color red } asDictionary; - initializeWithDataFrame: AIDatasets loadIris; - plot. - -] - -{ #category : #'instance creation' } -AIR2ScatterPlotViz class >> plot: aDataFrame [ - - ^ self new - k: 1; - initializeWithDataFrame: aDataFrame; - plot -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> data [ - ^ data - ifNil: [ data := RTData new ] -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> data: anObject [ - data := anObject -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> featureASelector [ - ^ featureASelector - ifNil: [ featureASelector := #first ] -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> featureASelector: anObject [ - featureASelector := anObject -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> featureBSelector [ - ^ featureBSelector - ifNil: [ featureBSelector := #second ] -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> featureBSelector: anObject [ - featureBSelector := anObject -] - -{ #category : #accessing } -AIR2ScatterPlotViz >> graphObjects: aCollection [ - " Assume aCollection is a collection of Array. Set the builder's objects " - - self data points: (aCollection collect: #asArray) -] - -{ #category : #initialization } -AIR2ScatterPlotViz >> initializeColorMap [ - " Private - Set the receiver's colors of data points using colorMap " - - self colors associationsDo: [ : assoc | - self data dotShape - if: [ :row | row last = assoc key ] - fillColor: assoc value. - self legendBuilder - addColor: assoc value - text: assoc key ]. -] - -{ #category : #initialization } -AIR2ScatterPlotViz >> initializeDataPoints [ - - self graphObjects: self dataFrame asArrayOfRows. - self data x: self featureASelector. - self data y: self featureBSelector. - self graphBuilder add: self data. -] - -{ #category : #initialization } -AIR2ScatterPlotViz >> initializeGraphAxis [ - " Private - Setup the axis labels and color " - - self graphBuilder axisY - title: (self dfColumnNames perform: featureASelector); - color: Color black. - self graphBuilder axisX - title: (self dfColumnNames perform: featureBSelector); - color: Color black. -] - -{ #category : #initialization } -AIR2ScatterPlotViz >> initializeWithDataFrame: aDataFrame [ - " Private - Initialize the receiver's internal state using data points in aDataFrame " - - self graphBuilder: RTGrapher new. - self dataFrame: aDataFrame. - self - initializeLegendBuilder; - initializeColorMap; - initializeDataPoints; - initializeGraphAxis -] diff --git a/src/AI-Viz-Roassal2/AIR2ScatterViz.class.st b/src/AI-Viz-Roassal2/AIR2ScatterViz.class.st deleted file mode 100644 index f46c528..0000000 --- a/src/AI-Viz-Roassal2/AIR2ScatterViz.class.st +++ /dev/null @@ -1,80 +0,0 @@ -Class { - #name : #AIR2ScatterViz, - #superclass : #AIRoassal2Viz, - #instVars : [ - 'k', - 'colors' - ], - #category : #'AI-Viz-Roassal2' -} - -{ #category : #accessing } -AIR2ScatterViz >> colorAt: keyName [ - " Answer a in the receiver's colors at keyName " - - ^ self colors at: keyName -] - -{ #category : #accessing } -AIR2ScatterViz >> colors [ - " Answer a of receiver's k instances " - - ^ colors - ifNil: [ colors := self defaultColors ] -] - -{ #category : #accessing } -AIR2ScatterViz >> colors: aCollection [ - " Set the receiver's colors to be aCollection " - - colors := aCollection -] - -{ #category : #defaults } -AIR2ScatterViz >> defaultColors [ - " Answer a with defaults for the receiver's feature colors " - - ^ k = 1 - ifTrue: [ (self dfClusterNames collect: [ : featureName | featureName -> Color black ]) asDictionary ] - ifFalse: [ - (self dfClusterNames - with: (Color wheel: self dfClusterNames size) - collect: [ : featureName : colorName | featureName -> colorName ]) asDictionary ]. - -] - -{ #category : #initialization } -AIR2ScatterViz >> initializeLegendBuilder [ - " Set the receiver's legend. If we are just exploring the points cloud (k = 1) then omit legend " - - self k = 1 - ifTrue: [ ^ self ]. - self legendBuilder - view: self graphBuilder view; - addText: self legendTitle. - - self colors keysDo: [ : key | - self legendBuilder - addColor: (self colorAt: key) - text: key ]. - - self legendBuilder build. -] - -{ #category : #accessing } -AIR2ScatterViz >> k [ - ^ k -] - -{ #category : #accessing } -AIR2ScatterViz >> k: anObject [ - k := anObject -] - -{ #category : #drawing } -AIR2ScatterViz >> plot [ - " Open a new window with the receiver's rendered into it " - - self graphBuilder open. - -] diff --git a/src/AI-Viz-Roassal2/AIRoassal2Viz.class.st b/src/AI-Viz-Roassal2/AIRoassal2Viz.class.st deleted file mode 100644 index 7b97143..0000000 --- a/src/AI-Viz-Roassal2/AIRoassal2Viz.class.st +++ /dev/null @@ -1,93 +0,0 @@ -" -This class provides defaults for axis, subplots, for the Roassal visualization engine. It also provides subclasses with a legend builder and legend title accessors. - -Internal Representation and Key Implementation Points. - - Instance Variables - graphBuilder: - legendBuilder: - legendTitle: - - - Implementation Points -" -Class { - #name : #AIRoassal2Viz, - #superclass : #AIViz, - #instVars : [ - 'legendBuilder', - 'graphBuilder', - 'legendTitle', - 'dataFrame' - ], - #category : #'AI-Viz-Roassal2' -} - -{ #category : #'accessing - data' } -AIRoassal2Viz >> dataFrame [ - " Answer the receiver's data source, a object " - - ^ dataFrame -] - -{ #category : #'accessing - data' } -AIRoassal2Viz >> dataFrame: anObject [ - dataFrame := anObject -] - -{ #category : #defaults } -AIRoassal2Viz >> defaultLegendTitle [ - " Answer a with the text for the receiver's Legend box " - - ^ 'Legend' -] - -{ #category : #'accessing - data' } -AIRoassal2Viz >> dfClusterNames [ - " Answer a of unique elements representing the receiver's actual features " - - ^ (self dataFrame columnAt: self dataFrame numberOfColumns) asOrderedSet -] - -{ #category : #'accessing - data' } -AIRoassal2Viz >> dfColumnNames [ - " Answer a of representing the receiver's column names " - - ^ self dataFrame columnNames -] - -{ #category : #accessing } -AIRoassal2Viz >> graphBuilder [ - ^ graphBuilder -] - -{ #category : #accessing } -AIRoassal2Viz >> graphBuilder: anObject [ - graphBuilder := anObject -] - -{ #category : #accessing } -AIRoassal2Viz >> legendBuilder [ - " Answer a to describe receiver's elements " - - ^ legendBuilder - ifNil: [ legendBuilder := RTLegendBuilder new ] -] - -{ #category : #accessing } -AIRoassal2Viz >> legendBuilder: anObject [ - legendBuilder := anObject -] - -{ #category : #initialization } -AIRoassal2Viz >> legendTitle [ - " Answer a with the receiver's legend title " - - ^ legendTitle - ifNil: [ legendTitle := self defaultLegendTitle ] -] - -{ #category : #accessing } -AIRoassal2Viz >> legendTitle: anObject [ - legendTitle := anObject -] diff --git a/src/AI-Viz-Roassal2/Array.extension.st b/src/AI-Viz-Roassal2/Array.extension.st deleted file mode 100644 index 08e32ca..0000000 --- a/src/AI-Viz-Roassal2/Array.extension.st +++ /dev/null @@ -1,7 +0,0 @@ -Extension { #name : #Array } - -{ #category : #'*AI-Viz-Roassal2' } -Array >> withAllSubtrees [ - - ^ (self collect: #withAllSubtrees) flatten -] diff --git a/src/AI-Viz-Roassal2/package.st b/src/AI-Viz-Roassal2/package.st deleted file mode 100644 index 9a12aa4..0000000 --- a/src/AI-Viz-Roassal2/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #'AI-Viz-Roassal2' } diff --git a/src/AI-Viz-Roassal2-HierarchicalClustering/AIClusterEngine.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIClusterEngine.extension.st similarity index 72% rename from src/AI-Viz-Roassal2-HierarchicalClustering/AIClusterEngine.extension.st rename to src/AI-Viz-Roassal3-HierarchicalClustering/AIClusterEngine.extension.st index 5616856..d89b8aa 100644 --- a/src/AI-Viz-Roassal2-HierarchicalClustering/AIClusterEngine.extension.st +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIClusterEngine.extension.st @@ -1,6 +1,6 @@ Extension { #name : #AIClusterEngine } -{ #category : #'*AI-Viz-Roassal2-HierarchicalClustering' } +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } AIClusterEngine >> plotDendrogram [ " Plot the receiver as dendrogram figure " diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st new file mode 100644 index 0000000..291ca4f --- /dev/null +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st @@ -0,0 +1,52 @@ +Extension { #name : #AIDendrogram } + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> baselineHeight [ + "height of one line in the dendrogram plot" + + ^20 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> baselineLength [ + "length between two 'levels' in the dendrogram plot + Actual horizontal position of a level also depends on its #threshold" + + ^30 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> minimalLeafLine [ + "minimal length of a line to a leaf (threshold of the node close to 0)" + + ^5 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> nodeDepth [ + + self subclassResponsibility +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> thresholdHorizontalScaling [ + "computes the scale between a DendrogramNode's threshold and its horizontal position in the plot + - The total width of the plot is: max numer of levels * #baselineLength + - this width is divided by the max threshold (threshold of root) to get the scaling factor + - leafs of the dendrogram are all at x=0 + - horizontal position of a node is its treshold * scaling factor + - everything is shifted left of #minimalLeafLine to ensure that nodes with a threshold close + to 0 still have a small line" + + ^self isRoot + ifTrue: [ -1 * (self minimalLeafLine + self nodeDepth * self baselineLength / self threshold) ] + ifFalse: [ self parent thresholdHorizontalScaling ] + +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogram >> thresholdPosition: thresholdScaling [ + + self subclassResponsibility + +] diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramLeaf.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramLeaf.extension.st new file mode 100644 index 0000000..df16ba3 --- /dev/null +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramLeaf.extension.st @@ -0,0 +1,52 @@ +Extension { #name : #AIDendrogramLeaf } + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> inOrderDo: aBlock [ + " Apply a block to the receiver's element " + + ^ aBlock value: self. +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> max: xStart sum: aFloat [ + + ^ xStart + + +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> nodeDepth [ + " Answer a with the receiver's depth " + + ^ 1 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> nodeHeight [ + " Answer a with the receiver's height " + + ^ 1 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> plotAt: aPoint scaling: aFloat view: aCanvas color: aColor [ + " Rrender the receiver's element (commonly a String) wrapped into a label " + + | label | + label := RSLabel new + text: self element; + color: aColor. + + "text is centered so shifting it right by half its width" + label position: 0 + (label textWidth / 2) @ aPoint y. + aCanvas add: label. + ^ aPoint +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramLeaf >> thresholdPosition: thresholdScaling [ + + ^0 + +] diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st new file mode 100644 index 0000000..4065cd8 --- /dev/null +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st @@ -0,0 +1,147 @@ +Extension { #name : #AIDendrogramNode } + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> inOrderDo: aBlock [ + " Visit the elements of the binary tree in order: left, then root, then right, applying a block to each element " + + self left inOrderDo: aBlock. + aBlock value: self. + self right inOrderDo: aBlock. +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> leftHeight [ + " Answer a with the receiver's left children height " + + ^ self left nodeHeight +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> nodeDepth [ + " Answer a with the receiver's depth, the max of each child plus own distance " + + ^ (self left nodeDepth max: self right nodeDepth) + 1 +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> nodeHeight [ + " Answer a with the receiver's height, the sum of each branch " + + ^ self left nodeHeight + self right nodeHeight +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> plotAt: aPoint scaling: thresholdHorizontalScaling view: aCanvas color: aColor [ + " Private - Render the receiver's into aView using aPoint as starting point. + Assuming an horizontal orientation, this method calculates the distance for 3 lines: + The vertical line from the receiver to the leaves. + The horizontal lines to both leaves. + And then recursively render the leaves " + + | fromPointL toPointL fromPointR toPointR | + + "horizontal line extremitites for left branch" + fromPointL := aPoint x @ (aPoint y - (self baselineHeight / 2 * self rightHeight)). + toPointL := (self left thresholdPosition: thresholdHorizontalScaling) @ fromPointL y. + + "horizontal line extremitites for right branch" + fromPointR := aPoint x @ (aPoint y + (self baselineHeight / 2 * self leftHeight)). + toPointR := (self right thresholdPosition: thresholdHorizontalScaling) @ fromPointR y. + + self left + plotAt: toPointL + scaling: thresholdHorizontalScaling + view: aCanvas + color: aColor. + + self right + plotAt: toPointR + scaling: thresholdHorizontalScaling + view: aCanvas + color: aColor. + + " Render vertical line from this node to the leaves " + self + plotLineIn: aCanvas + from: fromPointL + to: fromPointR + color: aColor. + + " Render horizontal line for the left branch " + self left isLeaf + ifTrue: [ toPointL := 0 @ toPointL y ]. + self + plotLineIn: aCanvas + from: fromPointL + to: toPointL + color: aColor. + + " Render horizontal line for the right branch " + self right isLeaf + ifTrue: [ toPointR := 0 @ toPointR y ]. + self plotLineIn: aCanvas + from: fromPointR + to: toPointR + color: aColor. + +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> plotDendrogram [ + " Open a view of the receiver's. + Starting point to render the receiver using Roassal. Create a small line which is the root of the receiver's tree. Then recursively process the leaves. " + + | canvas thresholdScaling fromPoint toPoint | + + canvas := RSCanvas new. + canvas @ RSCanvasController. + + thresholdScaling := self thresholdHorizontalScaling. + + "first horizontal line: root" + fromPoint := (self thresholdPosition: thresholdScaling) @ (self heightFactor / 2). + toPoint := (fromPoint x + self baselineLength) @ (fromPoint y). + + self + plotLineIn: canvas + from: fromPoint + to: toPoint + color: Color black. + + self + plotAt: toPoint + scaling: thresholdScaling + view: canvas + color: Color black. + canvas inspect +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> plotLineIn: view from: startPoint to: endPoint color: aColor [ + "Add a line from startPoint to endPoint into view " + + view canvas + add: + (RSLine new + from: startPoint; + to: endPoint; + color: aColor; + width: 2; + yourself). +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> rightHeight [ + " Answer a with the receiver's right children height " + + ^ self right nodeHeight +] + +{ #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } +AIDendrogramNode >> thresholdPosition: thresholdScaling [ + "X position depends on threshold of the node + -5 to have a minimal space before all leaves (all leaves are at 0)" + + ^(self threshold * thresholdScaling) - self minimalLeafLine + +] diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st b/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st new file mode 100644 index 0000000..abad264 --- /dev/null +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st @@ -0,0 +1,33 @@ +Class { + #name : #TestDendrogramPlot, + #superclass : #TestCase, + #instVars : [ + 'dendrogram' + ], + #category : #'AI-Viz-Roassal3-HierarchicalClustering-Tests' +} + +{ #category : #running } +TestDendrogramPlot >> setUp [ + | elts engine | + super setUp. + + elts := { + AIVectorItem with: #a and: #(1 0). + AIVectorItem with: #b and: #(1 0). + AIVectorItem with: #c and: #(2 0). + AIVectorItem with: #d and: #(3 0). + AIVectorItem with: #e and: #(4 0). + AIVectorItem with: #f and: #(5 0). + AIVectorItem with: #g and: #(6 0). + AIVectorItem with: #h and: #(7 0). + AIVectorItem with: #i and: #(8 0). + AIVectorItem with: #j and: #(9 0). + AIVectorItem with: #k and: #(0 10). + AIVectorItem with: #l and: #(0 10). + }. + + engine := AIClusterEngine with: elts. + engine hierarchicalClusteringUsing: #averageLinkage. + dendrogram := engine dendrogram +] diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/package.st b/src/AI-Viz-Roassal3-HierarchicalClustering/package.st new file mode 100644 index 0000000..6cf0779 --- /dev/null +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/package.st @@ -0,0 +1 @@ +Package { #name : #'AI-Viz-Roassal3-HierarchicalClustering' } diff --git a/src/AI-Viz/AIViz.class.st b/src/AI-Viz/AIViz.class.st deleted file mode 100644 index 330bf7e..0000000 --- a/src/AI-Viz/AIViz.class.st +++ /dev/null @@ -1,21 +0,0 @@ -" -Abstract superclass grouping common behavior for Machine Learning visualizations. Methods implemented in this class should be related with plotting machine learning models results. - -Public API and Key Messages - -- message one -- message two -- (for bonus points) how to create instances. - - One simple example is simply gorgeous. - -Internal Representation and Key Implementation Points. - - - Implementation Points -" -Class { - #name : #AIViz, - #superclass : #Object, - #category : #'AI-Viz' -} diff --git a/src/AI-Viz/package.st b/src/AI-Viz/package.st deleted file mode 100644 index e8225a4..0000000 --- a/src/AI-Viz/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #'AI-Viz' } From fe808ab3a7bdb06782d634c5e41849a1e499e60e Mon Sep 17 00:00:00 2001 From: anquetil Date: Mon, 25 Dec 2023 19:52:00 +0100 Subject: [PATCH 2/3] minor adjustements --- .../AIDendrogram.extension.st | 3 ++- .../AIDendrogramNode.extension.st | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st index 291ca4f..97ca69c 100644 --- a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogram.extension.st @@ -17,7 +17,8 @@ AIDendrogram >> baselineLength [ { #category : #'*AI-Viz-Roassal3-HierarchicalClustering' } AIDendrogram >> minimalLeafLine [ - "minimal length of a line to a leaf (threshold of the node close to 0)" + "minimal length of a line to a leaf (threshold of the nodes close to 0 for which + #thresholdPosition: would return 0)" ^5 ] diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st index 4065cd8..b13a749 100644 --- a/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st +++ b/src/AI-Viz-Roassal3-HierarchicalClustering/AIDendrogramNode.extension.st @@ -98,9 +98,10 @@ AIDendrogramNode >> plotDendrogram [ thresholdScaling := self thresholdHorizontalScaling. - "first horizontal line: root" - fromPoint := (self thresholdPosition: thresholdScaling) @ (self heightFactor / 2). - toPoint := (fromPoint x + self baselineLength) @ (fromPoint y). + "first horizontal line: root + We make it half the length of a normal level" + fromPoint := (self thresholdPosition: thresholdScaling) @ 0. + toPoint := (fromPoint x + (self baselineLength / 2)) @ (fromPoint y). self plotLineIn: canvas From 8c8601b71a6f7f6ac33f621fdbd2320b04c74e13 Mon Sep 17 00:00:00 2001 From: anquetil Date: Wed, 27 Dec 2023 09:35:04 +0100 Subject: [PATCH 3/3] removing empty test --- .../TestDendrogramPlot.class.st | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st diff --git a/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st b/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st deleted file mode 100644 index abad264..0000000 --- a/src/AI-Viz-Roassal3-HierarchicalClustering/TestDendrogramPlot.class.st +++ /dev/null @@ -1,33 +0,0 @@ -Class { - #name : #TestDendrogramPlot, - #superclass : #TestCase, - #instVars : [ - 'dendrogram' - ], - #category : #'AI-Viz-Roassal3-HierarchicalClustering-Tests' -} - -{ #category : #running } -TestDendrogramPlot >> setUp [ - | elts engine | - super setUp. - - elts := { - AIVectorItem with: #a and: #(1 0). - AIVectorItem with: #b and: #(1 0). - AIVectorItem with: #c and: #(2 0). - AIVectorItem with: #d and: #(3 0). - AIVectorItem with: #e and: #(4 0). - AIVectorItem with: #f and: #(5 0). - AIVectorItem with: #g and: #(6 0). - AIVectorItem with: #h and: #(7 0). - AIVectorItem with: #i and: #(8 0). - AIVectorItem with: #j and: #(9 0). - AIVectorItem with: #k and: #(0 10). - AIVectorItem with: #l and: #(0 10). - }. - - engine := AIClusterEngine with: elts. - engine hierarchicalClusteringUsing: #averageLinkage. - dendrogram := engine dendrogram -]