[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Help-smalltalk] [PATCH] GNUPlot bindings - histograms
From: |
Paolo Bonzini |
Subject: |
[Help-smalltalk] [PATCH] GNUPlot bindings - histograms |
Date: |
Wed, 05 Mar 2008 14:11:54 +0100 |
User-agent: |
Thunderbird 2.0.0.12 (Macintosh/20080213) |
I added support for histograms to the GNUPlot bindings (it's the only
part of gst that I get to use at work so...). It's not done using
gnuplot's histograms, because it doesn't really map well to an
object-oriented interface.
Stacked bars only work if the data for all the bars comes from the same
data source. It's going into 3.0.2 too.
Paolo
diff --git a/packages/gnuplot/2D.st b/packages/gnuplot/2D.st
index 4ad0a0b..6b1db28 100644
--- a/packages/gnuplot/2D.st
+++ b/packages/gnuplot/2D.st
@@ -33,7 +33,7 @@ GPAbstractPlot subclass: GPPlot [
<category: 'GNUPlot'>
<comment: 'My instance is used to define a single ''plot'' command.'>
- | xAxis x2Axis yAxis y2Axis |
+ | xAxis x2Axis yAxis y2Axis barWidth barGap |
GPPlot class >> defaultStyleClass [
^GPPlotStyle
@@ -88,6 +88,38 @@ GPAbstractPlot subclass: GPPlot [
y2Axis := aGPAxis
]
+ barGap [
+ <category: 'accessing'>
+ ^barGap
+ ]
+
+ barGap: anInteger [
+ <category: 'accessing'>
+ barGap := anInteger
+ ]
+
+ barWidth [
+ <category: 'accessing'>
+ barWidth isNil ifTrue: [ barWidth := 1 ].
+ ^barWidth
+ ]
+
+ barWidth: aNumber [
+ <category: 'accessing'>
+ barWidth := aNumber
+ ]
+
+ newGroup: anInteger of: maxGroup [
+ <category: 'private'>
+ | gap |
+ gap := (self barGap ifNil: [ #(##(1/2) 1) at: maxGroup ifAbsent: [2] ])
+ - (1 - self barWidth).
+ ^(super newGroup: anInteger of: maxGroup)
+ barOffset: (2 * anInteger - 1 - maxGroup) / 2 / (maxGroup + gap);
+ barWidth: self barWidth / (maxGroup + gap);
+ yourself
+ ]
+
function: exprBlock [
<category: 'building'>
| expr |
@@ -232,6 +264,19 @@ GPAbstractPlot subclass: GPPlot [
^series
]
+ bars: aDataSourceOrArray [
+ <category: 'building'>
+ ^self add: (GPBarSeries on: aDataSourceOrArray)
+ ]
+
+ bars: aDataSourceOrArray with: aBlock [
+ <category: 'building'>
+ | series |
+ series := self bars: aDataSourceOrArray.
+ aBlock value: series.
+ ^series
+ ]
+
boxes: aDataSourceOrArray [
<category: 'building'>
^self add: ((GPBoxSeries on: aDataSourceOrArray)
@@ -380,6 +425,75 @@ GPStyle subclass: GPPlotStyle [
]
+GPGroupSeries subclass: GPBarSeries [
+ <category: 'GNUPlot'>
+ <comment: 'My instance is used to define a data series for an histogram.'>
+
+ defaultColumns [
+ ^{ GPColumnRef column: 1 }
+ ]
+
+ data [
+ ^self columns first
+ ]
+
+ data: expr [
+ self columns: { expr asGPExpression }
+ ]
+
+ displayOn: aStream group: aGroup [
+ self dataSource displayOn: aStream.
+ aStream nextPutAll: ' using '.
+ self displayColumnsOn: aStream group: aGroup.
+ self displayStyleOn: aStream group: aGroup.
+ aGroup stackData: self data.
+ self displayTicLabelsOn: aStream group: aGroup.
+ ]
+
+ displayColumnsOn: aStream group: aGroup [
+ aStream nextPutAll: '($0'.
+ aGroup barOffset < 0 ifFalse: [ aStream nextPut: $+ ].
+ aStream print: aGroup barOffset asFloat.
+ aStream nextPutAll: '):'.
+ aGroup dataOffset isNil
+ ifTrue: [
+ aStream display: self data.
+ aStream nextPutAll: ':('.
+ aStream display: aGroup barWidth asFloat.
+ aStream nextPut: $) ]
+ ifFalse: [
+ aStream display: aGroup dataOffset + (self data / 2).
+ aStream nextPutAll: ':('.
+ aStream display: (aGroup barWidth / 2) asFloat.
+ aStream nextPutAll: '):'.
+ aStream display: self data / 2 ].
+ ]
+
+ displayStyleOn: aStream group: aGroup [
+ aGroup dataOffset isNil
+ ifTrue: [ aStream nextPutAll: ' with boxes' ]
+ ifFalse: [ aStream nextPutAll: ' with boxxyerrorbars' ].
+ super displayStyleOn: aStream group: aGroup
+ ]
+
+ displayTicLabelsOn: aStream group: aGroup [
+ ticColumns isNil ifFalse: [
+ aStream nextPutAll: ', '.
+ self dataSource displayOn: aStream.
+ aStream nextPutAll: ' using 0:'.
+ aStream display: aGroup dataOffset.
+ aStream nextPutAll: ':""'.
+ super displayTicLabelsOn: aStream group: aGroup.
+ aStream nextPutAll: ' notitle with labels'.
+ ]
+ ]
+
+ printDataOn: aStream [
+ super printDataOn: aStream.
+ ticColumns isNil ifFalse: [ super printDataOn: aStream ].
+ ]
+]
+
GPDataSeries subclass: GPXYSeries [
<category: 'GNUPlot'>
<comment: 'My instance is used to define a data series for (x,y) values.'>
diff --git a/packages/gnuplot/Base.st b/packages/gnuplot/Base.st
index c069db2..0aaabfa 100644
--- a/packages/gnuplot/Base.st
+++ b/packages/gnuplot/Base.st
@@ -464,12 +464,49 @@ GPContainer subclass: GPAbstractPlot [
series do: [:d | d displayPrologOn: aStream into: defs ].
]
+ groupedSeries [
+ "Assign groups to series that do not have one, and return a
+ Dictionary of OrderedCollections, holding the series according
+ to their #group."
+ <category: 'private'>
+
+ | groupedSeries maxGroup |
+ maxGroup := series inject: 0 into: [ :old :each |
+ each group = 0 ifTrue: [ each group: old + 1 ].
+ each group ].
+
+ groupedSeries := LookupTable new.
+ series do: [:d |
+ (groupedSeries
+ at: (self newGroup: d group of: maxGroup)
+ ifAbsentPut: [ OrderedCollection new ])
+ add: d ].
+
+ ^groupedSeries
+ ]
+
+ newGroup: anInteger of: maxGroup [
+ <category: 'private - factory'>
+ ^GPSeriesGroup new
+ id: anInteger;
+ yourself
+ ]
+
displaySeriesOn: aStream [
<category: 'printing'>
- series do: [:d | d displayOn: aStream]
- separatedBy: [aStream nextPutAll: ', '].
+ | groupedSeries first |
+ groupedSeries := self groupedSeries.
+ first := true.
+ groupedSeries
+ keysAndValuesDo: [:group :list |
+ list do: [:d |
+ first ifFalse: [aStream nextPutAll: ', '].
+ first := false.
+ d displayOn: aStream group: group]].
+
aStream nl.
- series do: [:d | d printDataOn: aStream]
+ groupedSeries do: [:list |
+ list do: [:d | d printDataOn: aStream]]
]
displayOn: aStream [
diff --git a/packages/gnuplot/ChangeLog b/packages/gnuplot/ChangeLog
index 9d21328..e9cc2d2 100644
--- a/packages/gnuplot/ChangeLog
+++ b/packages/gnuplot/ChangeLog
@@ -1,5 +1,14 @@
2008-03-04 Paolo Bonzini <address@hidden>
+ * 2D.st: Add #barGap, #barWidth, #bars:, GPBarSeries.
+ * Base.st: Fix some things in the design of grouped series.
+ * Examples.st: Add bar graph example.
+ * Series.st: Implement GPSeriesGroup. Use template method
+ pattern for GPSeries>>#displayOn:group: and
+ GPDataSeries>>#displayOn:group:.
+
+2008-03-04 Paolo Bonzini <address@hidden>
+
* Series.st: Add special ticSpacing value of 0 to suppress tics.
Add GPAxis>>#from: and GPAxis>>#to:
diff --git a/packages/gnuplot/Examples.st b/packages/gnuplot/Examples.st
index 0e120b2..5f81971 100644
--- a/packages/gnuplot/Examples.st
+++ b/packages/gnuplot/Examples.st
@@ -182,4 +182,57 @@ GPObject subclass: GNUPlotExamples [
Transcript display: p; nl.
p execute
]
+
+ GNUPlotExamples class >> bars [
+ ^self barsOn: nil
+ ]
+
+ GNUPlotExamples class >> barsOn: file [
+ | p plot data |
+ p := GNUPlot new.
+ file isNil ifFalse: [
+ p terminal: self newPngTerminal.
+ p output: file ].
+
+ data := #((1 2 'a') (2 3 'b') (3 4 'c') (4 5 'd') (5 6 'e')).
+
+ (plot := GPPlot new)
+ bars: data
+ with: [:series |
+ series style fillStyle: #solid.
+ series data: (GPColumnRef column: 1) ];
+
+ bars: data
+ with: [:series |
+ series style fillStyle: #solid.
+ series data: (GPColumnRef column: 2); xTicColumn: 3 ].
+
+ plot xAxis ticSpacing: 0.
+ plot xAxis from: -0.5 to: 4.5.
+ plot yAxis from: 0.
+ p add: plot.
+
+ data := #((1 1 'a') (2 1 'b') (3 1 'c') (4 1 'd') (5 1 'e')).
+
+ (plot := GPPlot new)
+ bars: data
+ with: [:series |
+ series style fillStyle: #solid.
+ series group: 1.
+ series data: (GPColumnRef column: 1) ];
+
+ bars: data
+ with: [:series |
+ series style fillStyle: #solid.
+ series group: 1.
+ series data: (GPColumnRef column: 2); xTicColumn: 3 ].
+
+ plot xAxis ticSpacing: 0.
+ plot xAxis from: -0.5 to: 4.5.
+ plot yAxis from: 0.
+
+ p add: plot.
+ Transcript display: p; nl.
+ p execute
+ ]
]
diff --git a/packages/gnuplot/Series.st b/packages/gnuplot/Series.st
index 8af98f0..270e725 100644
--- a/packages/gnuplot/Series.st
+++ b/packages/gnuplot/Series.st
@@ -163,6 +163,68 @@ function or data set.'>
]
]
+Object subclass: GPSeriesGroup [
+ <category: 'GNUPlot'>
+ <comment: 'I am used internally to track the series that have already
+been plotted in a group.'>
+
+ | id barWidth barOffset dataOffset |
+ = anObject [
+ <category: 'basic'>
+ ^self class == anObject class and: [ self id = anObject id ]
+ ]
+
+ hash [
+ <category: 'basic'>
+ ^id hash
+ ]
+
+ id [
+ <category: 'accessing'>
+ id isNil ifTrue: [ id := 0 ].
+ ^id
+ ]
+
+ id: anInteger [
+ <category: 'accessing'>
+ id := anInteger
+ ]
+
+ barWidth [
+ <category: 'accessing'>
+ barWidth isNil ifTrue: [ barWidth := 0.5 ].
+ ^barWidth
+ ]
+
+ barWidth: aNumber [
+ <category: 'accessing'>
+ barWidth := aNumber
+ ]
+
+ barOffset [
+ <category: 'accessing'>
+ barOffset isNil ifTrue: [ barOffset := 0 ].
+ ^barOffset
+ ]
+
+ barOffset: aNumber [
+ <category: 'accessing'>
+ barOffset := aNumber
+ ]
+
+ dataOffset [
+ <category: 'accessing'>
+ ^dataOffset
+ ]
+
+ stackData: aColumn [
+ <category: 'accessing'>
+ dataOffset := dataOffset isNil
+ ifTrue: [ aColumn ]
+ ifFalse: [ dataOffset + aColumn ]
+ ]
+]
+
GPContainer subclass: GPSeries [
<category: 'GNUPlot'>
<comment: 'My instances are used to define a plotted function or data
set.'>
@@ -171,14 +233,25 @@ GPContainer subclass: GPSeries [
^GPSeriesStyle
]
+ addTo: aGPPlot [
+ <category: 'private - double dispatch'>
+ aGPPlot addSeries: self
+ ]
+
defaultTitle [
<category: 'dwim'>
self subclassResponsibility
]
- addTo: aGPPlot [
- <category: 'private - double dispatch'>
- aGPPlot addSeries: self
+ group [
+ <category: 'accessing'>
+ ^0
+ ]
+
+ group: anInteger [
+ <category: 'accessing'>
+ "Do nothing. Grouping would not affect the way most data
+ series are drawn."
]
printDataOn: aStream [
@@ -187,6 +260,17 @@ GPContainer subclass: GPSeries [
displayOn: aStream [
<category: 'printing'>
+ | group |
+ group := GPSeriesGroup new id: self group; yourself.
+ self displayOn: aStream group: group.
+ ]
+
+ displayOn: aStream group: aGroup [
+ <category: 'printing'>
+ self displayStyleOn: aStream group: aGroup
+ ]
+
+ displayStyleOn: aStream group: aGroup [
| theParameters |
theParameters := style ifNil: [ self class defaultStyle ].
theParameters displayOn: aStream for: self
@@ -248,7 +332,7 @@ GPSeries subclass: GPFunctionSeries [
^range ifNotNil: [ :r | r second ]
]
- displayOn: aStream [
+ displayOn: aStream group: aGroup [
<category: 'printing'>
range isNil ifFalse: [
aStream
@@ -259,7 +343,7 @@ GPSeries subclass: GPFunctionSeries [
nextPut: $];
space ].
expression displayOn: aStream.
- super displayOn: aStream
+ super displayOn: aStream group: aGroup
]
displayPrologOn: aStream into: defs [
@@ -330,13 +414,27 @@ GPSeries subclass: GPDataSeries [
graphType := aString
]
- displayOn: aStream [
- dataSource displayOn: aStream.
+ displayOn: aStream group: aGroup [
+ self dataSource displayOn: aStream.
aStream nextPutAll: ' using '.
+ self displayColumnsOn: aStream group: aGroup.
+ self displayTicLabelsOn: aStream group: aGroup.
+ super displayOn: aStream group: aGroup.
+ ]
+
+ displayStyleOn: aStream group: aGroup [
+ graphType isNil ifFalse: [
+ aStream nextPutAll: ' with '; nextPutAll: graphType; space ].
+ super displayStyleOn: aStream group: aGroup
+ ]
+
+ displayColumnsOn: aStream group: aGroup [
self columns
do: [ :each | each displayOn: aStream ]
separatedBy: [ aStream nextPut: $: ].
+ ]
+ displayTicLabelsOn: aStream group: aGroup [
"Add xticlabels etc. fake columns."
ticColumns isNil ifFalse: [
ticColumns keysAndValuesDo: [ :k :v |
@@ -346,9 +444,6 @@ GPSeries subclass: GPDataSeries [
nextPut: $(;
display: v;
nextPut: $) ] ].
-
- aStream nextPutAll: ' with '; nextPutAll: style; space.
- super displayOn: aStream.
]
printDataOn: aStream [
@@ -416,6 +511,24 @@ GPSeries subclass: GPDataSeries [
]"
]
+GPDataSeries subclass: GPGroupSeries [
+ <category: 'GNUPlot'>
+ <comment: 'My instances are used to define plotted data sets when
+more series can be grouped together (e.g. in stacked bars).'>
+
+ | group |
+ group [
+ <category: 'accessing'>
+ group isNil ifTrue: [ group := 0 ].
+ ^group
+ ]
+
+ group: anInteger [
+ <category: 'accessing'>
+ group := anInteger.
+ ]
+]
+
GPObject subclass: GPAxis [
<category: 'GNUPlot'>
@@ -423,7 +536,7 @@ GPObject subclass: GPAxis [
axis.'>
| name range logScale mirrorTics outwardTics ticRange ticSpacing ticFormat
- ticSubdivision majorGrid minorGrid tics style label labelStyle |
+ ticSubdivision majorGrid minorGrid tics style label labelStyle |
name: aString [
<category: 'private - initialization'>
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Help-smalltalk] [PATCH] GNUPlot bindings - histograms,
Paolo Bonzini <=