unicode_plot 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/README.md +10 -12
- data/Rakefile +4 -1
- data/lib/unicode_plot.rb +3 -5
- data/lib/unicode_plot/barplot.rb +67 -2
- data/lib/unicode_plot/canvas.rb +18 -9
- data/lib/unicode_plot/{ascii_canvas.rb → canvas/ascii_canvas.rb} +4 -0
- data/lib/unicode_plot/canvas/block_canvas.rb +38 -0
- data/lib/unicode_plot/{braille_canvas.rb → canvas/braille_canvas.rb} +3 -1
- data/lib/unicode_plot/{density_canvas.rb → canvas/density_canvas.rb} +2 -0
- data/lib/unicode_plot/{dot_canvas.rb → canvas/dot_canvas.rb} +2 -0
- data/lib/unicode_plot/{lookup_canvas.rb → canvas/lookup_canvas.rb} +1 -1
- data/lib/unicode_plot/io_context.rb +32 -0
- data/lib/unicode_plot/lineplot.rb +36 -0
- data/lib/unicode_plot/plot.rb +8 -3
- data/lib/unicode_plot/renderer.rb +5 -1
- data/lib/unicode_plot/stairs.rb +88 -0
- data/lib/unicode_plot/stemplot.rb +344 -0
- data/lib/unicode_plot/styled_printer.rb +1 -5
- data/lib/unicode_plot/version.rb +1 -1
- data/test/helper/with_term.rb +16 -10
- data/test/test-barplot.rb +8 -0
- data/test/test-boxplot.rb +8 -0
- data/test/test-canvas.rb +15 -8
- data/test/test-densityplot.rb +8 -0
- data/test/test-histogram.rb +8 -0
- data/test/test-lineplot.rb +85 -1
- data/test/test-plot.rb +16 -0
- data/test/test-scatterplot.rb +8 -0
- data/test/test-stemplot.rb +95 -0
- data/test/test-utils.rb +13 -0
- data/unicode_plot.gemspec +4 -0
- metadata +40 -20
- data/lib/unicode_plot/layout.rb +0 -51
- data/test/test-result.rb +0 -0
@@ -40,6 +40,10 @@ module UnicodePlot
|
|
40
40
|
barplot: BorderMaps::BORDER_BARPLOT,
|
41
41
|
}.freeze
|
42
42
|
|
43
|
+
def self.border_types
|
44
|
+
BORDER_MAP.keys
|
45
|
+
end
|
46
|
+
|
43
47
|
module BorderPrinter
|
44
48
|
include StyledPrinter
|
45
49
|
|
@@ -128,7 +132,7 @@ module UnicodePlot
|
|
128
132
|
left_len = nocolor_string(left_str).length
|
129
133
|
right_len = nocolor_string(right_str).length
|
130
134
|
|
131
|
-
unless color?
|
135
|
+
unless out.color?
|
132
136
|
left_str = nocolor_string(left_str)
|
133
137
|
right_str = nocolor_string(right_str)
|
134
138
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module UnicodePlot
|
3
|
+
# @overload stairs(x, y, style: :post, name: "", title: "", xlabel: "", ylabel: "", labels: true, border: :solid, margin: 3, padding: 1, color: :auto, width: 40, height: 15, xlim: [0, 0], ylim: [0, 0], canvas: :braille, grid: true)
|
4
|
+
#
|
5
|
+
# Draws a staircase plot on a new canvas.
|
6
|
+
#
|
7
|
+
# The first vector `x` should contain the horizontal
|
8
|
+
# positions for all the points. The second vector `y` should then
|
9
|
+
# contain the corresponding vertical positions respectively. This
|
10
|
+
# means that the two vectors must be of the same length and
|
11
|
+
# ordering.
|
12
|
+
#
|
13
|
+
# @param x [Array<Numeric>] The horizontal position for each point.
|
14
|
+
# @param y [Array<Numeric>] The vertical position for each point.
|
15
|
+
# @param style [Symbol] Specifies where the transition of the stair takes place. Can be either `:pre` or `:post`.
|
16
|
+
# @param name [String] Annotation of the current drawing to be displayed on the right.
|
17
|
+
# @param height [Integer] Number of character rows that should be used for plotting.
|
18
|
+
# @param xlim [Array<Numeric>] Plotting range for the x axis. `[0, 0]` stands for automatic.
|
19
|
+
# @param ylim [Array<Numeric>] Plotting range for the y axis. `[0, 0]` stands for automatic.
|
20
|
+
# @param canvas [Symbol] The type of canvas that should be used for drawing.
|
21
|
+
# @param grid [Boolean] If `true`, draws grid-lines at the origin.
|
22
|
+
#
|
23
|
+
# @return [Plot] A plot object.
|
24
|
+
#
|
25
|
+
# @example Example usage of stairs on IRB:
|
26
|
+
#
|
27
|
+
# >> UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], style: :post, title: "My Staircase Plot").render
|
28
|
+
# My Staircase Plot
|
29
|
+
# ┌────────────────────────────────────────┐
|
30
|
+
# 7 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
31
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
32
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
33
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
34
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
35
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
36
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
|
37
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⡄⠀⠀⠀⠀⢸│
|
38
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
|
39
|
+
# │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
|
40
|
+
# │⠀⠀⠀⠀⠀⢸⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
|
41
|
+
# │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
|
42
|
+
# │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠧⠤⠤⠤⠤⠼│
|
43
|
+
# │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
|
44
|
+
# 1 │⣀⣀⣀⣀⣀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
|
45
|
+
# └────────────────────────────────────────┘
|
46
|
+
# 1 8
|
47
|
+
# => nil
|
48
|
+
#
|
49
|
+
# @see Plot
|
50
|
+
# @see scatterplot
|
51
|
+
# @see lineplot
|
52
|
+
module_function def stairs(xvec, yvec, style: :post, **kw)
|
53
|
+
x_vex, y_vex = compute_stair_lines(xvec, yvec, style: style)
|
54
|
+
lineplot(x_vex, y_vex, **kw)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Similar to stairs, but takes an existing plot object as a first argument.
|
58
|
+
module_function def stairs!(plot, xvec, yvec, style: :post, **kw)
|
59
|
+
x_vex, y_vex = compute_stair_lines(xvec, yvec, style: style)
|
60
|
+
lineplot!(plot, x_vex, y_vex, **kw)
|
61
|
+
end
|
62
|
+
|
63
|
+
module_function def compute_stair_lines(x, y, style: :post)
|
64
|
+
x_vex = Array.new(x.length * 2 - 1, 0)
|
65
|
+
y_vex = Array.new(x.length * 2 - 1, 0)
|
66
|
+
x_vex[0] = x[0]
|
67
|
+
y_vex[0] = y[0]
|
68
|
+
o = 0
|
69
|
+
if style == :post
|
70
|
+
(1 ... x.length).each do |i|
|
71
|
+
x_vex[i + o] = x[i]
|
72
|
+
x_vex[i + o + 1] = x[i]
|
73
|
+
y_vex[i + o] = y[i-1]
|
74
|
+
y_vex[i + o + 1] = y[i]
|
75
|
+
o += 1
|
76
|
+
end
|
77
|
+
elsif style == :pre
|
78
|
+
(1 ... x.length).each do |i|
|
79
|
+
x_vex[i + o] = x[i-1]
|
80
|
+
x_vex[i + o + 1] = x[i]
|
81
|
+
y_vex[i + o] = y[i]
|
82
|
+
y_vex[i + o + 1] = y[i]
|
83
|
+
o += 1
|
84
|
+
end
|
85
|
+
end
|
86
|
+
return [x_vex, y_vex]
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module UnicodePlot
|
4
|
+
|
5
|
+
# ## Description
|
6
|
+
#
|
7
|
+
# Draw a stem-leaf plot of the given vector +vec+.
|
8
|
+
#
|
9
|
+
# ```
|
10
|
+
# stemplot(vec, **kwargs)
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# Draw a back-to-back stem-leaf plot of the given vectors +vec1+ and +vec2+.
|
14
|
+
#
|
15
|
+
# ```
|
16
|
+
# stemplot(vec1, vec2, **kwargs)
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# The vectors can be any object that converts to an Array, e.g. an Array, Range, etc.
|
20
|
+
# If all elements of the vector are Numeric, the stem-leaf plot is classified as a
|
21
|
+
# {NumericStemplot}, otherwise it is classified as a {StringStemplot}. Back-to-back
|
22
|
+
# stem-leaf plots must be the same type, i.e. String and Numeric stem-leaf plots cannot
|
23
|
+
# be mixed in a back-to-back plot.
|
24
|
+
#
|
25
|
+
# ## Usage
|
26
|
+
#
|
27
|
+
# stemplot(vec, [vec2], scale:, divider:, padchar:, trim: )
|
28
|
+
#
|
29
|
+
# ## Arguments
|
30
|
+
#
|
31
|
+
# - +vec+: Vector for which the stem leaf plot should be computed.
|
32
|
+
# - +vec2+: Optional secondary vector, will be used to create a back-to-back stem-leaf plot.
|
33
|
+
# - +scale+: Set scale of plot. Default = 10. Scale is changed via orders of magnitude. Common values are 0.1, 1, and 10. For String stems, the default value of 10 is a one character stem, 100 is a two character stem.
|
34
|
+
# - +divider+: Character for break between stem and leaf. Default = "|"
|
35
|
+
# - +padchar+: Character(s) to separate stems, leaves and dividers. Default = " "
|
36
|
+
# - +trim+: Trims the stem labels when there are no leaves. This can be useful if your data is sparse. Default = +false+
|
37
|
+
# - +string_padchar+: Character used to replace missing position for input strings shorter than the stem-size. Default = "_"
|
38
|
+
#
|
39
|
+
# ## Result
|
40
|
+
# A plot of object type is sent to <tt>$stdout</tt>
|
41
|
+
#
|
42
|
+
# @example Examples using Numbers
|
43
|
+
# # Generate some numbers
|
44
|
+
# fifty_floats = 50.times.map { rand(-1000..1000)/350.0 }
|
45
|
+
# eighty_ints = 80.times.map { rand(1..100) }
|
46
|
+
# another_eighty_ints = 80.times.map { rand(1..100) }
|
47
|
+
# three_hundred_ints = 300.times.map { rand(-100..100) }
|
48
|
+
#
|
49
|
+
# # Single sided stem-plot
|
50
|
+
# UnicodePlot.stemplot(eighty_ints)
|
51
|
+
#
|
52
|
+
# # Single sided stem-plot with positive and negative values
|
53
|
+
# UnicodePlot.stemplot(three_hundred_ints)
|
54
|
+
#
|
55
|
+
# # Single sided stem-plot using floating point values, scaled
|
56
|
+
# UnicodePlot.stemplot(fifty_floats, scale: 1)
|
57
|
+
#
|
58
|
+
# # Single sided stem-plot using floating point values, scaled with new divider
|
59
|
+
# UnicodePlot.stemplot(fifty_floats, scale: 1, divider: "😄")
|
60
|
+
#
|
61
|
+
# # Back to back stem-plot
|
62
|
+
# UnicodePlot.stemplot(eighty_ints, another_eighty_ints)
|
63
|
+
#
|
64
|
+
# @example Examples using Strings
|
65
|
+
# # Generate some strings
|
66
|
+
# words_1 = %w[apple junk ant age bee bar baz dog egg a]
|
67
|
+
# words_2 = %w[ape flan can cat juice elf gnome child fruit]
|
68
|
+
#
|
69
|
+
# # Single sided stem-plot
|
70
|
+
# UnicodePlot.stemplot(words_1)
|
71
|
+
#
|
72
|
+
# # Back to back stem-plot
|
73
|
+
# UnicodePlot.stemplot(words_1, words_2)
|
74
|
+
#
|
75
|
+
# # Scaled stem plot using scale=100 (two letters for the stem) and trimmed stems
|
76
|
+
# UnicodePlot.stemplot(words_1, scale: 100, trim: true)
|
77
|
+
#
|
78
|
+
# # Above, but changing the string_padchar
|
79
|
+
# UnicodePlot.stemplot(words_1, scale: 100, trim: true, string_padchar: '?')
|
80
|
+
|
81
|
+
class Stemplot
|
82
|
+
|
83
|
+
# Use {factory} method -- should not be directly called.
|
84
|
+
def initialize(*_args, **_kw)
|
85
|
+
@stemleafs = {}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Factory method to create a Stemplot, creates either a NumericStemplot
|
89
|
+
# or StringStemplot depending on input.
|
90
|
+
#
|
91
|
+
# @param vector [Array] An array of elements to stem-leaf plot
|
92
|
+
# @return [NumericStemplot] If all elements are Numeric
|
93
|
+
# @return [StringStemplot] If any elements are not Numeric
|
94
|
+
def self.factory(vector, **kw)
|
95
|
+
vec = Array(vector)
|
96
|
+
if vec.all? { |item| item.is_a?(Numeric) }
|
97
|
+
NumericStemplot.new(vec, **kw)
|
98
|
+
else
|
99
|
+
StringStemplot.new(vec, **kw)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Insert a stem and leaf
|
104
|
+
def insert(stem, leaf)
|
105
|
+
@stemleafs[stem] ||= []
|
106
|
+
@stemleafs[stem] << leaf
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns an unsorted list of stems
|
110
|
+
# @return [Array] Unsorted list of stems
|
111
|
+
def raw_stems
|
112
|
+
@stemleafs.keys
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns a list of leaves for a given stem
|
116
|
+
# @param stem [Object] The stem
|
117
|
+
# @return [Array] Unsorted list of leaves
|
118
|
+
def leaves(stem)
|
119
|
+
@stemleafs[stem] || []
|
120
|
+
end
|
121
|
+
|
122
|
+
# Determines largest length of any stem
|
123
|
+
# @return [Integer] Length value
|
124
|
+
def max_stem_length
|
125
|
+
@stemleafs.values.map(&:length).max
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns a sorted list of stems
|
129
|
+
# @param all [Boolean] Return all stems if true, otherwise only return stems if a leaf exists for a stem
|
130
|
+
# @return [Array] Sorted list of stems
|
131
|
+
def stems(all: true)
|
132
|
+
self.class.sorted_stem_list(raw_stems, all: all)
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
class NumericStemplot < Stemplot
|
138
|
+
def initialize(vector, scale: 10, **kw)
|
139
|
+
super
|
140
|
+
Array(vector).each do |value|
|
141
|
+
fvalue = value.to_f.fdiv(scale/10.0)
|
142
|
+
stemnum = (fvalue/10).to_i
|
143
|
+
leafnum = (fvalue - (stemnum*10)).to_i
|
144
|
+
stemsign = value.negative? ? "-" : ''
|
145
|
+
stem = stemsign + stemnum.abs.to_s
|
146
|
+
leaf = leafnum.abs.to_s
|
147
|
+
self.insert(stem, leaf)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Print key to STDOUT
|
152
|
+
# @param scale [Integer] Scale, should be a power of 10
|
153
|
+
# @param divider [String] Divider character between stem and leaf
|
154
|
+
def print_key(scale, divider)
|
155
|
+
# First print the key
|
156
|
+
puts "Key: 1#{divider}0 = #{scale}"
|
157
|
+
# Description of where the decimal is
|
158
|
+
trunclog = Math.log10(scale).truncate
|
159
|
+
ndigits = trunclog.abs
|
160
|
+
right_or_left = (trunclog < 0) ? "left" : "right"
|
161
|
+
puts "The decimal is #{ndigits} digit(s) to the #{right_or_left} of #{divider}"
|
162
|
+
end
|
163
|
+
|
164
|
+
# Used when we have stems from a back-to-back stemplot and a combined list of stems is given
|
165
|
+
# @param stems [Array] Concatenated list of stems from two plots
|
166
|
+
# @param all [Boolean] Return all stems if true, otherwise only return stems if a leaf exists for a stem
|
167
|
+
# @return [Array] Sorted list of stems
|
168
|
+
def self.sorted_stem_list(stems, all: true)
|
169
|
+
negkeys, poskeys = stems.partition { |str| str[0] == '-'}
|
170
|
+
if all
|
171
|
+
negmin, negmax = negkeys.map(&:to_i).map(&:abs).minmax
|
172
|
+
posmin, posmax = poskeys.map(&:to_i).minmax
|
173
|
+
negrange = negmin ? (negmin..negmax).to_a.reverse.map { |s| "-"+s.to_s } : []
|
174
|
+
posrange = posmin ? (posmin..posmax).to_a.map(&:to_s) : []
|
175
|
+
return negrange + posrange
|
176
|
+
else
|
177
|
+
negkeys.sort! { |a,b| a.to_i <=> b.to_i }
|
178
|
+
poskeys.sort! { |a,b| a.to_i <=> b.to_i }
|
179
|
+
return negkeys + poskeys
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class StringStemplot < Stemplot
|
185
|
+
|
186
|
+
def initialize(vector, scale: 10, string_padchar: '_', **_kw)
|
187
|
+
super
|
188
|
+
stem_places = Math.log10(scale).floor
|
189
|
+
raise ArgumentError, "Cannot take fewer than 1 place from stem. Scale parameter should be greater than or equal to 10." if stem_places < 1
|
190
|
+
vector.each do |value|
|
191
|
+
# Strings may be shorter than the number of places we desire,
|
192
|
+
# so we will pad them with a string-pad-character.
|
193
|
+
padded_value = value.ljust(stem_places+1, string_padchar)
|
194
|
+
stem = padded_value[0...stem_places]
|
195
|
+
leaf = padded_value[stem_places]
|
196
|
+
self.insert(stem, leaf)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Function prototype to provide same interface as {NumericStemplot}.
|
201
|
+
# This function does not do anything.
|
202
|
+
# @return [false]
|
203
|
+
def print_key(_scale, _divider)
|
204
|
+
# intentionally empty
|
205
|
+
return false
|
206
|
+
end
|
207
|
+
|
208
|
+
# Used when we have stems from a back-to-back stemplot and a combined list of stems is given
|
209
|
+
# @param stems [Array] Concatenated list of stems from two plots
|
210
|
+
# @param all [Boolean] Return all stems if true, otherwise only return stems if a leaf exists for a stem
|
211
|
+
# @return [Array] Sorted list of stems
|
212
|
+
def self.sorted_stem_list(stems, all: true)
|
213
|
+
if all
|
214
|
+
rmin, rmax = stems.minmax
|
215
|
+
return (rmin .. rmax).to_a
|
216
|
+
else
|
217
|
+
stems.sort
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
# Print a Single-Vector stemplot to STDOUT.
|
224
|
+
#
|
225
|
+
# - Stem data is printed on the left.
|
226
|
+
# - Leaf data is printed on the right.
|
227
|
+
# - Key is printed at the bottom.
|
228
|
+
# @param plt [Stemplot] Stemplot object
|
229
|
+
# @param scale [Integer] Scale, should be a power of 10
|
230
|
+
# @param divider [String] Divider character between stem and leaf
|
231
|
+
# @param padchar [String] Padding character
|
232
|
+
# @param trim [Boolean] Trim missing stems from the plot
|
233
|
+
def stemplot1!(plt,
|
234
|
+
scale: 10,
|
235
|
+
divider: "|",
|
236
|
+
padchar: " ",
|
237
|
+
trim: false,
|
238
|
+
**_kw
|
239
|
+
)
|
240
|
+
|
241
|
+
stem_labels = plt.stems(all: !trim)
|
242
|
+
label_len = stem_labels.map(&:length).max
|
243
|
+
column_len = label_len + 1
|
244
|
+
|
245
|
+
stem_labels.each do |stem|
|
246
|
+
leaves = plt.leaves(stem).sort
|
247
|
+
stemlbl = stem.rjust(label_len, padchar).ljust(column_len, padchar)
|
248
|
+
puts stemlbl + divider + padchar + leaves.join
|
249
|
+
end
|
250
|
+
plt.print_key(scale, divider)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Print a Back-to-Back Stemplot to STDOUT
|
254
|
+
#
|
255
|
+
# - +plt1+ Leaf data is printed on the left.
|
256
|
+
# - Common stem data is printed in the center.
|
257
|
+
# - +plt2+ Leaf data is printed on the right.
|
258
|
+
# - Key is printed at the bottom.
|
259
|
+
# @param plt1 [Stemplot] Stemplot object for the left side
|
260
|
+
# @param plt2 [Stemplot] Stemplot object for the right side
|
261
|
+
# @param scale [Integer] Scale, should be a power of 10
|
262
|
+
# @param divider [String] Divider character between stem and leaf
|
263
|
+
# @param padchar [String] Padding character
|
264
|
+
# @param trim [Boolean] Trim missing stems from the plot
|
265
|
+
def stemplot2!(plt1, plt2,
|
266
|
+
scale: 10,
|
267
|
+
divider: "|",
|
268
|
+
padchar: " ",
|
269
|
+
trim: false,
|
270
|
+
**_kw
|
271
|
+
)
|
272
|
+
stem_labels = plt1.class.sorted_stem_list( (plt1.raw_stems + plt2.raw_stems).uniq, all: !trim )
|
273
|
+
label_len = stem_labels.map(&:length).max
|
274
|
+
column_len = label_len + 1
|
275
|
+
|
276
|
+
leftleaf_len = plt1.max_stem_length
|
277
|
+
|
278
|
+
stem_labels.each do |stem|
|
279
|
+
left_leaves = plt1.leaves(stem).sort.join('')
|
280
|
+
right_leaves = plt2.leaves(stem).sort.join('')
|
281
|
+
left_leaves_just = left_leaves.reverse.rjust(leftleaf_len, padchar)
|
282
|
+
stem = stem.rjust(column_len, padchar).ljust(column_len+1, padchar)
|
283
|
+
puts left_leaves_just + padchar + divider + stem + divider + padchar + right_leaves
|
284
|
+
end
|
285
|
+
|
286
|
+
plt1.print_key(scale, divider)
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
# Generates one or more {Stemplot} objects from the input data
|
291
|
+
# and prints a Single or Double stemplot using {stemplot1!} or {stemplot2!}
|
292
|
+
# @see Stemplot
|
293
|
+
# @example Single sided stemplot
|
294
|
+
# >> UnicodePlot.stemplot(eighty_ints)
|
295
|
+
# 0 | 257
|
296
|
+
# 1 | 00335679
|
297
|
+
# 2 | 034455899
|
298
|
+
# 3 | 145588
|
299
|
+
# 4 | 0022223
|
300
|
+
# 5 | 0223399
|
301
|
+
# 6 | 012345568889
|
302
|
+
# 7 | 01133334466777888
|
303
|
+
# 8 | 013689
|
304
|
+
# 9 | 22667
|
305
|
+
# Key: 1|0 = 10
|
306
|
+
# The decimal is 1 digit(s) to the right of |
|
307
|
+
#
|
308
|
+
# @example Back-to-back stemplot
|
309
|
+
# >> UnicodePlot.stemplot(eighty_ints, another_eighty_ints)
|
310
|
+
# 752 | 0 | 1244457899
|
311
|
+
# 97653300 | 1 | 4799
|
312
|
+
# 998554430 | 2 | 015668
|
313
|
+
# 885541 | 3 | 0144557888899
|
314
|
+
# 3222200 | 4 | 00268
|
315
|
+
# 9933220 | 5 | 0234778
|
316
|
+
# 988865543210 | 6 | 122222357889
|
317
|
+
# 88877766443333110 | 7 | 134556689
|
318
|
+
# 986310 | 8 | 24589
|
319
|
+
# 76622 | 9 | 022234468
|
320
|
+
# Key: 1|0 = 10
|
321
|
+
# The decimal is 1 digit(s) to the right of |
|
322
|
+
#
|
323
|
+
def stemplot(*args, scale: 10, **kw)
|
324
|
+
case args.length
|
325
|
+
when 1
|
326
|
+
# Stemplot object
|
327
|
+
plt = Stemplot.factory(args[0], scale: scale, **kw)
|
328
|
+
# Dispatch to plot routine
|
329
|
+
stemplot1!(plt, scale: scale, **kw)
|
330
|
+
when 2
|
331
|
+
# Stemplot object
|
332
|
+
plt1 = Stemplot.factory(args[0], scale: scale)
|
333
|
+
plt2 = Stemplot.factory(args[1], scale: scale)
|
334
|
+
raise ArgumentError, "Plot types must be the same for back-to-back stemplot " +
|
335
|
+
"#{plt1.class} != #{plt2.class}" unless plt1.class == plt2.class
|
336
|
+
# Dispatch to plot routine
|
337
|
+
stemplot2!(plt1, plt2, scale: scale, **kw)
|
338
|
+
else
|
339
|
+
raise ArgumentError, "Expecting one or two arguments"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
module_function :stemplot, :stemplot1!, :stemplot2!
|
344
|
+
end
|