1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
# = Becker’s Barley
# The "Trellis display":http://www.jstor.org/stable/1390777 by Becker et al. helped establish small multiples as a “powerful mechanism for understanding interactions in studies of how a response depends on explanatory variables”. Here we reproduce a trellis of Barley yields from the 1930s, complete with main-effects ordering to facilitate comparison. These examples demonstrate Protovis’ support for data transformation via the nest operator.
# Notice anything unusual about one of the sites? This anomaly led Becker et al. to suspect a major error with the data that went undetected for six decades.
$:.unshift(File.dirname(__FILE__)+"/../../../lib")
require 'rubyvis'
load(File.dirname(__FILE__)+"/barley_data.rb")
# Nest yields data by site then year. */
# Compute yield medians by site and by variety. */
median=lambda {|data|
pv.median(data, lambda {|d| d[:yield]})
}
site = pv.nest($barley).
key(lambda {|d| d[:site]}).
rollup(median)
variety = pv.nest($barley).key(lambda {|d| d[:variety]}).rollup(median);
barley = pv.nest($barley)
.key(lambda {|d| d[:site]})
.sort_keys(lambda {|a, b| site[b] - site[a]})
.key(lambda {|d| d[:year]})
.sort_values(lambda {|a, b| variety[b[:variety]] - variety[a[:variety]]})
.entries()
# Sizing and scales. */
w = 332
h = 132
x = pv.Scale.linear(10, 90).range(0, w)
c = pv.Colors.category10()
# The root panel. */
vis = Rubyvis::Panel.new
.width(w)
.height(h * pv.keys(site).size)
.top(15.5)
.left(0.5)
.right(0.5)
.bottom(25);
# A panel per site-year. */
cell = vis.add(pv.Panel)
.data(barley)
.height(h)
.left(90)
.top(lambda { self.index * h})
.stroke_style(pv.color("#999"));
# Title bar. */
cell.add(pv.Bar)
.height(14)
.fill_style("bisque")
.anchor("center").add(pv.Label)
.text(lambda{|site| site.key});
# A dot showing the yield. */
dot = cell.add(pv.Panel)
.data(lambda {|site| site.values})
.top(23)
.add(pv.Dot)
.data(lambda {|year| year.values})
.left(lambda {|d| x.scale(d[:yield])})
.top(lambda {self.index * 11})
.shape_size(10)
.line_width(2)
.stroke_style(lambda {|d| c.scale(d[:year])})
# A label showing the variety. */
dot.anchor("left").add(pv.Label)
.visible(lambda { self.parent.index!=0})
.left(-2)
.text(lambda {|d| d[:variety]})
# X-ticks. */
vis.add(pv.Rule)
.data(x.ticks())
.left(lambda {|d| 90+x.scale(d)})
.bottom(-5)
.height(5)
.stroke_style("#999")
.anchor("bottom").add(pv.Label);
# A legend showing the year. */
vis.add(pv.Dot)
.mark_extend(dot)
.data([{:year=>1931}, {:year=>1932}])
.left(lambda {|d| 260 + self.index * 40})
.top(-8)
.anchor("right").add(pv.Label)
.text(lambda {|d| d[:year]})
vis.render()
puts vis.to_svg
|