Skip to content

Commit 7001dfb

Browse files
committed
Add barplot! support
1 parent 00ff6bd commit 7001dfb

9 files changed

Lines changed: 159 additions & 27 deletions

File tree

lib/unicode_plot.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@
1414
require 'unicode_plot/barplot'
1515
require 'unicode_plot/boxplot'
1616
require 'unicode_plot/lineplot'
17+
require 'unicode_plot/histogram'

lib/unicode_plot/barplot.rb

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,19 @@ def n_columns
3434
@width
3535
end
3636

37+
def add_row!(bars)
38+
@bars.concat(bars)
39+
@max_freq, i = find_max(transform_values(@transform, bars))
40+
@max_len = @bars[i].to_s.length
41+
end
42+
3743
def print_row(out, row_index)
3844
check_row_index(row_index)
3945
bar = @bars[row_index]
4046
max_bar_width = [width - 2 - max_len, 1].max
4147
val = transform_values(@transform, bar)
4248
bar_len = max_freq > 0 ?
43-
([val, 0.0].max.fdiv(max_freq) * max_bar_width).round :
49+
([val, 0].max.fdiv(max_freq) * max_bar_width).round :
4450
0
4551
bar_str = max_freq > 0 ? @symbol * bar_len : ""
4652
bar_lbl = bar.to_s
@@ -81,7 +87,7 @@ def print_row(out, row_index)
8187
**kw)
8288
case args.length
8389
when 0
84-
data = Hash(args[0] || data)
90+
data = Hash(data)
8591
keys = data.keys.map(&:to_s)
8692
heights = data.values
8793
when 2
@@ -101,4 +107,34 @@ def print_row(out, row_index)
101107

102108
plot
103109
end
110+
111+
module_function def barplot!(plot,
112+
*args,
113+
data: nil,
114+
**kw)
115+
case args.length
116+
when 0
117+
data = Hash(data)
118+
keys = data.keys.map(&:to_s)
119+
heights = data.values
120+
when 2
121+
keys = Array(args[0])
122+
heights = Array(args[1])
123+
else
124+
raise ArgumentError, "invalid arguments"
125+
end
126+
127+
unless keys.length == heights.length
128+
raise ArgumentError, "The given vectors must be of the same length"
129+
end
130+
if keys.empty?
131+
raise ArgumentError, "Can't append empty array to barplot"
132+
end
133+
cur_idx = plot.n_rows
134+
plot.add_row!(heights)
135+
keys.each_with_index do |key, i|
136+
plot.annotate_row!(:l, cur_idx + i, key)
137+
end
138+
plot
139+
end
104140
end

lib/unicode_plot/renderer.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ def render_row(row)
124124
left_col = plot.colors_left.fetch(row, :light_black)
125125
right_str = plot.labels_right.fetch(row, "")
126126
right_col = plot.colors_right.fetch(row, :light_black)
127-
left_len = left_str.length
128-
right_len = right_str.length
127+
left_len = nocolor_string(left_str).length
128+
right_len = nocolor_string(right_str).length
129129

130130
unless color?(out)
131131
left_str = nocolor_string(left_str)
@@ -201,10 +201,10 @@ def init_render
201201

202202
# get length of largest strings to the left and right
203203
@max_len_l = plot.show_labels? && !plot.labels_left.empty? ?
204-
plot.labels_left.each_value.map {|l| l.to_s.length }.max :
204+
plot.labels_left.each_value.map {|l| nocolor_string(l).length }.max :
205205
0
206206
@max_len_r = plot.show_labels? && !plot.labels_right.empty? ?
207-
plot.labels_right.each_value.map {|l| l.to_s.length }.max :
207+
plot.labels_right.each_value.map {|l| nocolor_string(l).length }.max :
208208
0
209209
if plot.show_labels? && plot.ylabel_given?
210210
@max_len_l += plot.ylabel_length + 1

lib/unicode_plot/utils.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ def plotting_range_narrow(xmin, xmax)
2626
[xmin.to_f, xmax.to_f]
2727
end
2828

29+
def float_round_log10(x, m)
30+
if x == 0
31+
0.0
32+
elsif x > 0
33+
x.round(ceil_neg_log10(m) + 1).to_f
34+
else
35+
-(-x).round(ceil_neg_log10(m) + 1).to_f
36+
end
37+
end
38+
2939
def round_up_subtick(x, m)
3040
if x == 0
3141
0.0

test/fixtures/barplot/default2.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
 ┌ ┐
2+
bar ┤■■■■■■■■■ 23  
3+
foo ┤■■■■■■■■■■■■■■■ 37  
4+
zoom ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 90  
5+
 └ ┘
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
 ┌ ┐
2+
bar ┤■■■■■■■■■■■■■■■■■■■■■ 23.0  
3+
2.1 ┤■■■■■■■■■ 10  
4+
foo ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 37.0  
5+
 └ ┘

test/fixtures/barplot/ranges.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
 ┌ ┐
