The OpenD Programming Language

1 /**
2     This module contains the different examples that are shown in the README
3 
4     It will only be included in unittest code, but is empty otherwise.
5 */
6 module example;
7 
8 version (unittest)
9 
10 import dunit.toolkit;
11 import std.stdio : writeln;
12 
13 ///
14 unittest
15 {
16     /// http://blackedder.github.io/ggplotd/images/function.png
17     import std.random : uniform;
18     import std.typecons : Tuple;
19     import ggplotd.stat : statFunction;
20     import ggplotd.ggplotd : GGPlotD;
21     import ggplotd.geom : geomLine, geomPoint;
22     import ggplotd.range : mergeRange;
23 
24     auto f = (double x) { return x / (1 + x); };
25 
26     auto aes = statFunction(f, 0.0, 10);
27     auto gg = GGPlotD().put(geomLine(aes));
28 
29     // Generate some noisy points 
30     auto f2 = (double x) { return x / (1 + x) * uniform(0.75, 1.25); };
31     auto aes2 = f2.statFunction(0.0, 10, 25);
32 
33     // Show points in different colour
34     auto withColour = Tuple!(string, "colour")("aquamarine").mergeRange(aes2);
35     gg = gg.put(withColour.geomPoint);
36 
37     gg.save("function.png");
38 }
39 
40 ///
41 unittest
42 {
43     /// http://blackedder.github.io/ggplotd/images/hist2D.svg
44     import std.array : array;
45     import std.algorithm : map;
46     import std.range : iota, zip;
47     import std.random : uniform;
48 
49     import ggplotd.aes : aes;
50     import ggplotd.colour : colourGradient;
51     import ggplotd.colourspace : XYZ;
52     import ggplotd.geom : geomHist2D;
53     import ggplotd.ggplotd : GGPlotD, putIn;
54     import ggplotd.legend : continuousLegend;
55 
56     auto xs = iota(0,500,1).map!((x) => uniform(0.0,5)+uniform(0.0,5))
57         .array;
58     auto ys = iota(0,500,1).map!((y) => uniform(0.0,5)+uniform(0.0,5))
59         .array;
60     auto gg = xs.zip(ys)
61                 .map!((t) => aes!("x","y")(t[0], t[1]))
62                 .geomHist2D.putIn(GGPlotD());
63     // Use a different colour scheme
64     gg.put( colourGradient!XYZ( "white-cornflowerBlue-crimson" ) );
65 
66     gg.put(continuousLegend);
67 
68     gg.save( "hist2D.svg" );
69 }
70 
71 ///
72 unittest
73 {
74     /// http://blackedder.github.io/ggplotd/images/filled_density.svg
75     import std.array : array;
76     import std.algorithm : map;
77     import std.range : repeat, iota, chain, zip;
78     import std.random : uniform;
79 
80     import ggplotd.aes : aes;
81     import ggplotd.geom : geomDensity;
82     import ggplotd.ggplotd : GGPlotD, putIn;
83     import ggplotd.legend : discreteLegend;
84     auto xs = iota(0,50,1).map!((x) => uniform(0.0,5)+uniform(0.0,5)).array;
85     auto cols = "a".repeat(25).chain("b".repeat(25));
86     auto gg = xs.zip(cols, 0.45.repeat(xs.length))
87         .map!((a) => aes!("x", "colour", "fill")(a[0], a[1], a[2]))
88         .geomDensity
89         .putIn(GGPlotD());
90     gg = discreteLegend.putIn(gg);
91     gg.save( "filled_density.svg" );
92 }
93 
94 ///
95 unittest
96 {
97     /// http://blackedder.github.io/ggplotd/images/density2D.png
98     import std.algorithm : map;
99     import std.range : iota, zip;
100     import std.random : uniform, Random, unpredictableSeed;
101 
102     import ggplotd.aes : aes;
103     import ggplotd.colour : colourGradient;
104     import ggplotd.colourspace : XYZ;
105     import ggplotd.geom : geomDensity2D;
106     import ggplotd.ggplotd : GGPlotD, putIn;
107     import ggplotd.legend : continuousLegend;
108 
109     // For debugging reasons, print out the current seed
110     import std.stdio : writeln;
111     auto seed = unpredictableSeed;
112     auto rnd = Random(seed);
113     //auto rnd = Random(1193462362); // This is a seed that currently fails. Use it for debugging
114     writeln("Random seed MCMC: ", seed);
115 
116     auto xs = iota(0,500,1).map!((x) => uniform(0.0,5, rnd)+uniform(0.0,5, rnd));
117     auto ys = iota(0,500,1).map!((y) => uniform(0.5,1.5, rnd)+uniform(0.5,1.5, rnd));
118     auto gg = zip(xs, ys)
119         .map!((a) => aes!("x","y")(a[0], a[1]))
120         .geomDensity2D
121         .putIn( GGPlotD() );
122     // Use a different colour scheme
123     gg.put( colourGradient!XYZ( "white-cornflowerBlue-crimson" ) );
124     gg.put(continuousLegend);
125 
126     gg.save( "density2D.png" );
127 }
128 
129 ///
130 unittest
131 {
132     /// http://blackedder.github.io/ggplotd/images/labels.png
133     import std.algorithm : map;
134     import std.range : zip;
135     import std.math : PI;
136 
137     import ggplotd.aes : aes;
138     import ggplotd.geom : geomPoint, geomLabel;
139     import ggplotd.ggplotd : GGPlotD;
140     import ggplotd.axes : xaxisRange, yaxisRange;
141     auto dt = zip( [0.0,1,2,3,4], [4.0,3,2,1,0], 
142         ["center", "left", "right", "bottom", "top"],
143         [0.0, 0.0, 0.0, 0.0, 0.0],
144         ["center", "left", "right", "bottom", "top"])
145         .map!((a) => aes!("x", "y", "label", "angle", "justify")
146             (a[0], a[1], a[2], a[3], a[4]));
147 
148     auto gg = GGPlotD()
149         .put(geomPoint( dt ))
150         .put(geomLabel(dt))
151         .put(xaxisRange(-2,11))
152         .put(yaxisRange(-2,11));
153 
154     auto dt2 = zip( [1.0,2,3,4,5], [5.0,4,3,2,1], 
155         ["center", "left", "right", "bottom", "top"],
156         [0.5*PI, 0.5*PI, 0.5*PI, 0.5*PI, 0.5*PI],
157         ["center", "left", "right", "bottom", "top"])
158         .map!((a) => aes!("x", "y", "label", "angle", "justify")
159             (a[0], a[1], a[2], a[3], a[4]));
160     gg.put( geomLabel(dt2) ).put(geomPoint(dt2));
161 
162     auto dt3 = zip( [1.0,2,4,6,7], [8.0,7,5,3,2], 
163         ["center", "left", "right", "bottom", "top"],
164         [0.25*PI, 0.25*PI, 0.25*PI, 0.25*PI, 0.25*PI],
165         ["center", "left", "right", "bottom", "top"])
166         .map!((a) => aes!("x", "y", "label", "angle", "justify")
167             (a[0], a[1], a[2], a[3], a[4]));
168     gg.put( geomLabel(dt3) ).put(geomPoint(dt3));
169 
170     gg.save( "labels.png" );
171 }
172 
173 auto runMCMC() {
174     import std.algorithm : map;
175     import std.array : array;
176     import std.math : pow;
177     import std.range : iota; import std.random : Random, unpredictableSeed; 
178     // For debugging reasons, print out the current seed
179     import std.stdio : writeln;
180     auto seed = unpredictableSeed;
181     auto rnd = Random(seed);
182     //auto rnd = Random(1193462362); // This is a seed that currently fails. Use it for debugging
183     writeln("Random seed MCMC: ", seed);
184     //writeln("Random seed MCMC: ", rnd.front);
185 
186 
187     import dstats.random : rNormal;
188     return iota(0,1000).map!((i) {
189         auto x = rNormal(1, 0.5, rnd);
190         auto y = rNormal(pow(x,3), 0.5, rnd);
191         auto z = rNormal(x + y, 0.5, rnd);
192         return [x, y, z];
193     }).array;
194 }
195 
196 ///
197 unittest
198 {
199     // http://blackedder.github.io/ggplotd/images/parameter_distribution.png
200     import std.algorithm : map;
201     import std.format : format;
202     import ggplotd.aes : aes;
203     import ggplotd.axes : xaxisLabel, yaxisLabel;
204     import ggplotd.geom : geomDensity, geomDensity2D;
205     import ggplotd.ggplotd : Facets, GGPlotD, putIn;
206     import ggplotd.colour : colourGradient;
207     import ggplotd.colourspace : XYZ;
208 
209     // Running MCMC for a model that takes 3 parameters
210     // Will return 1000 posterior samples for the 3 parameters
211     // [[par1, par2, par3], ...]
212     auto samples = runMCMC();
213 
214     // Facets can be used for multiple subplots
215     Facets facets;
216 
217     // Cycle over the parameters
218     foreach(i; 0..3) 
219     {
220         foreach(j; 0..3) 
221         {
222             auto gg = GGPlotD();
223 
224             gg = format("Parameter %s", i).xaxisLabel.putIn(gg);
225             if (i != j)
226             {
227                 // Change the colourGradient used
228                 gg = colourGradient!XYZ( "white-cornflowerBlue-crimson" )
229                     .putIn(gg);
230                 gg = format("Parameter %s", j).yaxisLabel.putIn(gg);
231                 gg = samples.map!((sample) => aes!("x", "y")(sample[i], sample[j]))
232                     .geomDensity2D
233                     .putIn(gg);
234             } else {
235                 gg = "Density".yaxisLabel.putIn(gg);
236                 gg = samples.map!((sample) => aes!("x", "y")(sample[i], sample[j]))
237                     .geomDensity
238                     .putIn(gg);
239             }
240             facets = gg.putIn(facets);
241         }
242     }
243     facets.save("parameter_distribution.png", 670, 670);
244 }
245 
246 ///
247 unittest
248 {
249     // http://blackedder.github.io/ggplotd/images/diamonds.png
250     import std.csv : csvReader; import std.file : readText;
251     import std.algorithm : map;
252     import std.array : array;
253     import ggplotd.aes : aes;
254     import ggplotd.axes : xaxisLabel, yaxisLabel;
255     import ggplotd.ggplotd : GGPlotD, putIn;
256     import ggplotd.geom : geomPoint;
257 
258 
259     struct Diamond {
260         double carat;
261         string clarity;
262         double price;
263     }
264 
265     // Read the data
266     auto diamonds = readText("test_files/diamonds.csv").csvReader!(Diamond)(
267     ["carat","clarity","price"]);
268 
269     auto gg = diamonds.map!((diamond) => 
270         // Map data to aesthetics (x, y and colour)
271         aes!("x", "y", "colour", "size")(diamond.carat, diamond.price, diamond.clarity, 0.8))
272     .array
273     // Draw points
274     .geomPoint.putIn(GGPlotD());
275 
276     // Axis labels
277     gg = "Carat".xaxisLabel.putIn(gg);
278     gg = "Price".yaxisLabel.putIn(gg);
279     gg.save("diamonds.png"); 
280 }
281 
282 /// Multiple histograms examples
283 unittest
284 {
285     // http://blackedder.github.io/ggplotd/images/filled_hist.svg
286     import std.array : array;
287     import std.algorithm : map;
288     import std.range : repeat, iota, chain, zip;
289     import std.random : uniform;
290 
291     import ggplotd.aes : aes;
292     import ggplotd.geom : geomHist;
293     import ggplotd.ggplotd : putIn, GGPlotD;
294 
295     auto xs = iota(0,50,1).map!((x) => uniform(0.0,5)+uniform(0.0,5)).array;
296     auto cols = "a".repeat(25).chain("b".repeat(25));
297     auto gg = xs.zip(cols)
298         .map!((a) => aes!("x", "colour", "fill")(a[0], a[1], 0.45))
299         .geomHist
300         .putIn(GGPlotD());
301     gg.save( "filled_hist.svg" );
302 }
303 
304 /// Size as third dimension
305 unittest
306 {
307     import std.range : zip;
308     import std.algorithm : map;
309     import ggplotd.aes : aes;
310     import ggplotd.geom : geomPoint;
311     import ggplotd.ggplotd : putIn, GGPlotD;
312     import ggplotd.axes : xaxisRange, yaxisRange;
313 
314     auto gg = [0.0,1.0,2.0].zip([0.5, 0.25, 0.75], [1000, 10000, 50000])
315         .map!((a) => aes!("x", "y", "size")(a[0], a[1], a[2]))
316         .geomPoint
317         .putIn(GGPlotD());
318     gg.put(xaxisRange(-0.5, 2.5));
319     gg.put(yaxisRange(0, 1));
320     gg.save("sizeStore.png");
321 }
322 
323 /// Boxplot example
324 unittest
325 {
326     // http://blackedder.github.io/ggplotd/images/boxplot.svg
327     import std.array : array;
328     import std.algorithm : map;
329     import std.range : repeat, iota, chain, zip;
330     import std.random : uniform;
331 
332     import ggplotd.aes : aes;
333     import ggplotd.geom : geomBox;
334     import ggplotd.ggplotd : GGPlotD, putIn;
335 
336     auto xs = iota(0,50,1).map!((x) => uniform(0.0,5)+uniform(0.0,5));
337     auto cols = "a".repeat(25).chain("b".repeat(25));
338     auto gg = xs.zip(cols)
339         .map!((a) => aes!("x", "colour", "fill", "label" )(a[0], a[1], 0.45, a[1]))
340         .geomBox
341         .putIn(GGPlotD());
342     gg.save( "boxplot.svg" );
343 }
344 
345 /// Changing axes details
346 unittest
347 {
348     // http://blackedder.github.io/ggplotd/images/axes.svg
349     import std.array : array;
350     import std.math : sqrt;
351     import std.algorithm : map;
352     import std.range : iota;
353 
354     import ggplotd.aes : aes;
355     import ggplotd.axes : xaxisLabel, yaxisLabel, xaxisOffset, yaxisOffset, 
356 		   xaxisRange, yaxisRange, xaxisTextAngle;
357     import ggplotd.geom : geomLine;
358     import ggplotd.ggplotd : GGPlotD, putIn, Margins, title;
359     import ggplotd.stat : statFunction;
360 
361     auto f = (double x) { return x/(1+x); };
362     auto gg = statFunction(f, 0, 10.0)
363         .geomLine
364         .putIn(GGPlotD());
365 
366     // Setting range and label for xaxis
367     gg.put( xaxisRange( 0, 8 ) )
368 		.put( xaxisLabel( "My xlabel" ) )
369 		.put( xaxisTextAngle( 90 ) );
370     // Setting range and label for yaxis
371     gg.put( yaxisRange( 0, 2.0 ) ).put( yaxisLabel( "My ylabel" ) );
372 
373     // change offset
374     gg.put( xaxisOffset( 0.25 ) ).put( yaxisOffset( 0.5 ) );
375 
376     // Change Margins 
377     gg.put( Margins( 60, 60, 40, 30 ) );
378 
379     // Set a title
380     gg.put( title( "And now for something completely different" ) );
381 
382     // Saving on a 500x300 pixel surface
383     gg.save( "axes.svg", 500, 300 );
384 }
385 
386 /// Example from the readme using aes and merge
387 unittest
388 {
389     import ggplotd.aes : aes, merge;
390     struct Data1
391     {
392         double value1 = 1.0;
393         double value2 = 2.0;
394     }
395 
396     Data1 dat1;
397 
398     // Merge to add a value
399     auto merged = aes!("x", "y")(dat1.value1, dat1.value2)
400         .merge(
401             aes!("colour")("a")
402         );
403     assertEqual(merged.x, 1.0);
404     assertEqual(merged.colour, "a");
405 
406     // Merge to a second data struct
407     struct Data2 { string colour = "b"; } 
408     Data2 dat2;
409 
410     auto merged2 = aes!("x", "y")(dat1.value1, dat1.value2)
411         .merge( dat2 );
412     assertEqual(merged2.x, 1.0);
413     assertEqual(merged2.colour, "b");
414 
415     // Overriding a field 
416     auto merged3 = aes!("x", "y")(dat1.value1, dat1.value2)
417         .merge(
418             aes!("y")("a")
419         );
420     assertEqual(merged3.y, "a");
421 }
422 
423 /// Polygon
424 unittest
425 {
426     import std.range : zip;
427     import std.algorithm : map;
428     import ggplotd.aes : aes;
429     import ggplotd.geom : geomPolygon;
430     import ggplotd.ggplotd : GGPlotD, putIn;
431 
432     // http://blackedder.github.io/ggplotd/images/polygon.png
433     auto gg = zip([1, 0, 0.0], [1, 1, 0.0], [1, 0.1, 0])
434         .map!((a) => aes!("x", "y", "colour")(a[0], a[1], a[2]))
435         .geomPolygon
436         .putIn(GGPlotD());
437     gg.save( "polygon.png" );
438 }
439 
440 /// Log scale
441 unittest
442 {
443     import std.range : zip;
444     import std.algorithm : map;
445     import ggplotd.aes : aes;
446     import ggplotd.scale : scale;
447     import ggplotd.ggplotd : GGPlotD, putIn;
448     import ggplotd.geom : geomLine;
449 
450     // http://blackedder.github.io/ggplotd/images/logScale.png
451     auto gg = zip([1.0, 10.0, 15], [30, 100, 1000.0])
452         .map!((a) => aes!("x", "y")(a[0], a[1]))
453         .geomLine
454         .putIn(GGPlotD());
455 
456     gg = scale!("x")("log").putIn(gg);
457     gg = scale!("y")("log10").putIn(gg);
458     gg.save( "logScale.png" );
459 }