├── src ├── .properties ├── AI-MetricsInspector │ ├── package.st │ ├── AISpRegressionMetricsPresenter.class.st │ ├── AISpClassificationMetricsPresenter.class.st │ ├── AISpRegressionMetricsModel.class.st │ ├── AISpClassificationMetricsModel.class.st │ ├── AISpAbstractMetricsModel.class.st │ └── AISpAbstractMetricsPresenter.class.st ├── AI-DataFrameInspector │ ├── package.st │ ├── SequenceableCollection.extension.st │ ├── RSBox.extension.st │ ├── AISpDataFrameCommand.class.st │ ├── AISpAbstractToolbarCommand.class.st │ ├── AISpDataFrameAbstractPresenter.class.st │ ├── DataSeries.extension.st │ ├── AISpDataSeriesVisualizerPresenter.class.st │ ├── AISpDataFrameInspectCommand.class.st │ ├── AISpDataFrameCopyRowValuesCommand.class.st │ ├── AISpDataFrameCopyRowSelectionsCommand.class.st │ ├── AISpDataFrameCopyRowValuesWithLabelsCommand.class.st │ ├── AISpEvaluateItCommand.class.st │ ├── AISpDataFrameEvaluatorToolbarPresenter.class.st │ ├── AISpDataFrameVisualizerPresenter.class.st │ ├── AISpDataFrameEvaluatorPresenter.class.st │ ├── AISpDataFrameBasicInfoPresenter.class.st │ ├── AISpDataFrameSummaryPresenter.class.st │ ├── AISpAbstractVisualizerPresenter.class.st │ ├── AISpScatterMatrixPresenter.class.st │ ├── DataFrame.extension.st │ ├── AISpSortTablePresenter.class.st │ ├── AISpAddRowPresenter.class.st │ ├── AISpDataFrameDescriberPresenter.class.st │ └── AISpDataFramePresenter.class.st ├── AI-DataFrameInspector-Tests │ ├── package.st │ ├── AISpDataFrameAbstractTest.class.st │ ├── AISpDataFrameDescriberPresenterTest.class.st │ ├── AISpDataFrameEvaluatorPresenterTest.class.st │ ├── AISpDataFrameBasicInfoPresenterTest.class.st │ └── AISpDataFrameSummaryPresenterTest.class.st └── BaselineOfAIDataFrameInspector │ ├── package.st │ └── BaselineOfAIDataFrameInspector.class.st ├── .project ├── resources └── imgs │ ├── histograms.png │ ├── datainspector.png │ └── scattermatrix.png ├── .gitattributes ├── .smalltalk.ston ├── .gitignore ├── .github └── workflows │ └── CI.yml ├── LICENSE └── README.md /src/.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : 'src', 3 | 'tags' : [ #pharo-ai ] 4 | } 5 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'AI-MetricsInspector' } 2 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'AI-DataFrameInspector' } 2 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'AI-DataFrameInspector-Tests' } 2 | -------------------------------------------------------------------------------- /src/BaselineOfAIDataFrameInspector/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #BaselineOfAIDataFrameInspector } 2 | -------------------------------------------------------------------------------- /resources/imgs/histograms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharo-ai/data-inspector/HEAD/resources/imgs/histograms.png -------------------------------------------------------------------------------- /resources/imgs/datainspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharo-ai/data-inspector/HEAD/resources/imgs/datainspector.png -------------------------------------------------------------------------------- /resources/imgs/scattermatrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharo-ai/data-inspector/HEAD/resources/imgs/scattermatrix.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | *.st text eol=crlf 6 | 7 | # Denote all files that are truly binary and should not be modified. 8 | *.png binary 9 | *.jpg binary 10 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/SequenceableCollection.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'SequenceableCollection' } 2 | 3 | { #category : '*AI-DataFrameInspector' } 4 | SequenceableCollection >> dataSeriesElementAt: anInteger [ 5 | "Support for DataFrame sorting when column name is clicked" 6 | 7 | ^ self at: anInteger 8 | ] 9 | -------------------------------------------------------------------------------- /.smalltalk.ston: -------------------------------------------------------------------------------- 1 | SmalltalkCISpec { 2 | #loading : [ 3 | SCIMetacelloLoadSpec { 4 | #baseline : 'AIDataFrameInspector', 5 | #directory : 'src', 6 | #platforms : [ #pharo ] 7 | } 8 | ], 9 | #testing : { 10 | #coverage : { 11 | #packages : [ 'AI-DataFrameInspector*' ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/RSBox.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'RSBox' } 2 | 3 | { #category : '*AI-DataFrameInspector' } 4 | RSBox >> asString [ 5 | 6 | ^ String streamContents: [ :stream | 7 | stream << 'Min: ' << (model key key printShowingDecimalPlaces: 2) 8 | << String cr << 'Max: ' 9 | << (model key value printShowingDecimalPlaces: 2) << String cr 10 | << 'Ocurrences: ' << model value asString ] 11 | ] 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # changes file 2 | *.changes 3 | *.chg 4 | 5 | # system image 6 | *.image 7 | *.img7 8 | *.img 9 | 10 | # Pharo Smalltalk Debug log file 11 | PharoDebug.log 12 | 13 | # Squeak Smalltalk Debug log file 14 | SqueakDebug.log 15 | 16 | # Dolphin Smalltalk source file 17 | *.sml 18 | 19 | # Dolphin Smalltalk error file 20 | *.errors 21 | 22 | # Monticello package cache 23 | /package-cache 24 | 25 | # playground cache 26 | /play-cache 27 | /play-stash 28 | 29 | # Metacello-github cache 30 | /github-cache 31 | github-*.zip 32 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpRegressionMetricsPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a subclass that changes the name of the label for the regression models 3 | " 4 | Class { 5 | #name : #AISpRegressionMetricsPresenter, 6 | #superclass : #AISpAbstractMetricsPresenter, 7 | #category : #'AI-MetricsInspector' 8 | } 9 | 10 | { #category : #initialization } 11 | AISpRegressionMetricsPresenter >> initializeRecommendedLabel [ 12 | 13 | recommendedMetricsLabel := self newLabel. 14 | recommendedMetricsLabel label: 'Recommended Metrics for a Regression Model' 15 | ] 16 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpClassificationMetricsPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a subclass that only changes the name of the label for the logistic regression model 3 | " 4 | Class { 5 | #name : #AISpClassificationMetricsPresenter, 6 | #superclass : #AISpAbstractMetricsPresenter, 7 | #category : #'AI-MetricsInspector' 8 | } 9 | 10 | { #category : #initialization } 11 | AISpClassificationMetricsPresenter >> initializeRecommendedLabel [ 12 | 13 | recommendedMetricsLabel := self newLabel. 14 | recommendedMetricsLabel label: 'Recommended Metrics for a Classification Model' 15 | ] 16 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/AISpDataFrameAbstractTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #AISpDataFrameAbstractTest, 3 | #superclass : #TestCase, 4 | #category : #'AI-DataFrameInspector-Tests' 5 | } 6 | 7 | { #category : #accessing } 8 | AISpDataFrameAbstractTest >> sampleDataFrame [ 9 | 10 | ^ DataFrame readFromCsvWithRowNames: 'country,child_mort,exports,health,imports,income,inflation,life_expec,total_fer,gdpp 11 | Afghanistan,90.2,10,7.58,44.9,1610,9.44,56.2,5.82,553 12 | Albania,16.6,28,6.55,48.6,9930,4.49,76.3,1.65,4090 13 | Algeria,27.3,38.4,4.17,31.4,12900,16.1,76.5,2.89,4460' 14 | ] 15 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/AISpDataFrameDescriberPresenterTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An AISpDataFrameBasicPresenterTest is a test class for testing the behavior of AISpDataFrameBasicPresenter 3 | " 4 | Class { 5 | #name : #AISpDataFrameDescriberPresenterTest, 6 | #superclass : #AISpDataFrameAbstractTest, 7 | #instVars : [ 8 | 'dfPresenter' 9 | ], 10 | #category : #'AI-DataFrameInspector-Tests' 11 | } 12 | 13 | { #category : #running } 14 | AISpDataFrameDescriberPresenterTest >> setUp [ 15 | 16 | super setUp. 17 | dfPresenter := AISpDataFrameDescriberPresenter on: self sampleDataFrame 18 | ] 19 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Abstract class for context commands related with DataFrame inspector. 3 | " 4 | Class { 5 | #name : 'AISpDataFrameCommand', 6 | #superclass : 'CmCommand', 7 | #category : 'AI-DataFrameInspector-Context menu commands', 8 | #package : 'AI-DataFrameInspector', 9 | #tag : 'Context menu commands' 10 | } 11 | 12 | { #category : 'accessing' } 13 | AISpDataFrameCommand >> dataFrame [ 14 | 15 | ^ self context dataFrame 16 | ] 17 | 18 | { #category : 'accessing' } 19 | AISpDataFrameCommand >> selectedItem [ 20 | 21 | ^ self context dataFramePresenter selectedItem 22 | ] 23 | 24 | { #category : 'accessing' } 25 | AISpDataFrameCommand >> selectedItems [ 26 | 27 | ^ self context selectedItems 28 | ] 29 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 5 | 6 | # Controls when the action will run. Triggers the workflow on push or pull request 7 | # events but only for the development branch 8 | on: 9 | push: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | smalltalk: [ Pharo64-9.0, Pharo64-10, Pharo64-11, Pharo64-12 ] 19 | name: ${{ matrix.smalltalk }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: hpi-swa/setup-smalltalkCI@v1 23 | with: 24 | smalltalk-version: ${{ matrix.smalltalk }} 25 | - run: smalltalkci -s ${{ matrix.smalltalk }} 26 | shell: bash 27 | timeout-minutes: 15 28 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/AISpDataFrameEvaluatorPresenterTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An AISpDataFrameEvaluatorTest is a test class for testing the behavior of AISpDataFrameEvaluator 3 | " 4 | Class { 5 | #name : #AISpDataFrameEvaluatorPresenterTest, 6 | #superclass : #AISpDataFrameAbstractTest, 7 | #instVars : [ 8 | 'dfEvaluator' 9 | ], 10 | #category : #'AI-DataFrameInspector-Tests' 11 | } 12 | 13 | { #category : #running } 14 | AISpDataFrameEvaluatorPresenterTest >> setUp [ 15 | super setUp. 16 | 17 | dfEvaluator := AISpDataFrameEvaluatorPresenter on: self sampleDataFrame 18 | ] 19 | 20 | { #category : #tests } 21 | AISpDataFrameEvaluatorPresenterTest >> testEvaluatorPresenterText [ 22 | 23 | self assert: dfEvaluator evaluatorPresenterText equals: 'self' 24 | ] 25 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpAbstractToolbarCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am an abstract class for the toolbar buttons of the evaluator in the data frame inspector. If you want to add a new button to the toolbar, just create a subclass of me and add the behaviour. 3 | " 4 | Class { 5 | #name : 'AISpAbstractToolbarCommand', 6 | #superclass : 'CmCommand', 7 | #category : 'AI-DataFrameInspector-Toolbar evaluator commands', 8 | #package : 'AI-DataFrameInspector', 9 | #tag : 'Toolbar evaluator commands' 10 | } 11 | 12 | { #category : 'initialization' } 13 | AISpAbstractToolbarCommand class >> defaultIconName [ 14 | 15 | ^ self subclassResponsibility 16 | ] 17 | 18 | { #category : 'accessing' } 19 | AISpAbstractToolbarCommand class >> order [ 20 | 21 | ^ self subclassResponsibility 22 | ] 23 | 24 | { #category : 'accessing' } 25 | AISpAbstractToolbarCommand class >> toolbarButtons [ 26 | 27 | ^ self allSubclasses sorted: #order ascending 28 | ] 29 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpRegressionMetricsModel.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a subclass that defines which are going to by my primary metrics and which are the secondary 3 | " 4 | Class { 5 | #name : #AISpRegressionMetricsModel, 6 | #superclass : #AISpAbstractMetricsModel, 7 | #category : #'AI-MetricsInspector' 8 | } 9 | 10 | { #category : #inspector } 11 | AISpRegressionMetricsModel >> inspectorTab [ 12 | 13 | 14 | 15 | ^ AISpRegressionMetricsPresenter on: self 16 | ] 17 | 18 | { #category : #inspector } 19 | AISpRegressionMetricsModel >> inspectorTabContext: aContext [ 20 | 21 | aContext withoutEvaluator 22 | ] 23 | 24 | { #category : #accessing } 25 | AISpRegressionMetricsModel >> otherMetrics [ 26 | 27 | ^ self collectMetrics: AIClassificationMetric subclasses 28 | ] 29 | 30 | { #category : #accessing } 31 | AISpRegressionMetricsModel >> primaryMetrics [ 32 | 33 | ^ self collectMetrics: AIRegressionMetric subclasses 34 | ] 35 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpClassificationMetricsModel.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a subclass that defines which are going to by my primary metrics and which are the secondary 3 | " 4 | Class { 5 | #name : #AISpClassificationMetricsModel, 6 | #superclass : #AISpAbstractMetricsModel, 7 | #category : #'AI-MetricsInspector' 8 | } 9 | 10 | { #category : #inspector } 11 | AISpClassificationMetricsModel >> inspectorTab [ 12 | 13 | 14 | 15 | ^ AISpClassificationMetricsPresenter on: self 16 | ] 17 | 18 | { #category : #inspector } 19 | AISpClassificationMetricsModel >> inspectorTabContext: aContext [ 20 | 21 | aContext withoutEvaluator 22 | ] 23 | 24 | { #category : #accessing } 25 | AISpClassificationMetricsModel >> otherMetrics [ 26 | 27 | ^ self collectMetrics: AIRegressionMetric subclasses 28 | ] 29 | 30 | { #category : #accessing } 31 | AISpClassificationMetricsModel >> primaryMetrics [ 32 | 33 | ^ self collectMetrics: AIClassificationMetric subclasses 34 | ] 35 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameAbstractPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This is an abstract superclass with behavior to set the presenter's model data frame. 3 | 4 | Internal Representation and Key Implementation Points. 5 | 6 | Instance Variables 7 | dataFrame: 8 | 9 | 10 | Implementation Points 11 | " 12 | Class { 13 | #name : 'AISpDataFrameAbstractPresenter', 14 | #superclass : 'SpPresenter', 15 | #instVars : [ 16 | 'dataFrame' 17 | ], 18 | #category : 'AI-DataFrameInspector-Sub presenters', 19 | #package : 'AI-DataFrameInspector', 20 | #tag : 'Sub presenters' 21 | } 22 | 23 | { #category : 'accessing' } 24 | AISpDataFrameAbstractPresenter >> dataFrame [ 25 | 26 | ^ dataFrame 27 | ] 28 | 29 | { #category : 'accessing' } 30 | AISpDataFrameAbstractPresenter >> dataFrame: anObject [ 31 | 32 | dataFrame := anObject 33 | ] 34 | 35 | { #category : 'accessing - model' } 36 | AISpDataFrameAbstractPresenter >> setModelBeforeInitialization: aDomainObject [ 37 | 38 | dataFrame := aDomainObject 39 | ] 40 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/DataSeries.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'DataSeries' } 2 | 3 | { #category : '*AI-DataFrameInspector' } 4 | DataSeries >> copyRowValuesToClipboard [ 5 | 6 | Clipboard 7 | clipboardText: (String streamContents: [ :s | s << self values asString ]) 8 | ] 9 | 10 | { #category : '*AI-DataFrameInspector' } 11 | DataSeries >> copyRowValuesWithLabelsToClipboard [ 12 | 13 | Clipboard clipboardText: (String streamContents: [ :s | s << self associations asString ]) 14 | ] 15 | 16 | { #category : '*AI-DataFrameInspector' } 17 | DataSeries >> dataSeriesElementAt: anInteger [ 18 | "Support for DataFrame sorting when column name is clicked" 19 | 20 | ^ self atIndex: anInteger 21 | ] 22 | 23 | { #category : '*AI-DataFrameInspector' } 24 | DataSeries >> histogram [ 25 | 26 | ^ AISpDataSeriesVisualizerPresenter openOn: self 27 | ] 28 | 29 | { #category : '*AI-DataFrameInspector' } 30 | DataSeries >> histogramBins: aNumber [ 31 | 32 | ^ (AISpDataSeriesVisualizerPresenter on: self) 33 | numberOfBins: aNumber; 34 | open 35 | ] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 pharo-ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataSeriesVisualizerPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a presenter to visualize a data series in the form of a historigram. 3 | 4 | TO use me, just send #historigram to a DataSeries. 5 | " 6 | Class { 7 | #name : 'AISpDataSeriesVisualizerPresenter', 8 | #superclass : 'AISpAbstractVisualizerPresenter', 9 | #classTraits : 'SpTModel classTrait', 10 | #category : 'AI-DataFrameInspector-Main presenters', 11 | #package : 'AI-DataFrameInspector', 12 | #tag : 'Main presenters' 13 | } 14 | 15 | { #category : 'initialization' } 16 | AISpDataSeriesVisualizerPresenter >> allColumnValuesWithColumnName [ 17 | " Private - Answer a of with numeric column values in the receiver's data frame " 18 | 19 | self model isNumerical ifFalse: [ self error: 'Can only plot numerical columns.' ]. 20 | 21 | ^ { (self model name -> (self model reject: #isNil)) } 22 | ] 23 | 24 | { #category : 'instance creation' } 25 | AISpDataSeriesVisualizerPresenter >> open [ 26 | 27 | ^ super 28 | open; 29 | withWindowDo: [ :w | 30 | w 31 | extent: 1200 @ 700; 32 | centered; 33 | title: 'DataSeries Inspector' translated; 34 | aboutText: 'About DataSeries' ] 35 | ] 36 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameInspectCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a command that opens the inspector on the DataFrame clicked row. 3 | 4 | I am converted to a spec context menu that appears when you right click in a DataFrame row. 5 | " 6 | Class { 7 | #name : 'AISpDataFrameInspectCommand', 8 | #superclass : 'AISpDataFrameCommand', 9 | #category : 'AI-DataFrameInspector-Context menu commands', 10 | #package : 'AI-DataFrameInspector', 11 | #tag : 'Context menu commands' 12 | } 13 | 14 | { #category : 'converting' } 15 | AISpDataFrameInspectCommand >> asSpecCommand [ 16 | 17 | ^ super asSpecCommand 18 | iconName: #smallInspectIt; 19 | shortcutKey: $i control unix | $i control win | $i command mac; 20 | yourself 21 | ] 22 | 23 | { #category : 'executing' } 24 | AISpDataFrameInspectCommand >> execute [ 25 | 26 | "Execute the actions that should be done by the command. 27 | This method expect that the context has been put in #context inst. var. if any context is relevant." 28 | 29 | self selectedItem inspect 30 | ] 31 | 32 | { #category : 'initialization' } 33 | AISpDataFrameInspectCommand >> initialize [ 34 | 35 | super initialize. 36 | self 37 | name: 'Inspect'; 38 | description: 'Inspect selection' 39 | ] 40 | -------------------------------------------------------------------------------- /src/BaselineOfAIDataFrameInspector/BaselineOfAIDataFrameInspector.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a baseline 3 | " 4 | Class { 5 | #name : #BaselineOfAIDataFrameInspector, 6 | #superclass : #BaselineOf, 7 | #category : #BaselineOfAIDataFrameInspector 8 | } 9 | 10 | { #category : #baselines } 11 | BaselineOfAIDataFrameInspector >> baseline: spec [ 12 | 13 | 14 | 15 | spec for: #( common ) do: [ 16 | "External dependencies" 17 | spec 18 | baseline: 'AIExternalDataFrame' 19 | with: [ spec repository: 'github://pharo-ai/external-dependencies' ]. 20 | 21 | "pharo-ai dependencies" 22 | spec 23 | baseline: 'AIMetrics' 24 | with: [ spec repository: 'github://pharo-ai/metrics' ]. 25 | 26 | "Packages" 27 | spec 28 | package: 'AI-DataFrameInspector' with: [ spec requires: #('AIExternalDataFrame' ) ]; 29 | package: 'AI-MetricsInspector' with: [ spec requires: #('AIMetrics') ]; 30 | package: 'AI-DataFrameInspector-Tests' with: [ spec requires: #( 'AI-DataFrameInspector' ) ]. 31 | 32 | "Groups" 33 | spec 34 | group: 'Core' with: #( 'AI-DataFrameInspector' 'AI-MetricsInspector' ); 35 | group: 'Tests' with: #( 'AI-DataFrameInspector' 'AI-DataFrameInspector-Tests' ); 36 | group: 'default' with: #( 'Core' 'Tests' ) ] 37 | ] 38 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameCopyRowValuesCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a command that copies the clicked row of the DataFrame. 3 | 4 | I am converted to a spec context menu that appears when you right click in a DataFrame row. 5 | " 6 | Class { 7 | #name : 'AISpDataFrameCopyRowValuesCommand', 8 | #superclass : 'AISpDataFrameCommand', 9 | #category : 'AI-DataFrameInspector-Context menu commands', 10 | #package : 'AI-DataFrameInspector', 11 | #tag : 'Context menu commands' 12 | } 13 | 14 | { #category : 'converting' } 15 | AISpDataFrameCopyRowValuesCommand >> asSpecCommand [ 16 | 17 | ^ super asSpecCommand 18 | iconName: #smallCopy; 19 | shortcutKey: $v control unix | $v control win | $v command mac; 20 | yourself 21 | ] 22 | 23 | { #category : 'executing' } 24 | AISpDataFrameCopyRowValuesCommand >> execute [ 25 | 26 | "Execute the actions that should be done by the command. 27 | This method expect that the context has been put in #context inst. var. if any context is relevant." 28 | 29 | self selectedItem copyRowValuesToClipboard 30 | ] 31 | 32 | { #category : 'initialization' } 33 | AISpDataFrameCopyRowValuesCommand >> initialize [ 34 | 35 | super initialize. 36 | self 37 | name: 'Copy row'; 38 | description: 'Copy current row to clipboard.' 39 | ] 40 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameCopyRowSelectionsCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a command that copies the selected rows of the DataFrame. 3 | 4 | I am converted to a spec context menu that appears when you right click in a DataFrame row. 5 | " 6 | Class { 7 | #name : 'AISpDataFrameCopyRowSelectionsCommand', 8 | #superclass : 'AISpDataFrameCommand', 9 | #category : 'AI-DataFrameInspector-Context menu commands', 10 | #package : 'AI-DataFrameInspector', 11 | #tag : 'Context menu commands' 12 | } 13 | 14 | { #category : 'converting' } 15 | AISpDataFrameCopyRowSelectionsCommand >> asSpecCommand [ 16 | 17 | ^ super asSpecCommand 18 | iconName: #smallCopy; 19 | shortcutKey: $s control unix | $s control win | $s command mac; 20 | yourself 21 | ] 22 | 23 | { #category : 'executing' } 24 | AISpDataFrameCopyRowSelectionsCommand >> execute [ 25 | 26 | "Execute the actions that should be done by the command. 27 | This method expect that the context has been put in #context inst. var. if any context is relevant." 28 | 29 | Clipboard clipboardText: self selectedItems storeString 30 | ] 31 | 32 | { #category : 'initialization' } 33 | AISpDataFrameCopyRowSelectionsCommand >> initialize [ 34 | 35 | super initialize. 36 | self 37 | name: 'Copy selected rows'; 38 | description: 'Copy selected rows to clipboard.' 39 | ] 40 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameCopyRowValuesWithLabelsCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a command that copies the clicked row with the names of the label of the DataFrame. 3 | 4 | I am converted to a spec context menu that appears when you right click in a DataFrame row. 5 | " 6 | Class { 7 | #name : 'AISpDataFrameCopyRowValuesWithLabelsCommand', 8 | #superclass : 'AISpDataFrameCommand', 9 | #category : 'AI-DataFrameInspector-Context menu commands', 10 | #package : 'AI-DataFrameInspector', 11 | #tag : 'Context menu commands' 12 | } 13 | 14 | { #category : 'converting' } 15 | AISpDataFrameCopyRowValuesWithLabelsCommand >> asSpecCommand [ 16 | 17 | ^ super asSpecCommand 18 | iconName: #smallCopy; 19 | shortcutKey: $c control unix | $c control win | $c command mac; 20 | yourself 21 | ] 22 | 23 | { #category : 'executing' } 24 | AISpDataFrameCopyRowValuesWithLabelsCommand >> execute [ 25 | 26 | "Execute the actions that should be done by the command. 27 | This method expect that the context has been put in #context inst. var. if any context is relevant." 28 | 29 | self selectedItem copyRowValuesWithLabelsToClipboard 30 | ] 31 | 32 | { #category : 'initialization' } 33 | AISpDataFrameCopyRowValuesWithLabelsCommand >> initialize [ 34 | 35 | super initialize. 36 | self 37 | name: 'Copy row with labels'; 38 | description: 'Copy current row values with labels to clipboard.' 39 | ] 40 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpEvaluateItCommand.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a command that can be converted into a button to be used in the toolbar of the evaluator 3 | " 4 | Class { 5 | #name : 'AISpEvaluateItCommand', 6 | #superclass : 'AISpAbstractToolbarCommand', 7 | #category : 'AI-DataFrameInspector-Toolbar evaluator commands', 8 | #package : 'AI-DataFrameInspector', 9 | #tag : 'Toolbar evaluator commands' 10 | } 11 | 12 | { #category : 'default' } 13 | AISpEvaluateItCommand class >> defaultDescription [ 14 | 15 | ^ 'Do it all and print it.' 16 | ] 17 | 18 | { #category : 'initialization' } 19 | AISpEvaluateItCommand class >> defaultIconName [ 20 | 21 | ^ #smallDoIt 22 | ] 23 | 24 | { #category : 'default' } 25 | AISpEvaluateItCommand class >> defaultName [ 26 | 27 | ^ 'Do it' 28 | ] 29 | 30 | { #category : 'accessing' } 31 | AISpEvaluateItCommand class >> order [ 32 | 33 | ^ 0 34 | ] 35 | 36 | { #category : 'converting' } 37 | AISpEvaluateItCommand >> asSpecCommand [ 38 | 39 | ^ super asSpecCommand 40 | iconName: self class defaultIconName; 41 | yourself 42 | ] 43 | 44 | { #category : 'executing' } 45 | AISpEvaluateItCommand >> execute [ 46 | 47 | | evaluatingResult | 48 | 49 | context evaluatorPresenter selectAll. 50 | 51 | evaluatingResult := context evaluatorPresenter 52 | evaluate: context evaluatorPresenter selectedTextOrLine 53 | onCompileError: [ ^ self ] 54 | onError: [ :e | e pass ]. 55 | 56 | context evaluatorPresenter insertPrintPopoverAfterCurrentSelection: evaluatingResult 57 | ] 58 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameEvaluatorToolbarPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Provides actions applied to the inspected data frame. Actions currently implemented are mostly derived from the NewTools playground toolbar. 3 | 4 | Internal Representation and Key Implementation Points. 5 | 6 | Instance Variables 7 | toolbar: 8 | 9 | 10 | Implementation Points 11 | " 12 | Class { 13 | #name : 'AISpDataFrameEvaluatorToolbarPresenter', 14 | #superclass : 'AISpDataFrameAbstractPresenter', 15 | #instVars : [ 16 | 'toolbar' 17 | ], 18 | #category : 'AI-DataFrameInspector-Sub presenters', 19 | #package : 'AI-DataFrameInspector', 20 | #tag : 'Sub presenters' 21 | } 22 | 23 | { #category : 'layout' } 24 | AISpDataFrameEvaluatorToolbarPresenter >> defaultLayout [ 25 | 26 | ^ SpBoxLayout newLeftToRight 27 | add: toolbar; 28 | yourself 29 | ] 30 | 31 | { #category : 'accessing' } 32 | AISpDataFrameEvaluatorToolbarPresenter >> evaluatorPresenter [ 33 | 34 | ^ self owner evaluatorPresenter 35 | ] 36 | 37 | { #category : 'initialization' } 38 | AISpDataFrameEvaluatorToolbarPresenter >> initializePresenters [ 39 | 40 | self initializeToolbar 41 | ] 42 | 43 | { #category : 'initialization' } 44 | AISpDataFrameEvaluatorToolbarPresenter >> initializeToolbar [ 45 | "evaluateItButton := AISpEvaluateItCommand asSpecButtonForContext: self" 46 | 47 | | group | 48 | group := CmCommandGroup forSpec. 49 | AISpAbstractToolbarCommand toolbarButtons do: [ :aCommand | group register: (aCommand forSpecContext: self) ]. 50 | 51 | toolbar := SpToolbarPresenterBuilder new 52 | visit: group; 53 | toolbarPresenter 54 | ] 55 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameVisualizerPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This Roassal >=3 presenter provides histogram for all numeric columns in the displayed data frame. 3 | 4 | # Implementation Points 5 | 6 | - Initialization happens in setModelBeforeInitialization: 7 | - They key method for rendering is `AISpDataFrameBasicViz>>plotAllHistograms` 8 | 9 | " 10 | Class { 11 | #name : 'AISpDataFrameVisualizerPresenter', 12 | #superclass : 'AISpAbstractVisualizerPresenter', 13 | #classTraits : 'SpTModel classTrait', 14 | #category : 'AI-DataFrameInspector-Main presenters', 15 | #package : 'AI-DataFrameInspector', 16 | #tag : 'Main presenters' 17 | } 18 | 19 | { #category : 'initialization' } 20 | AISpDataFrameVisualizerPresenter >> allColumnValuesWithColumnName [ 21 | " Private - Answer a of with numeric column values in the receiver's data frame " 22 | 23 | | columns | 24 | columns := self model columns. 25 | 26 | ^ self numericColumnNames collect: [ :colName | colName -> ((columns at: (self model columnNames indexOf: colName)) reject: #isNil) ] 27 | ] 28 | 29 | { #category : 'to review' } 30 | AISpDataFrameVisualizerPresenter >> createCanvas [ 31 | 32 | | canvas | 33 | canvas := RSCanvas new. 34 | canvas inspectorContext: RSEmptyContextInteraction new. 35 | self plotAllHistograms: canvas. 36 | ^ canvas 37 | ] 38 | 39 | { #category : 'accessing' } 40 | AISpDataFrameVisualizerPresenter >> dataFrame [ 41 | 42 | ^ model 43 | ] 44 | 45 | { #category : 'instance creation' } 46 | AISpDataFrameVisualizerPresenter >> open [ 47 | 48 | ^ super 49 | open; 50 | withWindowDo: [ :w | 51 | w 52 | extent: 1200 @ 700; 53 | centered; 54 | title: 'DataFrame Inspector' translated; 55 | aboutText: 'About DataFrame' ] 56 | ] 57 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameEvaluatorPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Implements a presenter with Pharo source code evaluation capabilities. The receiver in this evaluator represents the data frame currently displayed. It includes a toolbar, implemented in `AISpDataFrameEvaluatorToolbar` 3 | 4 | Internal Representation and Key Implementation Points. 5 | 6 | Instance Variables 7 | activationBlock: 8 | evalPresenter: 9 | toolbar: 10 | 11 | 12 | Implementation Points 13 | " 14 | Class { 15 | #name : 'AISpDataFrameEvaluatorPresenter', 16 | #superclass : 'AISpDataFrameAbstractPresenter', 17 | #instVars : [ 18 | 'toolbar', 19 | 'evaluatorPresenter' 20 | ], 21 | #category : 'AI-DataFrameInspector-Sub presenters', 22 | #package : 'AI-DataFrameInspector', 23 | #tag : 'Sub presenters' 24 | } 25 | 26 | { #category : 'layout' } 27 | AISpDataFrameEvaluatorPresenter >> defaultLayout [ 28 | 29 | ^ SpBoxLayout newTopToBottom 30 | add: toolbar expand: false; 31 | add: evaluatorPresenter; 32 | yourself 33 | ] 34 | 35 | { #category : 'accessing' } 36 | AISpDataFrameEvaluatorPresenter >> evaluatorPresenter [ 37 | 38 | ^ evaluatorPresenter 39 | ] 40 | 41 | { #category : 'initialization' } 42 | AISpDataFrameEvaluatorPresenter >> evaluatorPresenterText [ 43 | 44 | ^ evaluatorPresenter text 45 | ] 46 | 47 | { #category : 'initialization' } 48 | AISpDataFrameEvaluatorPresenter >> initializeEvaluator [ 49 | 50 | evaluatorPresenter := self instantiate: SpCodePresenter. 51 | evaluatorPresenter 52 | withSyntaxHighlight; 53 | withLineNumbers; 54 | beForObject: self dataFrame; 55 | text: 'self' 56 | ] 57 | 58 | { #category : 'initialization' } 59 | AISpDataFrameEvaluatorPresenter >> initializePresenters [ 60 | 61 | self initializeToolbar. 62 | self initializeEvaluator 63 | ] 64 | 65 | { #category : 'initialization' } 66 | AISpDataFrameEvaluatorPresenter >> initializeToolbar [ 67 | 68 | toolbar := self instantiate: AISpDataFrameEvaluatorToolbarPresenter on: self dataFrame 69 | ] 70 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/AISpDataFrameBasicInfoPresenterTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #AISpDataFrameBasicInfoPresenterTest, 3 | #superclass : #AISpDataFrameAbstractTest, 4 | #instVars : [ 5 | 'dfBrowser' 6 | ], 7 | #category : #'AI-DataFrameInspector-Tests' 8 | } 9 | 10 | { #category : #accessing } 11 | AISpDataFrameBasicInfoPresenterTest >> dfBrowser [ 12 | ^ dfBrowser 13 | ] 14 | 15 | { #category : #accessing } 16 | AISpDataFrameBasicInfoPresenterTest >> entity [ 17 | 18 | ^ self dfBrowser entity 19 | ] 20 | 21 | { #category : #running } 22 | AISpDataFrameBasicInfoPresenterTest >> setUp [ 23 | 24 | super setUp. 25 | dfBrowser := AISpDataFrameBasicInfoPresenter on: self sampleDataFrame 26 | ] 27 | 28 | { #category : #tests } 29 | AISpDataFrameBasicInfoPresenterTest >> testInitializePresenters [ 30 | 31 | self 32 | assertCollection: dfBrowser dataFrameBasicInfoPresenter items 33 | hasSameElements: dfBrowser summaryDescriptionsLabels 34 | ] 35 | 36 | { #category : #tests } 37 | AISpDataFrameBasicInfoPresenterTest >> testSummaryDescriptions [ 38 | 39 | self assert: 40 | (self dfBrowser summaryDescriptions isKindOf: Dictionary) 41 | ] 42 | 43 | { #category : #tests } 44 | AISpDataFrameBasicInfoPresenterTest >> testSummaryDescriptionsLabels [ 45 | 46 | self 47 | assertCollection: self dfBrowser summaryDescriptionsLabels 48 | hasSameElements: { 'Dimensions'. 'Has nil'. 'Has categorical' } 49 | ] 50 | 51 | { #category : #tests } 52 | AISpDataFrameBasicInfoPresenterTest >> testSummaryDescriptorsAt [ 53 | 54 | self 55 | assert: (self dfBrowser summaryDescriptorsAt: 'Dimensions') 56 | equals: #dimensions. 57 | self 58 | assert: (self dfBrowser summaryDescriptorsAt: 'Has nil') 59 | equals: #hasNil. 60 | self 61 | assert: (self dfBrowser summaryDescriptorsAt: 'Has categorical') 62 | equals: #hasCategorical 63 | ] 64 | 65 | { #category : #tests } 66 | AISpDataFrameBasicInfoPresenterTest >> testSummaryValueFor [ 67 | 68 | self 69 | assert: (self dfBrowser summaryValueFor: 'Dimensions') 70 | equals: '(3@9)'. 71 | self 72 | assert: (self dfBrowser summaryValueFor: 'Has nil') 73 | equals: 'false'. 74 | self 75 | assert: (self dfBrowser summaryValueFor: 'Has categorical') 76 | equals: 'false' 77 | ] 78 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpAbstractMetricsModel.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am an abtract model that calculates all the available metrics for a given actual and predicted values. 3 | 4 | I must be instantiated with the actual and predicted values that a model returns. See my class side instantiation method. 5 | " 6 | Class { 7 | #name : #AISpAbstractMetricsModel, 8 | #superclass : #Object, 9 | #instVars : [ 10 | 'actualValues', 11 | 'predictedValues' 12 | ], 13 | #category : #'AI-MetricsInspector' 14 | } 15 | 16 | { #category : #'instance creation' } 17 | AISpAbstractMetricsModel class >> predictedValues: predicted actualValues: actual [ 18 | 19 | ^ self new 20 | predictedValues: predicted; 21 | actualValues: actual; 22 | yourself 23 | ] 24 | 25 | { #category : #accessing } 26 | AISpAbstractMetricsModel >> actualValues: aCollection [ 27 | 28 | actualValues := aCollection 29 | ] 30 | 31 | { #category : #accessing } 32 | AISpAbstractMetricsModel >> allMetrics [ 33 | 34 | ^ self primaryMetrics , self otherMetrics 35 | ] 36 | 37 | { #category : #defaults } 38 | AISpAbstractMetricsModel >> collectMetrics: aCollectionOfClasses [ 39 | 40 | ^ aCollectionOfClasses collect: [ :aMetricClass | 41 | | name value | 42 | name := aMetricClass metricName. 43 | value := aMetricClass new computeForActual: actualValues predicted: predictedValues. 44 | value := self roundResult: value. 45 | name -> value ] 46 | ] 47 | 48 | { #category : #inspector } 49 | AISpAbstractMetricsModel >> inspectorTab [ 50 | ^ self subclassResponsibility 51 | ] 52 | 53 | { #category : #inspector } 54 | AISpAbstractMetricsModel >> inspectorTabContext: arg1 [ 55 | ^ self subclassResponsibility 56 | ] 57 | 58 | { #category : #accessing } 59 | AISpAbstractMetricsModel >> otherMetrics [ 60 | 61 | ^ self subclassResponsibility 62 | ] 63 | 64 | { #category : #accessing } 65 | AISpAbstractMetricsModel >> predictedValues: aCollection [ 66 | 67 | predictedValues := aCollection 68 | ] 69 | 70 | { #category : #accessing } 71 | AISpAbstractMetricsModel >> primaryMetrics [ 72 | 73 | ^ self subclassResponsibility 74 | ] 75 | 76 | { #category : #defaults } 77 | AISpAbstractMetricsModel >> roundResult: aNumber [ 78 | 79 | "We choose to show only 3 decimal places according to the European Association of Science Editors guidelines include the useful rule of thumb: “numbers should be given in (sic) 2–3 effective digits”" 80 | 81 | ^ aNumber printShowingDecimalPlaces: 3 82 | ] 83 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameBasicInfoPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Displays a table with basic information about the data frame such as : 3 | 4 | - Dimensions 5 | - Whether contains categorical columns 6 | - Whether any column contains NA/NULL/nil values. 7 | 8 | Internal Representation and Key Implementation Points. 9 | 10 | Instance Variables 11 | dataFramePresenter: 12 | 13 | 14 | Implementation Points 15 | " 16 | Class { 17 | #name : 'AISpDataFrameBasicInfoPresenter', 18 | #superclass : 'AISpDataFrameAbstractPresenter', 19 | #instVars : [ 20 | 'dataFrameBasicInfoPresenter' 21 | ], 22 | #category : 'AI-DataFrameInspector-Sub presenters', 23 | #package : 'AI-DataFrameInspector', 24 | #tag : 'Sub presenters' 25 | } 26 | 27 | { #category : 'accessing' } 28 | AISpDataFrameBasicInfoPresenter >> dataFrameBasicInfoPresenter [ 29 | 30 | ^ dataFrameBasicInfoPresenter 31 | ] 32 | 33 | { #category : 'layout' } 34 | AISpDataFrameBasicInfoPresenter >> defaultLayout [ 35 | 36 | ^ SpBoxLayout newTopToBottom 37 | add: dataFrameBasicInfoPresenter; 38 | yourself 39 | ] 40 | 41 | { #category : 'initialization' } 42 | AISpDataFrameBasicInfoPresenter >> initializePresenters [ 43 | 44 | dataFrameBasicInfoPresenter := self newTable. 45 | dataFrameBasicInfoPresenter 46 | showColumnHeaders; 47 | beMultipleSelection; 48 | items: self summaryDescriptionsLabels; 49 | addColumn: 50 | (SpStringTableColumn title: 'Property' evaluated: [ :each | each ]); 51 | addColumn: (SpStringTableColumn new 52 | title: 'Value'; 53 | evaluated: [ :aString | self summaryValueFor: aString ]; 54 | beSortable) 55 | ] 56 | 57 | { #category : 'private building' } 58 | AISpDataFrameBasicInfoPresenter >> summaryDescriptions [ 59 | 60 | ^ Dictionary new 61 | at: 'Dimensions' put: #dimensions; 62 | at: 'Has nil' put: #hasNil; 63 | at: 'Has categorical' put: #hasCategorical; 64 | yourself 65 | ] 66 | 67 | { #category : 'private building' } 68 | AISpDataFrameBasicInfoPresenter >> summaryDescriptionsLabels [ 69 | 70 | ^ self summaryDescriptions keys asSortedCollection 71 | ] 72 | 73 | { #category : 'private building' } 74 | AISpDataFrameBasicInfoPresenter >> summaryDescriptorsAt: aString [ 75 | 76 | ^ self summaryDescriptions at: aString 77 | ] 78 | 79 | { #category : 'private building' } 80 | AISpDataFrameBasicInfoPresenter >> summaryValueFor: aString [ 81 | 82 | ^ (self dataFrame perform: (self summaryDescriptorsAt: aString)) asString 83 | ] 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://github.com/pharo-ai/data-inspector/workflows/CI/badge.svg)](https://github.com/pharo-ai/data-inspector/actions/workflows/CI.yml) 2 | [![Coverage Status](https://coveralls.io/repos/github/pharo-ai/data-inspector/badge.svg?branch=master)](https://coveralls.io/github/pharo-ai/data-inspector?branch=master) 3 | [![Pharo version](https://img.shields.io/badge/Pharo-10-%23aac9ff.svg)](https://pharo.org/download) 4 | [![Pharo version](https://img.shields.io/badge/Pharo-11-%23aac9ff.svg)](https://pharo.org/download) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/pharo-ai/data-inspector/master/LICENSE) 6 | 7 | # Description 8 | 9 | A Pharo inspector extension to operate and view DataFrame multiple information in a same inspector view. Currently it displays: 10 | - General informations about the dataframe 11 | - Histograms about all numerical columns of the dataframe 12 | - A scatter matrix of the DataFrame 13 | 14 | A configurable limit is set by default to quickly visualize a DataFrame when we are in the inspector. To change the limit, for example to 50000, you can evaluate: 15 | 16 | ```smalltalk 17 | AISpDataFrameInspector maxRows: 50000 18 | ``` 19 | 20 | # Installation 21 | 22 | ```smalltalk 23 | EpMonitor disableDuring: [ 24 | Metacello new 25 | baseline: 'AIDataFrameInspector'; 26 | repository: 'github://pharo-ai/data-inspector/src'; 27 | onConflictUseIncoming; 28 | load ] 29 | ``` 30 | 31 | # If you want to depend on it 32 | 33 | ```smalltalk 34 | spec 35 | baseline: 'AIDataFrameInspector' 36 | with: [ spec repository: 'github://pharo-ai/data-inspector/src' ]. 37 | ``` 38 | 39 | # Usage 40 | 41 | Just inspect any DataFrame and select the palette with "Data Inspector": 42 | 43 | ```smalltalk 44 | EpMonitor disableDuring: [ 45 | Metacello new 46 | baseline: 'AIDatasets'; 47 | repository: 'github://pharo-ai/datasets'; 48 | load ]. 49 | 50 | AIDatasets loadIris inspect. 51 | ``` 52 | 53 | ![](resources/imgs/datainspector.png) 54 | 55 | ![](resources/imgs/histograms.png) 56 | 57 | ![](resources/imgs/scattermatrix.png) 58 | 59 | It is also possible to visualize the historigrams of a DataFrame or a DataSeries like this 60 | 61 | ```st 62 | iris := AIDatasets loadIris. 63 | 64 | iris histograms. 65 | (iris column: #'petal length (cm)') histogram. 66 | ``` 67 | 68 | You can specify the number of intervals to display like this: 69 | 70 | ```st 71 | iris histogramsBins: 50. 72 | (iris column: #'petal length (cm)') histogramBins: 50. 73 | ``` 74 | 75 | The scatter matrix can be opened like this: 76 | 77 | ```st 78 | iris scatterMatrix 79 | ``` 80 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector-Tests/AISpDataFrameSummaryPresenterTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An AISpDataFrameSummaryPresenterTest is a test class for testing the behavior of AISpDataFrameSummaryPresenter 3 | " 4 | Class { 5 | #name : #AISpDataFrameSummaryPresenterTest, 6 | #superclass : #AISpDataFrameAbstractTest, 7 | #instVars : [ 8 | 'dfSummaryPresenter' 9 | ], 10 | #category : #'AI-DataFrameInspector-Tests' 11 | } 12 | 13 | { #category : #accessing } 14 | AISpDataFrameSummaryPresenterTest >> dfSummaryPresenter [ 15 | 16 | ^ dfSummaryPresenter 17 | ] 18 | 19 | { #category : #running } 20 | AISpDataFrameSummaryPresenterTest >> setUp [ 21 | "Hooks that subclasses may override to define the fixture of test." 22 | 23 | super setUp. 24 | 25 | dfSummaryPresenter := AISpDataFrameSummaryPresenter on: self sampleDataFrame 26 | ] 27 | 28 | { #category : #test } 29 | AISpDataFrameSummaryPresenterTest >> testInitializePresenters [ 30 | 31 | self 32 | assert: 33 | self dfSummaryPresenter dataFrameSummaryPresenter columns size 34 | equals: self dfSummaryPresenter dataFrame columns size + 1 35 | ] 36 | 37 | { #category : #test } 38 | AISpDataFrameSummaryPresenterTest >> testRoundResult [ 39 | 40 | self assert: (self dfSummaryPresenter roundResult: 56.8787887) equals: '56.88'. 41 | self deny: (self dfSummaryPresenter roundResult: 56.8787887) equals: '56.87' 42 | ] 43 | 44 | { #category : #test } 45 | AISpDataFrameSummaryPresenterTest >> testSummaryStats [ 46 | 47 | self assert: (self dfSummaryPresenter summaryStats isKindOf: Dictionary) 48 | ] 49 | 50 | { #category : #test } 51 | AISpDataFrameSummaryPresenterTest >> testSummaryStatsAt [ 52 | 53 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Average') equals: #average. 54 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Mode') equals: #mode. 55 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Median') equals: #median. 56 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Variance') equals: #variance. 57 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Standard deviation') equals: #stdev. 58 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Minimum') equals: #min. 59 | self assert: (self dfSummaryPresenter summaryStatsAt: 'Maximum') equals: #max. 60 | self assert: (self dfSummaryPresenter summaryStatsAt: '1st Quartile') equals: #firstQuartile 61 | ] 62 | 63 | { #category : #test } 64 | AISpDataFrameSummaryPresenterTest >> testSummaryStatsLabels [ 65 | 66 | self assert: (self dfSummaryPresenter summaryStatsLabels isKindOf: Collection). 67 | self denyEmpty: self dfSummaryPresenter summaryStatsLabels 68 | ] 69 | 70 | { #category : #test } 71 | AISpDataFrameSummaryPresenterTest >> testSummaryStatsValueForIn [ 72 | 73 | self assert: (self dfSummaryPresenter summaryStatsValueFor: 'Minimum' in: 'child_mort') equals: '16.60'. 74 | self assert: (self dfSummaryPresenter summaryStatsValueFor: 'Maximum' in: 'exports') equals: '38.40'. 75 | self assert: (self dfSummaryPresenter summaryStatsValueFor: 'Average' in: 'gdpp') equals: '3034.33' 76 | ] 77 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameSummaryPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a presenter that shows the summary of the statistics of a given DataFrame. 3 | " 4 | Class { 5 | #name : 'AISpDataFrameSummaryPresenter', 6 | #superclass : 'AISpDataFrameAbstractPresenter', 7 | #instVars : [ 8 | 'dataFrameSummaryPresenter' 9 | ], 10 | #category : 'AI-DataFrameInspector-Sub presenters', 11 | #package : 'AI-DataFrameInspector', 12 | #tag : 'Sub presenters' 13 | } 14 | 15 | { #category : 'accessing' } 16 | AISpDataFrameSummaryPresenter >> dataFrameSummaryPresenter [ 17 | 18 | ^ dataFrameSummaryPresenter 19 | ] 20 | 21 | { #category : 'layout' } 22 | AISpDataFrameSummaryPresenter >> defaultLayout [ 23 | 24 | ^ SpBoxLayout newTopToBottom 25 | add: dataFrameSummaryPresenter; 26 | yourself 27 | ] 28 | 29 | { #category : 'initialization' } 30 | AISpDataFrameSummaryPresenter >> initializePresenters [ 31 | 32 | dataFrameSummaryPresenter := self newTable. 33 | dataFrameSummaryPresenter 34 | showColumnHeaders; 35 | beMultipleSelection; 36 | items: self summaryStatsLabels; 37 | addColumn: (SpStringTableColumn title: 'Statistic' evaluated: [ :each | each ]). 38 | 39 | self dataFrame numericColumnNames do: [ :columnName | 40 | dataFrameSummaryPresenter addColumn: (SpStringTableColumn new 41 | title: columnName; 42 | evaluated: [ :aString | self summaryStatsValueFor: aString in: columnName ]; 43 | beSortable) ] 44 | ] 45 | 46 | { #category : 'defaults' } 47 | AISpDataFrameSummaryPresenter >> performCalculation: aString forDataSeries: colDataSeries [ 48 | 49 | | calculation | 50 | calculation := colDataSeries perform: (self summaryStatsAt: aString). 51 | 52 | ^ self roundResult: calculation 53 | ] 54 | 55 | { #category : 'defaults' } 56 | AISpDataFrameSummaryPresenter >> roundResult: aNumber [ 57 | 58 | "We choose to show only 3 decimal places according to the European Association of Science Editors guidelines include the useful rule of thumb: “numbers should be given in (sic) 2–3 effective digits”" 59 | 60 | ^ aNumber printShowingDecimalPlaces: 2 61 | ] 62 | 63 | { #category : 'calculating' } 64 | AISpDataFrameSummaryPresenter >> summaryStats [ 65 | 66 | ^ Dictionary new 67 | at: 'Average' put: #average; 68 | at: 'Mode' put: #mode; 69 | at: 'Median' put: #median; 70 | at: 'Variance' put: #variance; 71 | at: 'Standard deviation' put: #stdev; 72 | at: 'Minimum' put: #min; 73 | at: 'Maximum' put: #max; 74 | at: '1st Quartile' put: #firstQuartile; 75 | at: '3rd Quartile' put: #thirdQuartile; 76 | yourself 77 | ] 78 | 79 | { #category : 'calculating' } 80 | AISpDataFrameSummaryPresenter >> summaryStatsAt: aString [ 81 | 82 | ^ self summaryStats at: aString 83 | ] 84 | 85 | { #category : 'calculating' } 86 | AISpDataFrameSummaryPresenter >> summaryStatsLabels [ 87 | 88 | ^ self summaryStats keys 89 | ] 90 | 91 | { #category : 'calculating' } 92 | AISpDataFrameSummaryPresenter >> summaryStatsValueFor: aString in: col [ 93 | 94 | ^ self performCalculation: aString forDataSeries: (self dataFrame column: col) 95 | ] 96 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpAbstractVisualizerPresenter.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'AISpAbstractVisualizerPresenter', 3 | #superclass : 'SpRoassalPresenter', 4 | #traits : 'SpTModel', 5 | #classTraits : 'SpTModel classTrait', 6 | #instVars : [ 7 | 'numberOfBins' 8 | ], 9 | #category : 'AI-DataFrameInspector-Main presenters', 10 | #package : 'AI-DataFrameInspector', 11 | #tag : 'Main presenters' 12 | } 13 | 14 | { #category : 'instance creation' } 15 | AISpAbstractVisualizerPresenter class >> openOn: aModel [ 16 | 17 | ^ (self on: aModel) open 18 | ] 19 | 20 | { #category : 'initialization' } 21 | AISpAbstractVisualizerPresenter >> allColumnValuesWithColumnName [ 22 | " Private - Answer a of with numeric column values in the receiver's data frame " 23 | 24 | ^ self subclassResponsibility 25 | ] 26 | 27 | { #category : 'initialization' } 28 | AISpAbstractVisualizerPresenter >> defaultNumberOfBins [ 29 | 30 | ^ 50 31 | ] 32 | 33 | { #category : 'initialization' } 34 | AISpAbstractVisualizerPresenter >> initializePresenters [ 35 | 36 | self canvas: RSCanvas new. 37 | self plotAllHistograms: self canvas. 38 | self refresh 39 | ] 40 | 41 | { #category : 'initialization' } 42 | AISpAbstractVisualizerPresenter >> numberOfBins [ 43 | 44 | ^ numberOfBins ifNil: [ numberOfBins := self defaultNumberOfBins ] 45 | ] 46 | 47 | { #category : 'initialization' } 48 | AISpAbstractVisualizerPresenter >> numberOfBins: aNumber [ 49 | 50 | numberOfBins := aNumber. 51 | self initializePresenters 52 | ] 53 | 54 | { #category : 'initialization' } 55 | AISpAbstractVisualizerPresenter >> numericColumnNames [ 56 | 57 | ^ self model numericColumnNames 58 | ] 59 | 60 | { #category : 'initialization' } 61 | AISpAbstractVisualizerPresenter >> plotAllHistograms: aCanvas [ 62 | 63 | | shapes | 64 | shapes := self allColumnValuesWithColumnName collect: [ :columnAssoc | 65 | self plotHistogram: columnAssoc ]. 66 | 67 | aCanvas 68 | addAll: shapes; 69 | addInteraction: RSCanvasController new. 70 | 71 | aCanvas 72 | when: RSExtentChangedEvent 73 | do: [ 74 | RSFlowLayout new 75 | gapSize: 10; 76 | maxWidth: aCanvas extent x; 77 | on: shapes. 78 | aCanvas camera zoomToFit: aCanvas extent * 0.98. 79 | 80 | aCanvas signalUpdate ] 81 | for: self 82 | ] 83 | 84 | { #category : 'initialization' } 85 | AISpAbstractVisualizerPresenter >> plotHistogram: columnAssoc [ 86 | 87 | | container c columnValues plot vertical horizontal | 88 | columnValues := columnAssoc value. 89 | c := RSChart new. 90 | container := RSGroup new. 91 | 92 | plot := [ 93 | RSHistogramPlot new 94 | x: columnValues; 95 | numberOfBins: self numberOfBins ] 96 | on: DomainError 97 | do: [ :ex | RSLinePlot new y: columnValues ]. 98 | 99 | vertical := RSVerticalTick new. 100 | vertical fontSize: 11. 101 | horizontal := RSHorizontalTick new. 102 | horizontal 103 | fontSize: 11; 104 | useDiagonalLabel. 105 | 106 | c container: container. 107 | c addPlot: plot. 108 | c addDecoration: vertical. 109 | c addDecoration: horizontal. 110 | c title: columnAssoc key. 111 | c build. 112 | 113 | plot createdShapes @ (RSPopup new 114 | model: #yourself; 115 | yourself). 116 | 117 | ^ container asShape 118 | ] 119 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpScatterMatrixPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a presenter rendering a scatter matrix visualization. 3 | 4 | The scatter matrix visualization is helpful to find correlations in a dataset. 5 | It will present a scatter plot of each numerical columns of a data frame against each others. 6 | This help to manually find some correlations since the human been is good at finding patterns. 7 | " 8 | Class { 9 | #name : 'AISpScatterMatrixPresenter', 10 | #superclass : 'SpRoassalPresenter', 11 | #traits : 'SpTModel', 12 | #classTraits : 'SpTModel classTrait', 13 | #category : 'AI-DataFrameInspector-Main presenters', 14 | #package : 'AI-DataFrameInspector', 15 | #tag : 'Main presenters' 16 | } 17 | 18 | { #category : 'instance creation' } 19 | AISpScatterMatrixPresenter class >> openOn: aDataFrame [ 20 | 21 | ^ (self on: aDataFrame) open 22 | ] 23 | 24 | { #category : 'to review' } 25 | AISpScatterMatrixPresenter >> createCanvas [ 26 | 27 | | canvas | 28 | canvas := RSCanvas new. 29 | canvas inspectorContext: RSEmptyContextInteraction new. 30 | self initializeScatterMatrix: canvas. 31 | ^ canvas 32 | ] 33 | 34 | { #category : 'initialization' } 35 | AISpScatterMatrixPresenter >> initializePresenters [ 36 | 37 | self initializeScatterMatrix: self canvas. 38 | self refresh 39 | ] 40 | 41 | { #category : 'initialization' } 42 | AISpScatterMatrixPresenter >> initializeScatterMatrix: aCanvas [ 43 | 44 | | shapes namedColumns | 45 | shapes := OrderedCollection new. 46 | namedColumns := (model numericColumnNames collect: [ :e | 47 | e -> (model column: e) ]) asOrderedDictionary. 48 | namedColumns keys doWithIndex: [ :column1 :index1 | 49 | namedColumns keys doWithIndex: [ :column2 :index2 | 50 | | container c plot | 51 | c := RSChart new. 52 | container := RSGroup new. 53 | 54 | plot := column1 = column2 55 | ifTrue: [ 56 | RSHistogramPlot new 57 | x: (namedColumns at: column1); 58 | numberOfBins: 50 ] 59 | ifFalse: [ 60 | RSScatterPlot new 61 | x: (namedColumns at: column2) values 62 | y: (namedColumns at: column1) values ]. 63 | 64 | 65 | c container: container. 66 | c addPlot: plot. 67 | 68 | index2 = 1 ifTrue: [ 69 | c 70 | addDecoration: RSVerticalTick new; 71 | addDecoration: (RSYLabelDecoration new title: column1) ]. 72 | index1 = namedColumns size ifTrue: [ 73 | c 74 | addDecoration: (RSHorizontalTick new 75 | useVerticalLabel; 76 | yourself); 77 | addDecoration: (RSXLabelDecoration new title: column2) ]. 78 | 79 | c build. 80 | 81 | plot createdShapes @ (RSPopup new 82 | model: #yourself; 83 | yourself). 84 | 85 | shapes add: container asShape ] ]. 86 | 87 | 88 | aCanvas 89 | addAll: shapes; 90 | addInteraction: RSCanvasController new. 91 | 92 | aCanvas 93 | when: RSExtentChangedEvent 94 | do: [ 95 | RSGridLayout on: shapes. 96 | aCanvas camera zoomToFit: aCanvas extent * 0.98. 97 | 98 | aCanvas signalUpdate ] 99 | for: self 100 | ] 101 | 102 | { #category : 'instance creation' } 103 | AISpScatterMatrixPresenter >> open [ 104 | 105 | ^ super 106 | open; 107 | withWindowDo: [ :w | 108 | w 109 | extent: 1200 @ 700; 110 | centered; 111 | title: 'DataFrame Scatter Matrix' translated; 112 | aboutText: 'About DataFrame' ] 113 | ] 114 | -------------------------------------------------------------------------------- /src/AI-MetricsInspector/AISpAbstractMetricsPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I am a presenter that show in two tables the metrics that can be applied for a regression model or a classification model. For the moment, I will work with linear regression and logistic regression models. 3 | " 4 | Class { 5 | #name : #AISpAbstractMetricsPresenter, 6 | #superclass : #SpPresenter, 7 | #traits : 'SpTModel', 8 | #classTraits : 'SpTModel classTrait', 9 | #instVars : [ 10 | 'primaryMetricsTable', 11 | 'otherMetricsTable', 12 | 'showOtherMetricsButton', 13 | 'isOtherMetricsTableShown', 14 | 'recommendedMetricsLabel', 15 | 'otherMetricsLabel' 16 | ], 17 | #category : #'AI-MetricsInspector' 18 | } 19 | 20 | { #category : #layout } 21 | AISpAbstractMetricsPresenter >> defaultLayout [ 22 | 23 | ^ SpBoxLayout newTopToBottom 24 | spacing: 5; 25 | add: recommendedMetricsLabel expand: false; 26 | add: primaryMetricsTable; 27 | add: (SpBoxLayout newLeftToRight 28 | add: otherMetricsLabel expand: false; 29 | add: showOtherMetricsButton withConstraints: [ :constraints | 30 | constraints 31 | height: 18; 32 | width: 18 ]; 33 | yourself) 34 | expand: false; 35 | yourself 36 | ] 37 | 38 | { #category : #initialization } 39 | AISpAbstractMetricsPresenter >> initialize [ 40 | 41 | super initialize. 42 | isOtherMetricsTableShown := false 43 | ] 44 | 45 | { #category : #initialization } 46 | AISpAbstractMetricsPresenter >> initializeOtherMetricsLabel [ 47 | 48 | otherMetricsLabel := self newLabel. 49 | otherMetricsLabel label: 'Other Metrics' 50 | ] 51 | 52 | { #category : #initialization } 53 | AISpAbstractMetricsPresenter >> initializeOtherMetricsTable [ 54 | 55 | otherMetricsTable := self newTable. 56 | otherMetricsTable 57 | items: model otherMetrics; 58 | addColumn: (SpStringTableColumn title: 'Metric' evaluated: [ :each | each key ]); 59 | addColumn: (SpStringTableColumn title: 'Value' evaluated: [ :each | each value ]) 60 | ] 61 | 62 | { #category : #initialization } 63 | AISpAbstractMetricsPresenter >> initializePresenters [ 64 | 65 | self initializePrimaryMetricsTable. 66 | self initializeShowOtherMetricsButton. 67 | self initializeOtherMetricsTable. 68 | 69 | self initializeOtherMetricsLabel. 70 | self initializeRecommendedLabel 71 | ] 72 | 73 | { #category : #initialization } 74 | AISpAbstractMetricsPresenter >> initializePrimaryMetricsTable [ 75 | 76 | primaryMetricsTable := self newTable. 77 | primaryMetricsTable 78 | items: model primaryMetrics; 79 | addColumn: (SpStringTableColumn title: 'Metric' evaluated: [ :each | each key ]); 80 | addColumn: (SpStringTableColumn title: 'Value' evaluated: [ :each | each value ]) 81 | ] 82 | 83 | { #category : #initialization } 84 | AISpAbstractMetricsPresenter >> initializeRecommendedLabel [ 85 | 86 | self subclassResponsibility 87 | ] 88 | 89 | { #category : #initialization } 90 | AISpAbstractMetricsPresenter >> initializeShowOtherMetricsButton [ 91 | 92 | showOtherMetricsButton := self newButton. 93 | showOtherMetricsButton 94 | action: [ self toggleOtherMetricsTable ]; 95 | icon: (self iconNamed: #arrowDown) 96 | ] 97 | 98 | { #category : #actions } 99 | AISpAbstractMetricsPresenter >> toggleOtherMetricsTable [ 100 | 101 | isOtherMetricsTableShown 102 | ifTrue: [ 103 | showOtherMetricsButton iconName: #arrowDown. 104 | self layout remove: otherMetricsTable ] 105 | ifFalse: [ 106 | showOtherMetricsButton iconName: #arrowUp. 107 | self layout add: otherMetricsTable ]. 108 | 109 | isOtherMetricsTableShown := isOtherMetricsTableShown not 110 | ] 111 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/DataFrame.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'DataFrame' } 2 | 3 | { #category : '*AI-DataFrameInspector' } 4 | DataFrame >> addScatterPlotShapeToChart: aRSChart [ 5 | " Assume a receiver with two first columns as x and y coordinates. 6 | Requires Roassal 3. Answer a Roassal scatter plot shape with points color configured to aColor " 7 | 8 | | scatterPlot x y | 9 | 10 | x := (self columnAt: 1) asOrderedCollection. 11 | y := (self columnAt: 2) asOrderedCollection. 12 | scatterPlot := RSScatterPlot new x: x y: y. 13 | aRSChart add: scatterPlot. 14 | ^ scatterPlot 15 | ] 16 | 17 | { #category : '*AI-DataFrameInspector' } 18 | DataFrame >> at: anIndex ifAbsent: aBlock [ 19 | 20 | ^ self row: anIndex ifAbsent: aBlock 21 | ] 22 | 23 | { #category : '*AI-DataFrameInspector' } 24 | DataFrame >> dataDescriber [ 25 | 26 | 27 | ^ AISpDataFrameDescriberPresenter on: ( 28 | self numberOfRows > self evaluatorRowsLimit 29 | ifFalse: [ self ] 30 | ifTrue: [ self head: AISpDataFrameDescriberPresenter maxRows ]) 31 | ] 32 | 33 | { #category : '*AI-DataFrameInspector' } 34 | DataFrame >> dataDescriberContext: aContext [ 35 | 36 | aContext withoutEvaluator 37 | ] 38 | 39 | { #category : '*AI-DataFrameInspector' } 40 | DataFrame >> dataFrameToolBar [ 41 | 42 | 43 | ^ AISpDataFramePresenter on: self 44 | ] 45 | 46 | { #category : '*AI-DataFrameInspector' } 47 | DataFrame >> evaluatorRowsLimit [ 48 | 49 | " Private - The maximum limit to consider a DataFrame is big enough for a visualization " 50 | 51 | ^ 5000 52 | ] 53 | 54 | { #category : '*AI-DataFrameInspector' } 55 | DataFrame >> hasCategorical [ 56 | 57 | " Answer if the receiver has at least one categorical (not numerical) value " 58 | 59 | ^ self anySatisfy: [ :each | each isCategorical ] 60 | ] 61 | 62 | { #category : '*AI-DataFrameInspector' } 63 | DataFrame >> hasNil [ 64 | 65 | " Answer if the receiver has at least one nil value " 66 | 67 | ^ self anySatisfy: [ :each | each hasNil ] 68 | ] 69 | 70 | { #category : '*AI-DataFrameInspector' } 71 | DataFrame >> histograms [ 72 | ^ AISpDataFrameVisualizerPresenter openOn: self 73 | ] 74 | 75 | { #category : '*AI-DataFrameInspector' } 76 | DataFrame >> histogramsBins: aNumber [ 77 | 78 | ^ (AISpDataFrameVisualizerPresenter on: self) 79 | numberOfBins: aNumber; 80 | open 81 | ] 82 | 83 | { #category : '*AI-DataFrameInspector' } 84 | DataFrame >> histogramsInspector [ 85 | 86 | 87 | | roassalPresenter | 88 | 89 | roassalPresenter := AISpDataFrameVisualizerPresenter on: 90 | (self numberOfRows > self evaluatorRowsLimit 91 | ifFalse: [ self ] 92 | ifTrue: [ self head: AISpDataFrameDescriberPresenter maxRows ]). 93 | 94 | ^ SpRoassal3InspectorPresenter new 95 | canvas: roassalPresenter createCanvas; 96 | yourself 97 | ] 98 | 99 | { #category : '*AI-DataFrameInspector' } 100 | DataFrame >> histogramsInspectorContext: aContext [ 101 | 102 | aContext withoutEvaluator 103 | ] 104 | 105 | { #category : '*AI-DataFrameInspector' } 106 | DataFrame >> numericColumnNames [ 107 | 108 | | r | 109 | r := OrderedCollection new. 110 | self columnNames do: [ :column | ((self dataTypes at: column) includesBehavior: Number) ifTrue: [ r add: column ] ]. 111 | ^ r asArray 112 | ] 113 | 114 | { #category : '*AI-DataFrameInspector' } 115 | DataFrame >> openScatterPlotWithTitle: aString forColumn: columnName [ 116 | " Assume a receiver with two first columns as x and y coordinates. 117 | Requires Roassal 3. Plot and open a new window" 118 | 119 | | roassalChart scatterPlotShapes uniqueColumns | 120 | roassalChart := RSChart new 121 | title: aString; 122 | addDecoration: (RSHorizontalTick new doNotUseNiceLabel asFloat: 3); 123 | addDecoration: RSVerticalTick new; 124 | yourself. 125 | uniqueColumns := (self column: columnName) asSet. 126 | scatterPlotShapes := uniqueColumns collect: [ : type | 127 | (self select: [ : e | e values includes: type ]) 128 | addScatterPlotShapeToChart: roassalChart ]. 129 | 130 | scatterPlotShapes asOrderedCollection 131 | with: ((1 to: uniqueColumns size) collect: [ : c | Color random ]) 132 | do: [ : scatterPlotShape : scatterPlotDotColor | 133 | scatterPlotShape color: scatterPlotDotColor ]. 134 | roassalChart padding: 10. 135 | 136 | roassalChart open. 137 | ] 138 | 139 | { #category : '*AI-DataFrameInspector' } 140 | DataFrame >> scatterMatrix [ 141 | 142 | ^ AISpScatterMatrixPresenter openOn: self 143 | ] 144 | 145 | { #category : '*AI-DataFrameInspector' } 146 | DataFrame >> scatterMatrixInspector [ 147 | 148 | 149 | | roassalPresenter | 150 | 151 | roassalPresenter := AISpScatterMatrixPresenter on: 152 | (self numberOfRows > self evaluatorRowsLimit 153 | ifFalse: [ self ] 154 | ifTrue: [ self head: AISpDataFrameDescriberPresenter maxRows ]). 155 | 156 | ^ SpRoassal3InspectorPresenter new 157 | canvas: roassalPresenter createCanvas; 158 | yourself 159 | ] 160 | 161 | { #category : '*AI-DataFrameInspector' } 162 | DataFrame >> scatterMatrixInspectorContext: aContext [ 163 | 164 | aContext withoutEvaluator 165 | ] 166 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpSortTablePresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Class for handling the sort window. 3 | It provides a user friendly way to sort DataFrames. 4 | The sort window has three different fields: 5 | - **Column Name** : A drop down list from which users can select the column name by which the DataFrame should be sorted. 6 | - **Sort Type** : A drop down list having three options 'ascending', 'descending' and 'other', the user can define the way the DataFrame should be sorted by choosing one of these options. 7 | - **Sorting Block** : This field is disabled if the user chooses 'ascending' or 'descending'. The user can write a custom sorting block in this field after selecting the 'other' option from the 'Sort Type' drop down list. 8 | 9 | **Chain sorting** is also possible using this window, simply click the 'Add' button and choose the next column name by which the DataFrame should be sorted if some of the values of the previous selected column are same. 10 | 11 | Click on the **sort** button to sort the DataFrame based on the preferences selected or choose the **cancel** button to discard these preferences. 12 | " 13 | Class { 14 | #name : 'AISpSortTablePresenter', 15 | #superclass : 'AISpDataFramePresenter', 16 | #instVars : [ 17 | 'sortType', 18 | 'columnName', 19 | 'block', 20 | 'addButton', 21 | 'tablePresenter', 22 | 'applyButton', 23 | 'cancelButton', 24 | 'dataRows', 25 | 'newRow' 26 | ], 27 | #category : 'AI-DataFrameInspector-sub presenters', 28 | #package : 'AI-DataFrameInspector', 29 | #tag : 'sub presenters' 30 | } 31 | 32 | { #category : 'instance creation' } 33 | AISpSortTablePresenter class >> openOn: aDataFrame [ 34 | 35 | ^ (self on: aDataFrame) open 36 | ] 37 | 38 | { #category : 'adding' } 39 | AISpSortTablePresenter >> addButtonAction [ 40 | 41 | newRow := Dictionary new. 42 | newRow at: 'columnName' put: columnName selectedItem. 43 | newRow at: 'sortType' put: sortType selectedItem. 44 | newRow at: 'sortBlock' put: block text. 45 | dataRows add: newRow. 46 | tablePresenter items: dataRows 47 | ] 48 | 49 | { #category : 'action' } 50 | AISpSortTablePresenter >> applyButtonAction [ 51 | 52 | | currentRow | 53 | currentRow := Dictionary new. 54 | (1 to: dataRows size) reverseDo: [ :rowNumber | 55 | currentRow := dataRows at: rowNumber. 56 | dataFrame 57 | sortBy: (currentRow at: 'columnName') 58 | using: (BlockClosure readFromString: (currentRow at: 'sortBlock')) ] 59 | ] 60 | 61 | { #category : 'action' } 62 | AISpSortTablePresenter >> changeSortType [ 63 | 64 | sortType whenSelectionChangedDo: [ :newSortType | 65 | newSortType selectedIndex = 1 ifTrue: [ 66 | block text: '[ :a :b | a <= b ]'. 67 | block beNotEditable ]. 68 | newSortType selectedIndex = 2 ifTrue: [ 69 | block text: '[ :a :b | a >= b ]'. 70 | block beNotEditable ]. 71 | newSortType selectedIndex = 3 ifTrue: [ 72 | block text: ''. 73 | block beEditable ] ] 74 | ] 75 | 76 | { #category : 'initialization' } 77 | AISpSortTablePresenter >> connectPresenters [ 78 | 79 | addButton action: [ self addButtonAction ]. 80 | applyButton action: [ 81 | self applyButtonAction. 82 | self window close ]. 83 | cancelButton action: [ self window close ]. 84 | self changeSortType 85 | ] 86 | 87 | { #category : 'layout' } 88 | AISpSortTablePresenter >> defaultLayout [ 89 | 90 | | topLayout bottomLayout | 91 | topLayout := SpBoxLayout newLeftToRight 92 | spacing: 5; 93 | add: columnName width: 150; 94 | add: sortType width: 100; 95 | add: block width: 200; 96 | yourself. 97 | 98 | bottomLayout := SpBoxLayout newLeftToRight 99 | spacing: 5; 100 | add: applyButton width: 65; 101 | add: cancelButton width: 65; 102 | yourself. 103 | 104 | ^ SpBoxLayout newTopToBottom 105 | spacing: 5; 106 | add: topLayout height: 20; 107 | add: tablePresenter; 108 | add: bottomLayout height: 30; 109 | yourself 110 | ] 111 | 112 | { #category : 'initialization' } 113 | AISpSortTablePresenter >> initializeAddButton [ 114 | 115 | addButton := self newToolbarButton 116 | label: 'Add'; 117 | icon: (self iconNamed: #smallAdd); 118 | yourself 119 | ] 120 | 121 | { #category : 'initialization' } 122 | AISpSortTablePresenter >> initializeApplyCancelButtons [ 123 | 124 | applyButton := self newButton 125 | label: 'sort'; 126 | icon: (self iconNamed: #smallOk). 127 | cancelButton := self newButton 128 | label: 'cancel'; 129 | icon: (self iconNamed: #smallCancel) 130 | ] 131 | 132 | { #category : 'initialization' } 133 | AISpSortTablePresenter >> initializePresenters [ 134 | 135 | dataRows := OrderedCollection new. 136 | self initializeTablePresenter. 137 | self initializeAddButton. 138 | self initializeSelectionFields. 139 | self initializeApplyCancelButtons 140 | ] 141 | 142 | { #category : 'initialization' } 143 | AISpSortTablePresenter >> initializeSelectionFields [ 144 | 145 | columnName := self newDropList items: dataFrame columnNames. 146 | block := self newTextInput 147 | text: '[ :a :b | a <= b ]'; 148 | placeholder: '[ :a :b | a <= b ]'; 149 | beNotEditable. 150 | sortType := self newDropList items: #( ascending descending 151 | other ) 152 | ] 153 | 154 | { #category : 'initialization' } 155 | AISpSortTablePresenter >> initializeTablePresenter [ 156 | 157 | tablePresenter := self newTable 158 | addColumn: (SpStringTableColumn 159 | title: 'Column Name' 160 | evaluated: [ :row | row at: 'columnName' ]); 161 | addColumn: (SpStringTableColumn 162 | title: 'Sort Type' 163 | evaluated: [ :row | row at: 'sortType' ]); 164 | addColumn: (SpStringTableColumn 165 | title: 'Sort Block' 166 | evaluated: [ :row | row at: 'sortBlock' ]); 167 | yourself 168 | ] 169 | 170 | { #category : 'initialization' } 171 | AISpSortTablePresenter >> initializeWindow: aWindowPresenter [ 172 | 173 | aWindowPresenter 174 | initialExtent: 500 @ 300; 175 | title: 'Sort Window'; 176 | whenClosedDo: [ 177 | dataRows := OrderedCollection new. 178 | tablePresenter items: dataRows ]; 179 | toolbar: (self newToolbar 180 | add: addButton; 181 | yourself) 182 | ] 183 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpAddRowPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Class for handling the window which allows users to add rows. 3 | It provides a user friendly way to add rows to DataFrames. 4 | This window has these fields: 5 | - **Position** : The position at which the new row should be added. 6 | - **Row Name** : The row name of the new row to be added. 7 | - **New Row** : The user can type the new row by adding the necessary items under each column. 8 | - **Add button** : Adds the row to the DataFrame. 9 | - **Reset button** : Clears the items entered under each column of the new row. 10 | After entering each item under the specific column, the 'enter' key must be hit. 11 | " 12 | Class { 13 | #name : 'AISpAddRowPresenter', 14 | #superclass : 'AISpDataFramePresenter', 15 | #instVars : [ 16 | 'sortType', 17 | 'columnName', 18 | 'emptyRow', 19 | 'resetButton', 20 | 'newRowName', 21 | 'newRow', 22 | 'oneRowPresenter', 23 | 'block', 24 | 'rowInputTable', 25 | 'newRowInputTable', 26 | 'positionInput', 27 | 'addButton', 28 | 'tablePresenter', 29 | 'applyButton', 30 | 'cancelButton', 31 | 'dataRows' 32 | ], 33 | #category : 'AI-DataFrameInspector-sub presenters', 34 | #package : 'AI-DataFrameInspector', 35 | #tag : 'sub presenters' 36 | } 37 | 38 | { #category : 'instance creation' } 39 | AISpAddRowPresenter class >> openOn: aDataFrame [ 40 | 41 | ^ (self on: aDataFrame) open 42 | ] 43 | 44 | { #category : 'adding' } 45 | AISpAddRowPresenter >> addButtonAction [ 46 | 47 | newRow name: newRowName text. 48 | dataFrame addRow: newRow atPosition: positionInput number. 49 | self window close 50 | ] 51 | 52 | { #category : 'initialization' } 53 | AISpAddRowPresenter >> connectPresenters [ 54 | 55 | addButton action: [ self addButtonAction ]. 56 | resetButton action: [ self resetButtonAction ] 57 | ] 58 | 59 | { #category : 'layout' } 60 | AISpAddRowPresenter >> defaultLayout [ 61 | 62 | | topLayout bottomLayout | 63 | topLayout := SpBoxLayout newLeftToRight 64 | spacing: 70; 65 | add: (SpGridLayout new 66 | add: 'Position' at: 1 @ 1; 67 | add: positionInput at: 2 @ 1; 68 | yourself); 69 | add: (SpGridLayout new 70 | add: 'Row Name' at: 1 @ 1; 71 | add: newRowName at: 2 @ 1; 72 | yourself); 73 | yourself. 74 | 75 | bottomLayout := SpBoxLayout newLeftToRight 76 | spacing: 5; 77 | add: addButton width: 65; 78 | add: resetButton width: 65; 79 | yourself. 80 | 81 | ^ SpBoxLayout newTopToBottom 82 | spacing: 15; 83 | add: topLayout height: 40; 84 | add: rowInputTable height: 70; 85 | add: bottomLayout height: 30; 86 | yourself 87 | ] 88 | 89 | { #category : 'initialization' } 90 | AISpAddRowPresenter >> initializeAddResetButtons [ 91 | 92 | addButton := self newButton 93 | label: 'Add'; 94 | icon: (self iconNamed: #smallAdd). 95 | resetButton := self newButton 96 | label: 'Reset'; 97 | icon: (self iconNamed: #refreshIcon) 98 | ] 99 | 100 | { #category : 'initialization' } 101 | AISpAddRowPresenter >> initializeNewRowInputTable [ 102 | 103 | emptyRow := dataFrame rowsFrom: 1 to: 1. 104 | 1 to: emptyRow numberOfColumns do: [ :columnIndex | 105 | 1 to: emptyRow numberOfRows do: [ :rowIndex | 106 | emptyRow at: rowIndex at: columnIndex put: '' ] ]. 107 | newRow := DataSeries 108 | withKeys: dataFrame columnNames 109 | values: (emptyRow at: 1) asArray. 110 | newRowInputTable := self newTable. 111 | dataFrame columnNames doWithIndex: [ :headerName :columnIndex | 112 | | column | 113 | column := SpStringTableColumn 114 | title: headerName 115 | evaluated: [ :rowWithName | 116 | (rowWithName at: columnIndex + 1) asString ]. 117 | column beEditable. 118 | column onAcceptEdition: [ :row :newValue | 119 | newRow at: headerName put: newValue ]. 120 | newRowInputTable addColumn: column ]. 121 | newRowInputTable beResizable. 122 | newRowInputTable items: emptyRow asArrayOfRowsWithName 123 | ] 124 | 125 | { #category : 'as yet unclassified' } 126 | AISpAddRowPresenter >> initializeNewRowName [ 127 | 128 | newRowName := self newTextInput 129 | placeholder: 'row name'; 130 | text: ''; 131 | yourself 132 | ] 133 | 134 | { #category : 'initialization' } 135 | AISpAddRowPresenter >> initializePositionInput [ 136 | 137 | positionInput := self newNumberInput 138 | number: dataFrame numberOfRows + 1; 139 | beInteger; 140 | yourself 141 | ] 142 | 143 | { #category : 'initialization' } 144 | AISpAddRowPresenter >> initializePresenters [ 145 | 146 | self initializeNewRowName. 147 | self initializeRowInputTable. 148 | self initializePositionInput. 149 | self initializeAddResetButtons 150 | ] 151 | 152 | { #category : 'as yet unclassified' } 153 | AISpAddRowPresenter >> initializeRowInputTable [ 154 | 155 | emptyRow := dataFrame rowsFrom: 1 to: 1. 156 | 1 to: emptyRow numberOfColumns do: [ :columnIndex | 157 | 1 to: emptyRow numberOfRows do: [ :rowIndex | 158 | emptyRow at: rowIndex at: columnIndex put: '' ] ]. 159 | newRow := DataSeries 160 | withKeys: dataFrame columnNames 161 | values: (emptyRow at: 1) asArray. 162 | rowInputTable := self newTable. 163 | dataFrame columnNames doWithIndex: [ :headerName :columnIndex | 164 | | column | 165 | column := SpStringTableColumn 166 | title: headerName 167 | evaluated: [ :rowWithName | 168 | (rowWithName at: columnIndex + 1) asString ]. 169 | column beEditable. 170 | column onAcceptEdition: [ :row :newValue | 171 | newRow at: headerName put: newValue ]. 172 | rowInputTable addColumn: column ]. 173 | rowInputTable beResizable. 174 | rowInputTable items: emptyRow asArrayOfRowsWithName 175 | ] 176 | 177 | { #category : 'initialization' } 178 | AISpAddRowPresenter >> initializeWindow: aWindowPresenter [ 179 | 180 | aWindowPresenter 181 | initialExtent: 500 @ 250; 182 | title: 'Add Row'; 183 | whenClosedDo: [ 184 | newRowName text: ''. 185 | positionInput number: dataFrame numberOfRows + 1 ] 186 | ] 187 | 188 | { #category : 'initialization' } 189 | AISpAddRowPresenter >> resetButtonAction [ 190 | 191 | self initializeNewRowInputTable. 192 | self layout replace: rowInputTable with: newRowInputTable. 193 | rowInputTable := newRowInputTable 194 | ] 195 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFrameDescriberPresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | ## Description 3 | 4 | Provide a Spec Presenter to display a data frame detailed description of its contents and basic statistics. 5 | 6 | ## Examples 7 | ```language=Pharo 8 | AISpDataFrameInspector openOn: AIDatasets loadIris. 9 | AISpDataFrameInspector openOn: AIDatasets loadWine. 10 | ``` 11 | " 12 | Class { 13 | #name : 'AISpDataFrameDescriberPresenter', 14 | #superclass : 'SpPresenter', 15 | #instVars : [ 16 | 'dataFrame', 17 | 'evaluatorPane', 18 | 'selectedItems', 19 | 'additionalInfoPane', 20 | 'dataFramePresenter', 21 | 'summaryPresenter' 22 | ], 23 | #classVars : [ 24 | 'MaxRows' 25 | ], 26 | #category : 'AI-DataFrameInspector-Main presenters', 27 | #package : 'AI-DataFrameInspector', 28 | #tag : 'Main presenters' 29 | } 30 | 31 | { #category : 'accessing' } 32 | AISpDataFrameDescriberPresenter class >> maxRows [ 33 | " Answer the of rows to visualize in the receiver " 34 | 35 | ^ MaxRows ifNil: [ MaxRows := 1000 ] 36 | ] 37 | 38 | { #category : 'accessing' } 39 | AISpDataFrameDescriberPresenter class >> maxRows: anInteger [ 40 | " Set the maximum of rows to visualize in the receiver " 41 | 42 | MaxRows := anInteger 43 | ] 44 | 45 | { #category : 'instance creation' } 46 | AISpDataFrameDescriberPresenter class >> openOn: aDataFrame [ 47 | 48 | ^ (self on: aDataFrame) open 49 | ] 50 | 51 | { #category : 'initialization' } 52 | AISpDataFrameDescriberPresenter >> contextMenu [ 53 | 54 | ^ self contextMenuActions asMenuPresenter 55 | ] 56 | 57 | { #category : 'initialization' } 58 | AISpDataFrameDescriberPresenter >> contextMenuActions [ 59 | 60 | ^ CmCommandGroup forSpec 61 | beRoot; 62 | register: (AISpDataFrameCopyRowValuesCommand forSpecContext: self); 63 | register: (AISpDataFrameCopyRowValuesWithLabelsCommand forSpecContext: self); 64 | register: (AISpDataFrameCopyRowSelectionsCommand forSpecContext: self); 65 | register: (AISpDataFrameInspectCommand forSpecContext: self); 66 | yourself 67 | ] 68 | 69 | { #category : 'initialization' } 70 | AISpDataFrameDescriberPresenter >> contextMenuKeyBindings [ 71 | 72 | ^ self contextMenuActions asKMCategory 73 | ] 74 | 75 | { #category : 'accessing' } 76 | AISpDataFrameDescriberPresenter >> dataFramePresenter [ 77 | 78 | ^ dataFramePresenter 79 | ] 80 | 81 | { #category : 'layout' } 82 | AISpDataFrameDescriberPresenter >> defaultLayout [ 83 | 84 | ^ SpPanedLayout newTopToBottom 85 | add: dataFramePresenter; 86 | add: (SpPanedLayout newLeftToRight 87 | positionOfSlider: 60 percent; 88 | add: summaryPresenter; 89 | add: (SpPanedLayout newTopToBottom 90 | positionOfSlider: 35 percent; 91 | add: additionalInfoPane; 92 | add: evaluatorPane; 93 | yourself); 94 | yourself); 95 | yourself 96 | ] 97 | 98 | { #category : 'initialization' } 99 | AISpDataFrameDescriberPresenter >> initialize [ 100 | 101 | selectedItems := Set new. 102 | super initialize 103 | ] 104 | 105 | { #category : 'initialization' } 106 | AISpDataFrameDescriberPresenter >> initializeAdditionalInfoPresenter [ 107 | 108 | additionalInfoPane := self instantiate: AISpDataFrameBasicInfoPresenter on: dataFrame 109 | ] 110 | 111 | { #category : 'initialization' } 112 | AISpDataFrameDescriberPresenter >> initializeDataFramePresenter [ 113 | 114 | dataFramePresenter := self newTable. 115 | dataFramePresenter 116 | enableSearch; 117 | contextMenu: [ self contextMenu ]; 118 | contextKeyBindings: self contextMenuKeyBindings. 119 | 120 | self initializeDataFramePresenterContents. 121 | 122 | dataFramePresenter items: dataFrame asArrayOfRowsWithName 123 | ] 124 | 125 | { #category : 'initialization' } 126 | AISpDataFrameDescriberPresenter >> initializeDataFramePresenterContents [ 127 | " Private - Set the receiver's displayed contents. Display row names if present " 128 | 129 | dataFramePresenter 130 | addColumn: self newIndexColumn; 131 | addColumn: self newCheckBoxColumn; 132 | addColumn: self newRowNamesColumn. 133 | 134 | dataFrame columnNames doWithIndex: [ : columnName :idx | 135 | dataFramePresenter addColumn: (SpStringTableColumn new 136 | title: columnName; 137 | compareFunction: [ : colA : colB | 138 | (colA at: (dataFrame indexOfColumnNamed: columnName) + 1) <= 139 | (colB at: (dataFrame indexOfColumnNamed: columnName) + 1) ]; 140 | evaluated: [ : selection | selection dataSeriesElementAt: idx + 1 ] ) ]. 141 | 142 | 143 | ] 144 | 145 | { #category : 'initialization' } 146 | AISpDataFrameDescriberPresenter >> initializeEvaluatorPresenter [ 147 | 148 | evaluatorPane := self instantiate: AISpDataFrameEvaluatorPresenter on: dataFrame 149 | ] 150 | 151 | { #category : 'initialization' } 152 | AISpDataFrameDescriberPresenter >> initializePresenters [ 153 | 154 | self initializeDataFramePresenter. 155 | self initializeEvaluatorPresenter. 156 | self initializeAdditionalInfoPresenter. 157 | self initializeSummaryPresenter. 158 | 159 | self focusOrder add: evaluatorPane 160 | ] 161 | 162 | { #category : 'initialization' } 163 | AISpDataFrameDescriberPresenter >> initializeSummaryPresenter [ 164 | 165 | summaryPresenter := self instantiate: AISpDataFrameSummaryPresenter on: dataFrame 166 | ] 167 | 168 | { #category : 'initialization' } 169 | AISpDataFrameDescriberPresenter >> initializeWindow: aWindow [ 170 | 171 | aWindow 172 | extent: 1200 @ 700; 173 | centered; 174 | title: 'DataFrame Inspector' translated; 175 | aboutText: 'About DataFrame' 176 | ] 177 | 178 | { #category : 'private building' } 179 | AISpDataFrameDescriberPresenter >> newCheckBoxColumn [ 180 | " Private - Answer a check box column to select specific rows in the receiver " 181 | 182 | ^ (SpCheckBoxTableColumn 183 | evaluated: [ :branch | self selectedItems includes: branch ]) 184 | onActivation: [ :branch | self selectedItems add: branch ]; 185 | onDeactivation: [ :branch | self selectedItems remove: branch ]; 186 | width: 20 * self currentWorld displayScaleFactor; 187 | yourself 188 | ] 189 | 190 | { #category : 'initialization' } 191 | AISpDataFrameDescriberPresenter >> newIndexColumn [ 192 | 193 | ^ SpIndexTableColumn new 194 | title: '#'; 195 | width: 30; 196 | beNotExpandable; 197 | yourself 198 | 199 | ] 200 | 201 | { #category : 'initialization' } 202 | AISpDataFrameDescriberPresenter >> newRowNamesColumn [ 203 | 204 | ^ SpStringTableColumn new 205 | title: 'Row Name'; 206 | compareFunction: [ : objA : objB | objA asString < objB asString ]; 207 | evaluated: [ : rowWithName | rowWithName at: 1 ] 208 | ] 209 | 210 | { #category : 'accessing' } 211 | AISpDataFrameDescriberPresenter >> selectedItems [ 212 | 213 | ^ selectedItems 214 | ] 215 | 216 | { #category : 'accessing - model' } 217 | AISpDataFrameDescriberPresenter >> setModelBeforeInitialization: aModel [ 218 | 219 | dataFrame := aModel 220 | ] 221 | -------------------------------------------------------------------------------- /src/AI-DataFrameInspector/AISpDataFramePresenter.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Class for handling the Dataframe in **interactive** mode. 3 | These are some of the key features of the interactive mode: 4 | - **Edit** : DataFrames can be edited and the changes will be saved to the original DataFrame. 5 | - **Search** : Only those rows of the DataFrame will be visible which have the element in the search bar. 6 | - **Sort** : A sorting window opens where the users can select from drop-down lists the column by which they want the DataFrame to be sorted and the sorting block they would like to use. 7 | - Column widths can be **resized** by dragging the column borders. 8 | " 9 | Class { 10 | #name : 'AISpDataFramePresenter', 11 | #superclass : 'SpPresenter', 12 | #instVars : [ 13 | 'dataFrame', 14 | 'dataFramePresenter', 15 | 'dataFrameColumnsEditable', 16 | 'setRowNames', 17 | 'editButton', 18 | 'searchBar', 19 | 'sortButton', 20 | 'addRowButton', 21 | 'newdataFramePresenter', 22 | 'sortPresenter', 23 | 'addRowPresenter', 24 | 'windowPresenter' 25 | ], 26 | #category : 'AI-DataFrameInspector-Main presenters', 27 | #package : 'AI-DataFrameInspector', 28 | #tag : 'Main presenters' 29 | } 30 | 31 | { #category : 'instance creation' } 32 | AISpDataFramePresenter class >> openOn: aDataFrame [ 33 | 34 | ^ (self on: aDataFrame) open 35 | ] 36 | 37 | { #category : 'adding' } 38 | AISpDataFramePresenter >> addRowButtonAction [ 39 | 40 | windowPresenter := addRowPresenter open. 41 | windowPresenter whenClosedDo: [ self refreshLayout ] 42 | ] 43 | 44 | { #category : 'initialization' } 45 | AISpDataFramePresenter >> connectPresenters [ 46 | 47 | editButton action: [ self editButtonAction ]. 48 | searchBar whenTextChangedDo: [ self searchBarAction ]. 49 | sortButton action: [ self sortButtonAction ]. 50 | addRowButton action: [ self addRowButtonAction ] 51 | ] 52 | 53 | { #category : 'as yet unclassified' } 54 | AISpDataFramePresenter >> dataFrameContextMenu [ 55 | 56 | ^ self newMenu addItem: [ :item | 57 | item 58 | name: 'delete row'; 59 | icon: (self iconNamed: #remove); 60 | action: [ 61 | dataFrame removeRow: (dataFramePresenter selectedItem at: 1). 62 | self refreshLayout ] ] 63 | ] 64 | 65 | { #category : 'layout' } 66 | AISpDataFramePresenter >> defaultLayout [ 67 | 68 | ^ SpBoxLayout newTopToBottom 69 | spacing: 5; 70 | add: (SpBoxLayout newLeftToRight 71 | spacing: 5; 72 | add: editButton width: 60; 73 | add: sortButton width: 50; 74 | add: searchBar width: 100; 75 | add: addRowButton width: 80; 76 | yourself) 77 | height: 20; 78 | add: dataFramePresenter; 79 | yourself 80 | ] 81 | 82 | { #category : 'editing' } 83 | AISpDataFramePresenter >> editButtonAction [ 84 | 85 | dataFrameColumnsEditable := dataFrameColumnsEditable not. 86 | dataFrameColumnsEditable 87 | ifTrue: [ 88 | editButton 89 | label: 'read'; 90 | icon: (self iconNamed: #book) ] 91 | ifFalse: [ 92 | editButton 93 | label: 'edit'; 94 | icon: (self iconNamed: #editIcon) ]. 95 | self editDataFrame. 96 | self layout replace: dataFramePresenter with: newdataFramePresenter. 97 | dataFramePresenter := newdataFramePresenter 98 | ] 99 | 100 | { #category : 'editing' } 101 | AISpDataFramePresenter >> editDataFrame [ 102 | 103 | newdataFramePresenter := self newTable. 104 | 105 | newdataFramePresenter addColumn: (SpIndexTableColumn new 106 | title: '#'; 107 | sortFunction: #yourself ascending; 108 | beNotExpandable; 109 | yourself). 110 | dataFrame rowNames 111 | = (1 to: dataFrame numberOfRows) asOrderedCollection ifFalse: [ 112 | newdataFramePresenter addColumn: (SpStringTableColumn 113 | title: '' 114 | evaluated: [ :rowWithName | rowWithName at: 1 ]) ]. 115 | dataFrameColumnsEditable 116 | ifFalse: [ 117 | dataFrame columnNames doWithIndex: [ :columnName :idx | 118 | newdataFramePresenter addColumn: (SpStringTableColumn new 119 | title: columnName; 120 | beSortable; 121 | evaluated: [ :selection | 122 | selection dataSeriesElementAt: idx + 1 ]) ] ] 123 | ifTrue: [ 124 | dataFrame columnNames doWithIndex: [ :columnName :idx | 125 | | column | 126 | column := SpStringTableColumn 127 | title: columnName 128 | evaluated: [ :rowWithName | 129 | | originalValue | 130 | originalValue := rowWithName at: idx + 1. 131 | originalValue asString ]. 132 | column beEditable. 133 | column onAcceptEdition: [ :row :newValue | 134 | | originalRowName rowInd putVal | 135 | putVal := newValue. 136 | originalRowName := row at: 1. 137 | rowInd := dataFrame indexOfRowNamed: originalRowName. 138 | (dataFrame at: rowInd at: idx) isNumber ifTrue: [ 139 | putVal := newValue asNumber ]. 140 | ((dataFrame at: rowInd at: idx) isKindOf: Boolean) ifTrue: [ 141 | newValue = 'true' ifTrue: [ putVal := true ]. 142 | newValue = 'false' ifTrue: [ putVal := false ] ]. 143 | 144 | dataFrame at: rowInd at: idx put: putVal ]. 145 | newdataFramePresenter addColumn: column ] ]. 146 | newdataFramePresenter beResizable. 147 | 148 | newdataFramePresenter 149 | items: dataFrame asArrayOfRowsWithName; 150 | contextMenu: self dataFrameContextMenu 151 | ] 152 | 153 | { #category : 'searching' } 154 | AISpDataFramePresenter >> filterTableWithSearchQuery: newValue [ 155 | 156 | | filteredRows | 157 | filteredRows := dataFrame select: [ :row | 158 | row anySatisfy: [ :value | 159 | value asString 160 | includesSubstring: newValue asString 161 | at: 1 ] ]. 162 | 163 | newdataFramePresenter items: filteredRows asArrayOfRowsWithName 164 | ] 165 | 166 | { #category : 'initialization' } 167 | AISpDataFramePresenter >> initialize [ 168 | 169 | dataFrameColumnsEditable := false. 170 | setRowNames := false. 171 | super initialize 172 | ] 173 | 174 | { #category : 'as yet unclassified' } 175 | AISpDataFramePresenter >> initializeAddRowPresenter [ 176 | 177 | windowPresenter := SpWindowPresenter new. 178 | addRowPresenter := self 179 | instantiate: AISpAddRowPresenter 180 | on: dataFrame 181 | ] 182 | 183 | { #category : 'initialization' } 184 | AISpDataFramePresenter >> initializeDataFramePresenter [ 185 | 186 | dataFramePresenter := self newTable. 187 | 188 | dataFramePresenter addColumn: (SpIndexTableColumn new 189 | title: '#'; 190 | sortFunction: #yourself ascending; 191 | beNotExpandable; 192 | yourself). 193 | dataFrame rowNames 194 | = (1 to: dataFrame numberOfRows) asOrderedCollection ifFalse: [ 195 | setRowNames := true. 196 | dataFramePresenter addColumn: (SpStringTableColumn 197 | title: '' 198 | evaluated: [ :rowWithName | rowWithName at: 1 ]) ]. 199 | 200 | dataFrame columnNames doWithIndex: [ :headerName :columnIndex | 201 | dataFramePresenter addColumn: (SpStringTableColumn 202 | title: headerName 203 | evaluated: [ :rowWithName | rowWithName at: columnIndex + 1 ]) ]. 204 | dataFramePresenter beResizable. 205 | 206 | dataFramePresenter 207 | items: dataFrame asArrayOfRowsWithName; 208 | contextMenu: self dataFrameContextMenu 209 | ] 210 | 211 | { #category : 'initialization' } 212 | AISpDataFramePresenter >> initializeNewDataFramePresenter [ 213 | 214 | newdataFramePresenter := self newTable. 215 | 216 | newdataFramePresenter addColumn: (SpIndexTableColumn new 217 | title: '#'; 218 | sortFunction: #yourself ascending; 219 | beNotExpandable; 220 | yourself). 221 | setRowNames ifTrue: [ 222 | newdataFramePresenter addColumn: (SpStringTableColumn 223 | title: '' 224 | evaluated: [ :rowWithName | rowWithName at: 1 ]) ]. 225 | 226 | dataFrame columnNames doWithIndex: [ :headerName :columnIndex | 227 | newdataFramePresenter addColumn: (SpStringTableColumn 228 | title: headerName 229 | evaluated: [ :rowWithName | rowWithName at: columnIndex + 1 ]) ]. 230 | newdataFramePresenter beResizable. 231 | 232 | newdataFramePresenter 233 | items: dataFrame asArrayOfRowsWithName; 234 | contextMenu: self dataFrameContextMenu 235 | ] 236 | 237 | { #category : 'initialization' } 238 | AISpDataFramePresenter >> initializePresenters [ 239 | 240 | self initializeDataFramePresenter. 241 | self initializeToolbarPresenter. 242 | self initializeSortPresenter. 243 | self initializeAddRowPresenter 244 | ] 245 | 246 | { #category : 'initialization' } 247 | AISpDataFramePresenter >> initializeSortPresenter [ 248 | 249 | windowPresenter := SpWindowPresenter new. 250 | sortPresenter := self 251 | instantiate: AISpSortTablePresenter 252 | on: dataFrame 253 | ] 254 | 255 | { #category : 'initialization' } 256 | AISpDataFramePresenter >> initializeToolbarPresenter [ 257 | 258 | editButton := self newButton 259 | label: 'Edit'; 260 | icon: (self iconNamed: #editIcon); 261 | yourself. 262 | searchBar := self newTextInput placeholder: ' search'. 263 | sortButton := self newButton 264 | label: 'sort'; 265 | icon: (self iconNamed: #smallOpen); 266 | yourself. 267 | addRowButton := self newButton 268 | label: 'Add Row'; 269 | icon: (self iconNamed: #smallAdd); 270 | yourself 271 | ] 272 | 273 | { #category : 'as yet unclassified' } 274 | AISpDataFramePresenter >> refreshLayout [ 275 | 276 | self initializeNewDataFramePresenter. 277 | self layout replace: dataFramePresenter with: newdataFramePresenter. 278 | dataFramePresenter := newdataFramePresenter 279 | ] 280 | 281 | { #category : 'searching' } 282 | AISpDataFramePresenter >> searchBarAction [ 283 | 284 | searchBar text 285 | ifEmpty: [ 286 | self initializeDataFramePresenter. 287 | self layout replace: newdataFramePresenter with: dataFramePresenter ] 288 | ifNotEmpty: [ :text | 289 | newdataFramePresenter := dataFramePresenter. 290 | self filterTableWithSearchQuery: text. 291 | self layout replace: dataFramePresenter with: newdataFramePresenter ] 292 | ] 293 | 294 | { #category : 'accessing - model' } 295 | AISpDataFramePresenter >> setModelBeforeInitialization: aModel [ 296 | 297 | dataFrame := aModel 298 | ] 299 | 300 | { #category : 'sorting' } 301 | AISpDataFramePresenter >> sortButtonAction [ 302 | 303 | windowPresenter := sortPresenter open. 304 | windowPresenter whenClosedDo: [ self refreshLayout ] 305 | ] 306 | --------------------------------------------------------------------------------