Skip to content

Commit c4bc5c3

Browse files
committed
ported code for stacked bar
1 parent 7b5baff commit c4bc5c3

6 files changed

Lines changed: 236 additions & 3 deletions

File tree

lib/rubyplot/axes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ def dot! *args, &block
7777
add_plot "Dot", *args, &block
7878
end
7979

80+
def stacked_bar! *args, &block
81+
add_plot "StackedBar", *args, &block
82+
end
83+
8084
def write file_name
8185
@plots[0].write file_name
8286
end

lib/rubyplot/magick_wrapper/plot.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
require_relative 'plot/area'
55
require_relative 'plot/bubble'
66
require_relative 'plot/dot'
7+
require_relative 'plot/stacked_bar'

lib/rubyplot/magick_wrapper/plot/dot.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def draw_line_markers
6262
@geometry.marker_count ||= 5
6363
end
6464
# TODO: Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
65-
@geometry.increment = @spread > 0 && @geometry.marker_count > 0 ? significant(@spread / @geometry.marker_count) : 1
65+
@geometry.increment = @spread > 0 && @geometry.marker_count > 0 ?
66+
significant(@spread / @geometry.marker_count) : 1
6667

6768
number_of_lines = @geometry.marker_count
6869
increment = @geometry.increment
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
require_relative 'stacked_bar/geometry'
2+
3+
module Rubyplot
4+
module MagickWrapper
5+
module Plot
6+
class StackedBar < Artist
7+
#DATA_VALUES_INDEX = Rubyplot::MagickArtist::DATA_VALUES_INDEX
8+
#
9+
# A stacked bar graph (or stacked bar chart) is a chart that uses bars to show
10+
# comparisons between categories of data, but with ability to break down and
11+
# compare parts of a whole. Each bar in the chart represents a whole, and
12+
# segments in the bar represent different parts or categories of that whole.
13+
#
14+
# ==== Example
15+
#
16+
# plot = Rubyplot::StackedBar.new(400)
17+
#
18+
# @datasets = [
19+
# [:Car, [25, 36, 86, 39]],
20+
# [:Bus, [80, 54, 67, 54]],
21+
# [:Train, [22, 29, 35, 38]]
22+
# ]
23+
#
24+
# plot.title = 'Stacked Bar'
25+
# plot.labels = {
26+
# 0 => '10',
27+
# 1 => '15',
28+
# 2 => '20',
29+
# 3 => '30'
30+
# }
31+
# @datasets.each do |data|
32+
# plot.data(data[0], data[1])
33+
# end
34+
# plot.write('stacked_bar.png')
35+
#
36+
def initialize(*)
37+
super
38+
@geometry = Plot::StackedBar::Geometry.new
39+
end
40+
41+
def set_spacings
42+
# Setup spacing.
43+
#
44+
# Columns sit stacked.
45+
@bar_spacing ||= 0.9
46+
@segment_spacing ||= 1
47+
@bar_width = @graph_width / @geometry.column_count.to_f
48+
@padding = (@bar_width * (1 - @bar_spacing)) / 2
49+
end
50+
51+
# Draws a bar graph, but multiple sets are stacked on top of each other.
52+
# Modified drawing feature of the bar graph with multiple bar graphs
53+
# stacked on top of each other.
54+
def draw
55+
max_stack_height
56+
artist_draw
57+
58+
set_spacings
59+
@d = @d.stroke_opacity 0.0
60+
height = Array.new(@geometry.column_count, 0)
61+
62+
@geometry.norm_data.each_with_index do |data_row, row_index|
63+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
64+
@d = @d.fill @plot_colors[row_index]
65+
# Calculate center based on bar_width and current row
66+
label_center = @graph_left + (@bar_width * point_index) +
67+
(@bar_width * @bar_spacing / 2.0)
68+
draw_label(label_center, point_index)
69+
70+
next if data_point == 0
71+
# Use incremented x and scaled y
72+
left_x = @graph_left + (@bar_width * point_index) + @padding
73+
left_y = @graph_top + (@graph_height -
74+
data_point * @graph_height -
75+
height[point_index]) + @segment_spacing
76+
right_x = left_x + @bar_width * @bar_spacing
77+
right_y = @graph_top + @graph_height - height[point_index] - @segment_spacing
78+
79+
# update the total height of the current stacked bar
80+
height[point_index] += (data_point * @graph_height)
81+
82+
# Draw the rectangle
83+
@d = @d.rectangle(left_x, left_y, right_x, right_y)
84+
end
85+
end
86+
87+
@d.draw(@base_image)
88+
end
89+
90+
def max_stack_height
91+
# Get the sum of values in each stack to the get the stack height
92+
max_hash = {}
93+
#@data.each do |data_set|
94+
@data[:y_values].each_with_index do |data_point, i|
95+
max_hash[i] = 0.0 unless max_hash[i]
96+
max_hash[i] += data_point.to_f
97+
end
98+
# end
99+
100+
@geometry.maximum_value = 0
101+
max_hash.keys.each do |key|
102+
@geometry.maximum_value = max_hash[key] if max_hash[key] > @geometry.maximum_value
103+
end
104+
@geometry.minimum_value = 0
105+
end
106+
end # class StackedBar
107+
end # module Plot
108+
end # module MagickWrapper
109+
end # module Rubyplot
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Rubyplot
2+
module MagickWrapper
3+
module Plot
4+
class StackedBar < MagickWrapper::Artist
5+
class Geometry < MagickWrapper::Artist::Geometry
6+
attr_accessor :all_colors_array
7+
attr_accessor :plot_colors
8+
9+
# Spacing factor applied between bars
10+
attr_accessor :bar_spacing
11+
12+
attr_accessor :graph_width, :bar_width, :padding
13+
# Number of pixels between bar segments
14+
attr_accessor :segment_spacing, :column_count
15+
16+
def initialize
17+
super
18+
@all_colors_array = Magick.colors
19+
@plot_colors = []
20+
end
21+
end # class Geometry
22+
end # class StackedBar
23+
end # module Plot
24+
end # module MagickWrapper
25+
end # module Rubyplot