2+
2 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■ 11  
3+
3 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 12  
4+
4 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 13  
5+
5 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 14  
6+
6 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 15  
7+
 └ ┘

test/fixtures/barplot/ranges2.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
 ┌ ┐
2+
2 ┤■■■■■■■■■■■■■■■■■■■ 11  
3+
3 ┤■■■■■■■■■■■■■■■■■■■■■ 12  
4+
4 ┤■■■■■■■■■■■■■■■■■■■■■■ 13  
5+
5 ┤■■■■■■■■■■■■■■■■■■■■■■■■ 14  
6+
6 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■ 15  
7+
9 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 20  
8+
10 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 21  
9+
 └ ┘

test/test-barplot.rb

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,37 @@ class BarplotTest < Test::Unit::TestCase
33
include Helper::WithTerm
44

55
sub_test_case("UnicodePlot.barplot") do
6-
sub_test_case("print to tty") do
7-
test("the output is colored") do
8-
data = { bar: 23, foo: 37 }
9-
plot = UnicodePlot.barplot(data: data)
10-
_, output = with_term do
11-
plot.render($stdout)
12-
end
13-
assert_equal(fixture_path("barplot/default.txt").read,
14-
output)
15-
end
6+
test("colored") do
7+
data = { bar: 23, foo: 37 }
8+
plot = UnicodePlot.barplot(data: data)
9+
_, output = with_term { plot.render($stdout) }
10+
assert_equal(fixture_path("barplot/default.txt").read,
11+
output)
12+
13+
plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
14+
_, output = with_term { plot.render($stdout) }
15+
assert_equal(fixture_path("barplot/default.txt").read,
16+
output)
1617
end
1718

18-
sub_test_case("print to non-tty IO") do
19-
test("the output is not colored") do
20-
data = { bar: 23, foo: 37 }
21-
plot = UnicodePlot.barplot(data: data)
22-
output = StringIO.open do |sio|
23-
sio.print(plot)
24-
sio.close
25-
sio.string
26-
end
27-
assert_equal(fixture_path("barplot/nocolor.txt").read,
28-
output)
19+
test("not colored") do
20+
data = { bar: 23, foo: 37 }
21+
plot = UnicodePlot.barplot(data: data)
22+
output = StringIO.open do |sio|
23+
sio.print(plot)
24+
sio.close
25+
sio.string
2926
end
27+
assert_equal(fixture_path("barplot/nocolor.txt").read,
28+
output)
29+
end
30+
31+
test("mixed") do
32+
data = { bar: 23.0, 2.1 => 10, foo: 37.0 }
33+
plot = UnicodePlot.barplot(data: data)
34+
_, output = with_term { plot.render($stdout) }
35+
assert_equal(fixture_path("barplot/default_mixed.txt").read,
36+
output)
3037
end
3138

3239
sub_test_case("xscale: :log10") do
@@ -55,5 +62,57 @@ class BarplotTest < Test::Unit::TestCase
5562
output)
5663
end
5764
end
65+
66+
test("ranges") do
67+
plot = UnicodePlot.barplot(2..6, 11..15)
68+
_, output = with_term { plot.render($stdout) }
69+
assert_equal(fixture_path("barplot/ranges.txt").read,
70+
output)
71+
end
72+
end
73+
74+
sub_test_case("UnicodePlot.barplot!") do
75+
test("errors") do
76+
plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
77+
assert_raise(ArgumentError) do
78+
UnicodePlot.barplot!(plot, ["zoom"], [90, 80])
79+
end
80+
assert_raise(ArgumentError) do
81+
UnicodePlot.barplot!(plot, ["zoom", "boom"], [90])
82+
end
83+
UnicodePlot.barplot!(plot, "zoom", 90.1)
84+
end
85+
86+
test("return value") do
87+
plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
88+
assert_same(plot,
89+
UnicodePlot.barplot!(plot, ["zoom"], [90]))
90+
_, output = with_term { plot.render($stdout) }
91+
assert_equal(fixture_path("barplot/default2.txt").read,
92+
output)
93+
94+
plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
95+
assert_same(plot,
96+
UnicodePlot.barplot!(plot, "zoom", 90))
97+
_, output = with_term { plot.render($stdout) }
98+
assert_equal(fixture_path("barplot/default2.txt").read,
99+
output)
100+
101+
plot = UnicodePlot.barplot([:bar, :foo], [23, 37])
102+
assert_same(plot,
103+
UnicodePlot.barplot!(plot, data: { zoom: 90 }))
104+
_, output = with_term { plot.render($stdout) }
105+
assert_equal(fixture_path("barplot/default2.txt").read,
106+
output)
107+
end
108+
109+
test("ranges") do
110+
plot = UnicodePlot.barplot(2..6, 11..15)
111+
assert_same(plot,
112+
UnicodePlot.barplot!(plot, 9..10, 20..21))
113+
_, output = with_term { plot.render($stdout) }
114+
assert_equal(fixture_path("barplot/ranges2.txt").read,
115+
output)
116+
end
58117
end
59118
end

0 commit comments

Comments
 (0)