spec/axes_spec.rb

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,100 @@
1313
]
1414
end
1515

16-
context "#dot!", focus: true do
16+
context "#stacked_bar!" do
17+
before do
18+
@temp_dir = SPEC_ROOT + "temp/stacked_bar"
19+
@fix_dir = SPEC_ROOT + "fixtures/stacked_bar"
20+
FileUtils.mkdir_p @temp_dir
21+
end
22+
23+
it "plots a single stacked bar graph with default colors" do
24+
fig = Rubyplot::Figure.new
25+
axes = fig.add_subplot 0,0
26+
axes.stacked_bar! do |p|
27+
p.data [25, 36, 86, 39]
28+
p.label = "moon"
29+
end
30+
axes.title = "net earnings in different months."
31+
axes.x_ticks = {
32+
0 => 'Jan',
33+
1 => 'Feb',
34+
2 => 'March',
35+
3 => 'April',
36+
4 => 'May',
37+
5 => 'June',
38+
6 => 'July',
39+
7 => 'August',
40+
8 => 'September',
41+
9 => 'October',
42+
10 => 'November',
43+
11 => 'December'
44+
}
45+
46+
file = "/#{Rubyplot.backend}_simple_stacked_bar.png"
47+
fig.write(@temp_dir + file)
48+
49+
#expect("temp/stacked_bar" + file).to eq_image("fixtures/stacked_bar" + file)
50+
end
51+
52+
it "plots multiple stacked bar graphs with default colors" do
53+
fig = Rubyplot::Figure.new
54+
axes = fig.add_subplot 0,0
55+
[
56+
["Charles", [20, 10, 5, 12, 11, 6, 10, 7]],
57+
["Adam", [5, 10, 20, 6, 9, 12, 14, 8]],
58+
["Daniel", [19, 9, 6, 11, 12, 7, 15, 8]]
59+
].each do |label, data|
60+
axes.stacked_bar! do |p|
61+
p.data data
62+
p.label = label
63+
end
64+
end
65+
axes.title = "net earnings in different months."
66+
axes.x_ticks = {
67+
0 => 'Jan',
68+
1 => 'Feb',
69+
2 => 'March',
70+
3 => 'April',
71+
4 => 'May',
72+
5 => 'June',
73+
6 => 'July',
74+
7 => 'August',
75+
8 => 'September',
76+
9 => 'October',
77+
10 => 'November',
78+
11 => 'December'
79+
}
80+
81+
file = "/#{Rubyplot.backend}_multiple_stacked_bar.png"
82+
fig.write(@temp_dir + file)
83+
84+
#expect("temp/stacked_bar" + file).to eq_image("fixtures/stacked_bar" + file)
85+
end
86+
87+
it "plots stacked bar in a small size" do
88+
fig = Rubyplot::Figure.new
89+
axes = fig.add_subplot 0,0
90+
[
91+
["Car", [25, 36, 86, 39]],
92+
["Bus", [80, 54, 67, 54]],
93+
["Train", [22, 29, 35, 38]]
94+
].each do |label, data|
95+
axes.stacked_bar!(400) do |p|
96+
p.data data
97+
p.label = label
98+
end
99+
end
100+
axes.title = "stacked bar."
101+
102+
file = "/#{Rubyplot.backend}_small_stacked_bar.png"
103+
fig.write(@temp_dir + file)
104+
105+
#expect("temp/stacked_bar" + file).to eq_image("fixtures/stacked_bar" + file)
106+
end
107+
end
108+
109+
context "#dot!" do
17110
before do
18111
@temp_dir = SPEC_ROOT + "temp/dot"
19112
@fix_dir = SPEC_ROOT + "fixtures/dot"
@@ -64,7 +157,7 @@
64157
3 => '5/30'
65158
}
66159

67-
file = "/#{Rubyplot.backend}_simple_dot.png"
160+
file = "/#{Rubyplot.backend}_multiple_dot.png"
68161
fig.write(@temp_dir + file)
69162

70163
#expect("temp/dot" + file).to eq_image("fixtures/dot" + file)

0 commit comments

Comments
 (0)