Module: UnicodePlot

Defined in:
src/lib/unicode_plot/plot.rb,
src/lib/unicode_plot/utils.rb,
src/lib/unicode_plot/canvas.rb,
src/lib/unicode_plot/stairs.rb,
src/lib/unicode_plot/barplot.rb,
src/lib/unicode_plot/boxplot.rb,
src/lib/unicode_plot/version.rb,
src/lib/unicode_plot/lineplot.rb,
src/lib/unicode_plot/renderer.rb,
src/lib/unicode_plot/stemplot.rb,
src/lib/unicode_plot/grid_plot.rb,
src/lib/unicode_plot/histogram.rb,
src/lib/unicode_plot/io_context.rb,
src/lib/unicode_plot/densityplot.rb,
src/lib/unicode_plot/scatterplot.rb,
src/lib/unicode_plot/styled_printer.rb,
src/lib/unicode_plot/canvas/dot_canvas.rb,
src/lib/unicode_plot/value_transformer.rb,
src/lib/unicode_plot/canvas/ascii_canvas.rb,
src/lib/unicode_plot/canvas/block_canvas.rb,
src/lib/unicode_plot/canvas/lookup_canvas.rb,
src/lib/unicode_plot/canvas/braille_canvas.rb,
src/lib/unicode_plot/canvas/density_canvas.rb

Defined Under Namespace

Modules: BorderMaps, BorderPrinter, StyledPrinter, Utils, ValueTransformer, Version Classes: AsciiCanvas, Barplot, BlockCanvas, Boxplot, BrailleCanvas, Canvas, DensityCanvas, DotCanvas, GridPlot, IOContext, Lineplot, LookupCanvas, NumericStemplot, Plot, Renderer, Scatterplot, Stemplot, StringStemplot

Constant Summary collapse

VERSION =
"0.0.5"
BORDER_MAP =
{
  solid:   BorderMaps::BORDER_SOLID,
  corners: BorderMaps::BORDER_CORNERS,
  barplot: BorderMaps::BORDER_BARPLOT,
}.freeze

Class Method Summary collapse

Class Method Details

.barplot(text, heights, xscale: nil, title: nil, xlabel: nil, ylabel: nil, labels: true, border: :barplot, margin: Plot::DEFAULT_MARGIN, padding: Plot::DEFAULT_PADDING, color: Barplot::DEFAULT_COLOR, width: Plot::DEFAULT_WIDTH, symbol: Barplot::DEFAULT_SYMBOL) ⇒ Barplot .barplot(data, **kwargs) ⇒ Barplot

Overloads:

  • .barplot(text, heights, xscale: nil, title: nil, xlabel: nil, ylabel: nil, labels: true, border: :barplot, margin: Plot::DEFAULT_MARGIN, padding: Plot::DEFAULT_PADDING, color: Barplot::DEFAULT_COLOR, width: Plot::DEFAULT_WIDTH, symbol: Barplot::DEFAULT_SYMBOL) ⇒ Barplot

    Draws a horizontal barplot.

    Examples:

    Example usage of barplot on IRB:

    
    >> UnicodePlot.barplot(["Paris", "New York", "Moskau", "Madrid"],
                           [2.244, 8.406, 11.92, 3.165],
                           xlabel: "population [in mil]").render
                ┌                                        ┐
          Paris ┤■■■■■■ 2.244
       New York ┤■■■■■■■■■■■■■■■■■■■■■■■ 8.406
         Moskau ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 11.92
         Madrid ┤■■■■■■■■■ 3.165
                └                                        ┘
                           population [in mil]
    => nil

    Parameters:

    • text (Array<String>)

      The lables / captions of the bars.

    • heights (Array<Numeric>)

      The values / heights of the bars.

    • xscale (nil, :log, :ln, :log10, :lg, :log2, :lb, callable) (defaults to: nil)

      A function name symbol or callable object to transform the bar
      length before plotting. This effectively scales the x-axis
      without influencing the captions of the individual bars.
      e.g. use xscale: :log10 for logscale.

    • title (defaults to: nil)
    • xlabel (defaults to: nil)
    • ylabel (defaults to: nil)
    • labels (defaults to: true)
    • border (defaults to: :barplot)
    • margin (defaults to: Plot::DEFAULT_MARGIN)
    • padding (defaults to: Plot::DEFAULT_PADDING)
    • color (defaults to: Barplot::DEFAULT_COLOR)
    • width (defaults to: Plot::DEFAULT_WIDTH)
    • symbol (String) (defaults to: Barplot::DEFAULT_SYMBOL)

      Specifies the character that should be used
      to render the bars.

    Returns:

    See Also:

  • .barplot(data, **kwargs) ⇒ Barplot

    The different variation of barplot described above.

    Parameters:

    • data (Hash)

      A hash in which the keys will be used as text and
      the values will be utilized as heights.

    • kwargs

      Optional keyword arguments same as ones described above.

    Returns:



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'src/lib/unicode_plot/barplot.rb', line 123

module_function def barplot(*args,
                            width: Plot::DEFAULT_WIDTH,
                            color: Barplot::DEFAULT_COLOR,
                            symbol: Barplot::DEFAULT_SYMBOL,
                            border: :barplot,
                            xscale: nil,
                            xlabel: nil,
                            data: nil,
                            **kw)
  case args.length
  when 0
    data = Hash(data)
    keys = data.keys.map(&:to_s)
    heights = data.values
  when 2
    keys = Array(args[0])
    heights = Array(args[1])
  else
    raise ArgumentError, "invalid arguments"
  end

  unless keys.length == heights.length
    raise ArgumentError, "The given vectors must be of the same length"
  end
  unless heights.min >= 0
    raise ArgumentError, "All values have to be positive. Negative bars are not supported."
  end

  xlabel ||= ValueTransformer.transform_name(xscale)
  plot = Barplot.new(heights, width, color, symbol, xscale,
                     border: border, xlabel: xlabel,
                     **kw)
  keys.each_with_index do |key, i|
    plot.annotate_row!(:l, i, key)
  end

  plot
end

.barplot!(plot, text, heights) ⇒ Barplot .barplot!(plot, data) ⇒ Barplot

Overloads:

  • .barplot!(plot, text, heights) ⇒ Barplot

    Draw additional bars on the given existing plot.

    Parameters:

    • plot (Barplot)

      the existing plot.

    Returns:

    See Also:

  • .barplot!(plot, data) ⇒ Barplot

    The different variation of barplot! that takes the plotting data in a hash.

    Parameters:

    • plot (Barplot)

      the existing plot.

    Returns:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'src/lib/unicode_plot/barplot.rb', line 177

module_function def barplot!(plot,
                             *args,
                             data: nil)
  case args.length
  when 0
    data = Hash(data)
    keys = data.keys.map(&:to_s)
    heights = data.values
  when 2
    keys = Array(args[0])
    heights = Array(args[1])
  else
    raise ArgumentError, "invalid arguments"
  end

  unless keys.length == heights.length
    raise ArgumentError, "The given vectors must be of the same length"
  end
  if keys.empty?
    raise ArgumentError, "Can't append empty array to barplot"
  end

  cur_idx = plot.n_rows
  plot.add_row!(heights)
  keys.each_with_index do |key, i|
    plot.annotate_row!(:l, cur_idx + i, key)
  end
  plot
end

.border_typesObject



43
44
45
# File 'src/lib/unicode_plot/renderer.rb', line 43

def self.border_types
  BORDER_MAP.keys
end

.boxplot(*args, data: nil, border: :corners, color: Boxplot::DEFAULT_COLOR, width: Plot::DEFAULT_WIDTH, xlim: [0, 0], **kw) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'src/lib/unicode_plot/boxplot.rb', line 94

module_function def boxplot(*args,
                            data: nil,
                            border: :corners,
                            color: Boxplot::DEFAULT_COLOR,
                            width: Plot::DEFAULT_WIDTH,
                            xlim: [0, 0],
                            **kw)
  case args.length
  when 0
    data = Hash(data)
    text = data.keys
    data = data.values
  when 1
    data = args[0]
  when 2
    text = Array(args[0])
    data = args[1]
  else
    raise ArgumentError, "wrong number of arguments"
  end

  case data[0]
  when Numeric
    data = [data]
  when Array
    # do nothing
  else
    data = data.to_ary
  end
  text ||= Array.new(data.length, "")

  unless text.length == data.length
    raise ArgumentError, "wrong number of text"
  end

  unless xlim.length == 2
    raise ArgumentError, "xlim must be a length 2 array"
  end

  min_x, max_x = Utils.extend_limits(data.map(&:minmax).flatten, xlim)
  width = [width, Boxplot::MIN_WIDTH].max

  plot = Boxplot.new(data[0], width, color, min_x, max_x,
                     border: border, **kw)
  (1 ... data.length).each do |i|
    plot.add_series!(data[i])
  end

  mean_x = (min_x + max_x) / 2.0
  min_x_str  = (Utils.roundable?(min_x) ? min_x.round : min_x).to_s
  mean_x_str = (Utils.roundable?(mean_x) ? mean_x.round : mean_x).to_s
  max_x_str  = (Utils.roundable?(max_x) ? max_x.round : max_x).to_s
  plot.annotate!(:bl, min_x_str, color: :light_black)
  plot.annotate!(:b,  mean_x_str, color: :light_black)
  plot.annotate!(:br, max_x_str, color: :light_black)

  text.each_with_index do |name, i|
    plot.annotate_row!(:l, i*3+1, name) if name.length > 0
  end

  plot
end

.boxplot!(plot, *args, **kw) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'src/lib/unicode_plot/boxplot.rb', line 157

module_function def boxplot!(plot, *args, **kw)
  case args.length
  when 1
    data = args[0]
    name = kw[:name] || ""
  when 2
    name = args[0]
    data = args[1]
  else
    raise ArgumentError, "worng number of arguments"
  end

  if data.empty?
    raise ArgumentError, "Can't append empty array to boxplot"
  end

  plot.add_series!(data)

  plot.annotate_row!(:l, (plot.n_data - 1)*3+1, name) if name && name != ""

  min_x = plot.min_x
  max_x = plot.max_x
  mean_x = (min_x + max_x) / 2.0
  min_x_str  = (Utils.roundable?(min_x) ? min_x.round : min_x).to_s
  mean_x_str = (Utils.roundable?(mean_x) ? mean_x.round : mean_x).to_s
  max_x_str  = (Utils.roundable?(max_x) ? max_x.round : max_x).to_s
  plot.annotate!(:bl, min_x_str, color: :light_black)
  plot.annotate!(:b,  mean_x_str, color: :light_black)
  plot.annotate!(:br, max_x_str, color: :light_black)

  plot
end

.canvas_typesObject



167
168
169
# File 'src/lib/unicode_plot/canvas.rb', line 167

def self.canvas_types
  Canvas::CANVAS_CLASS_MAP.keys
end

.compute_stair_lines(x, y, style: :post) ⇒ Object



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
# File 'src/lib/unicode_plot/stairs.rb', line 63

module_function def compute_stair_lines(x, y, style: :post)
  x_vex = Array.new(x.length * 2 - 1, 0)
  y_vex = Array.new(x.length * 2 - 1, 0)
  x_vex[0] = x[0]
  y_vex[0] = y[0]
  o = 0
  if style == :post
    (1 ... x.length).each do |i|
      x_vex[i + o] = x[i]
      x_vex[i + o + 1] = x[i]
      y_vex[i + o] = y[i-1]
      y_vex[i + o + 1] = y[i]
      o += 1
    end
  elsif style == :pre
    (1 ... x.length).each do |i|
      x_vex[i + o] = x[i-1]
      x_vex[i + o + 1] = x[i]
      y_vex[i + o] = y[i]
      y_vex[i + o + 1] = y[i]
      o += 1
    end
  end
  return [x_vex, y_vex]
end

.densityplot(x, y, color: :auto, grid: false, name: "", **kw) ⇒ Object



2
3
4
5
# File 'src/lib/unicode_plot/densityplot.rb', line 2

module_function def densityplot(x, y, color: :auto, grid: false, name: "", **kw)
  plot = GridPlot.new(x, y, :density, grid: grid, **kw)
  scatterplot!(plot, x, y, color: color, name: name)
end

.densityplot!(plot, x, y, **kw) ⇒ Object



7
8
9
# File 'src/lib/unicode_plot/densityplot.rb', line 7

module_function def densityplot!(plot, x, y, **kw)
  scatterplot!(plot, x, y, **kw)
end

.histogram(x, nbins: nil, closed: :left, symbol: "▇", **kw) ⇒ Object



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
# File 'src/lib/unicode_plot/histogram.rb', line 4

module_function def histogram(x,
                              nbins: nil,
                              closed: :left,
                              symbol: "",
                              **kw)
  hist = x.histogram(*[nbins].compact, closed: closed)
  edge, counts = hist.edge, hist.weights
  labels = []
  bin_width = edge[1] - edge[0]
  pad_left, pad_right = 0, 0
  (0 ... edge.length).each do |i|
    val1 = Utils.float_round_log10(edge[i], bin_width)
    val2 = Utils.float_round_log10(val1 + bin_width, bin_width)
    a1 = val1.to_s.split('.', 2).map(&:length)
    a2 = val2.to_s.split('.', 2).map(&:length)
    pad_left  = [pad_left,  a1[0], a2[0]].max
    pad_right = [pad_right, a1[1], a2[1]].max
  end
  l_str = hist.closed == :right ? "(" : "["
  r_str = hist.closed == :right ? "]" : ")"
  counts.each_with_index do |n, i|
    val1 = Utils.float_round_log10(edge[i], bin_width)
    val2 = Utils.float_round_log10(val1 + bin_width, bin_width)
    a1 = val1.to_s.split('.', 2).map(&:length)
    a2 = val2.to_s.split('.', 2).map(&:length)
    labels[i] = "\e[90m#{l_str}\e[0m" +
                (" " * (pad_left - a1[0])) +
                val1.to_s +
                (" " * (pad_right - a1[1])) +
                "\e[90m, \e[0m" +
                (" " * (pad_left - a2[0])) +
                val2.to_s +
                (" " * (pad_right - a2[1])) +
                "\e[90m#{r_str}\e[0m"
  end
  xscale = kw.delete(:xscale)
  xlabel = kw.delete(:xlabel) ||
           ValueTransformer.transform_name(xscale, "Frequency")
  barplot(labels, counts,
          symbol: symbol,
          xscale: xscale,
          xlabel: xlabel,
          **kw)
end

.lineplot([x], y, name: "", canvas: :braille, title: "", xlabel: "", ylabel: "", labels: true, border: :solid, margin: Plot::DEFAULT_MARGIN, padding: Plot::DEFAULT_PADDING, color: :auto, width: Plot::DEFAULT_WIDTH, height: GridPlot::DEFAULT_HEIGHT, xlim: [0, 0], ylim: [0, 0], canvas: :braille, grid: true) ⇒ Lineplot

Draws a path through the given points on a new canvas.

The first (optional) array x should contain the horizontal positions for all the points along the path.
The second array y should then contain the corresponding vertical positions respectively.
This means that the two vectors must be of the same length and ordering.

Parameters:

  • x (Array<Numeric>)

    Optional. The horizontal position for each point. If omitted, the axes of y will be used as x.

  • y (Array<Numeric>)

    The vertical position for each point.

  • name (String) (defaults to: "")

    Annotation of the current drawing to be displayed on the right.

  • title (defaults to: "")
  • xlabel (defaults to: "")
  • ylabel (defaults to: "")
  • labels (defaults to: true)
  • border (defaults to: :solid)
  • margin (defaults to: Plot::DEFAULT_MARGIN)
  • padding (defaults to: Plot::DEFAULT_PADDING)
  • color (defaults to: :auto)
  • width (defaults to: Plot::DEFAULT_WIDTH)
  • height (defaults to: GridPlot::DEFAULT_HEIGHT)
  • xlim (defaults to: [0, 0])
  • ylim (defaults to: [0, 0])
  • canvas (Symbol) (defaults to: :braille)

    The type of canvas that should be used for drawing.

  • grid (true, false) (defaults to: true)

    If true, draws grid-lines at the origin.

Returns:



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
# File 'src/lib/unicode_plot/lineplot.rb', line 33

module_function def lineplot(*args,
                             canvas: :braille,
                             color: :auto,
                             name: "",
                             **kw)
  case args.length
  when 1
    # y only
    y = Array(args[0])
    x = Array(1 .. y.length)
  when 2
    # x and y
    x = Array(args[0])
    y = Array(args[1])
  else
    raise ArgumentError, "wrong number of arguments"
  end

  case x[0]
  when Time, Date
    if x[0].is_a? Time
      d = x.map(&:to_f)
    else
      origin = Date.new(1, 1, 1)
      d = x.map {|xi| xi - origin }
    end
    plot = lineplot(d, y, canvas: canvas, color: color, name: name, **kw)
    xmin, xmax = x.minmax
    plot.annotate!(:bl, xmin.to_s, color: :light_black)
    plot.annotate!(:br, xmax.to_s, color: :light_black)
    plot
  else
    plot = Lineplot.new(x, y, canvas, **kw)
    lineplot!(plot, x, y, color: color, name: name)
  end
end

.lineplot!(plot, [x], y, name: "", color: :auto) ⇒ Lineplot

Draws a path through the given points on the given canvas.

Parameters:

  • plot (Lineplot)

    The plot object.

  • x (Array<Numeric>)

    Optional. The horizontal position for each point. If omitted, the axes of y will be used as x.

  • y (Array<Numeric>)

    The vertical position for each point.

  • name (String) (defaults to: "")

    Annotation of the current drawing to be displayed on the right.

  • color (defaults to: :auto)

Returns:

  • (Lineplot)

    The plot object same as the plot parameter.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'src/lib/unicode_plot/lineplot.rb', line 80

module_function def lineplot!(plot,
                              *args,
                              color: :auto,
                              name: "")
  case args.length
  when 1
    # y only
    y = Array(args[0])
    x = Array(1 .. y.length)
  when 2
    # x and y
    x = Array(args[0])
    y = Array(args[1])

    if x.length == 1 && y.length == 1
      # intercept and slope
      intercept = x[0]
      slope = y[0]
      xmin = plot.origin_x
      xmax = plot.origin_x + plot.plot_width
      ymin = plot.origin_y
      ymax = plot.origin_y + plot.plot_height
      x = [xmin, xmax]
      y = [intercept + xmin*slope, intercept + xmax*slope]
    end
  else
    raise ArgumentError, "wrong number of arguments"
  end

  case x[0]
  when Time, Date
    if x[0].is_a? Time
      d = x.map(&:to_f)
    else
      origin = Date.new(1, 1, 1)
      d = x.map {|xi| xi - origin }
    end
    lineplot!(plot, d, y, color: color, name: name)
  else
    color = color == :auto ? plot.next_color : color
    plot.annotate!(:r, name.to_s, color: color) unless name.nil? || name == ""
    plot.lines!(x, y, color)
  end
  plot
end

.scatterplot(*args, canvas: :braille, color: :auto, name: "", **kw) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'src/lib/unicode_plot/scatterplot.rb', line 5

module_function def scatterplot(*args,
                                canvas: :braille,
                                color: :auto,
                                name: "",
                                **kw)
  case args.length
  when 1
    # y only
    y = Array(args[0])
    x = Array(1 .. y.length)
  when 2
    # x and y
    x = Array(args[0])
    y = Array(args[1])
  else
    raise ArgumentError, "worng number of arguments"
  end

  plot = Scatterplot.new(x, y, canvas, **kw)
  scatterplot!(plot, x, y, color: color, name: name)
end

.scatterplot!(plot, *args, color: :auto, name: "") ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'src/lib/unicode_plot/scatterplot.rb', line 27

module_function def scatterplot!(plot,
                                 *args,
                                 color: :auto,
                                 name: "")
  case args.length
  when 1
    # y only
    y = Array(args[0])
    x = Array(1 .. y.length)
  when 2
    # x and y
    x = Array(args[0])
    y = Array(args[1])
  else
    raise ArgumentError, "worng number of arguments"
  end

  color = color == :auto ? plot.next_color : color
  plot.annotate!(:r, name.to_s, color: color) unless name.nil? || name == ""
  plot.points!(x, y, color)
  plot
end

.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) ⇒ Plot

Draws a staircase plot on a new canvas.

The first vector x should contain the horizontal
positions for all the points. The second vector y should then
contain the corresponding vertical positions respectively. This
means that the two vectors must be of the same length and
ordering.

Examples:

Example usage of stairs on IRB:


>> UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], style: :post, title: "My Staircase Plot").render
                 My Staircase Plot
     ┌────────────────────────────────────────┐
   7 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⡄⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⢸⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢸│
     │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠧⠤⠤⠤⠤⠼│
     │⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
   1 │⣀⣀⣀⣀⣀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
     └────────────────────────────────────────┘
     1                                        8
=> nil

Parameters:

  • x (Array<Numeric>)

    The horizontal position for each point.

  • y (Array<Numeric>)

    The vertical position for each point.

  • style (Symbol) (defaults to: :post)

    Specifies where the transition of the stair takes place. Can be either :pre or :post.

  • name (String) (defaults to: "")

    Annotation of the current drawing to be displayed on the right.

  • height (Integer) (defaults to: 15)

    Number of character rows that should be used for plotting.

  • xlim (Array<Numeric>) (defaults to: [0, 0])

    Plotting range for the x axis. [0, 0] stands for automatic.

  • ylim (Array<Numeric>) (defaults to: [0, 0])

    Plotting range for the y axis. [0, 0] stands for automatic.

  • canvas (Symbol) (defaults to: :braille)

    The type of canvas that should be used for drawing.

  • grid (Boolean) (defaults to: true)

    If true, draws grid-lines at the origin.

Returns:

  • (Plot)

    A plot object.

See Also:



52
53
54
55
# File 'src/lib/unicode_plot/stairs.rb', line 52

module_function def stairs(xvec, yvec, style: :post, **kw)
  x_vex, y_vex = compute_stair_lines(xvec, yvec, style: style)
  lineplot(x_vex, y_vex, **kw)
end

.stairs!(plot, xvec, yvec, style: :post, **kw) ⇒ Object

Similar to stairs, but takes an existing plot object as a first argument.



58
59
60
61
# File 'src/lib/unicode_plot/stairs.rb', line 58

module_function def stairs!(plot, xvec, yvec, style: :post, **kw)
  x_vex, y_vex = compute_stair_lines(xvec, yvec, style: style)
  lineplot!(plot, x_vex, y_vex, **kw)
end

.stemplot(*args, scale: 10, **kw) ⇒ Object

Generates one or more Stemplot objects from the input data
and prints a Single or Double stemplot using stemplot1! or stemplot2!

Examples:

Single sided stemplot

>> UnicodePlot.stemplot(eighty_ints)
0 | 257
1 | 00335679
2 | 034455899
3 | 145588
4 | 0022223
5 | 0223399
6 | 012345568889
7 | 01133334466777888
8 | 013689
9 | 22667
Key: 1|0 = 10
The decimal is 1 digit(s) to the right of |

Back-to-back stemplot

>> UnicodePlot.stemplot(eighty_ints, another_eighty_ints)
              752 | 0 | 1244457899
         97653300 | 1 | 4799
        998554430 | 2 | 015668
           885541 | 3 | 0144557888899
          3222200 | 4 | 00268
          9933220 | 5 | 0234778
     988865543210 | 6 | 122222357889
88877766443333110 | 7 | 134556689
           986310 | 8 | 24589
            76622 | 9 | 022234468
Key: 1|0 = 10
The decimal is 1 digit(s) to the right of |

See Also:



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'src/lib/unicode_plot/stemplot.rb', line 323

def stemplot(*args, scale: 10, **kw)
  case args.length
  when 1
    # Stemplot object
    plt = Stemplot.factory(args[0], scale: scale, **kw)
    # Dispatch to plot routine
    stemplot1!(plt, scale: scale, **kw)
  when 2
    # Stemplot object
    plt1 = Stemplot.factory(args[0], scale: scale)
    plt2 = Stemplot.factory(args[1], scale: scale)
    raise ArgumentError, "Plot types must be the same for back-to-back stemplot " +
          "#{plt1.class} != #{plt2.class}" unless plt1.class == plt2.class
    # Dispatch to plot routine
    stemplot2!(plt1, plt2, scale: scale, **kw)
  else
    raise ArgumentError, "Expecting one or two arguments"
  end
end

.stemplot1!(plt, scale: 10, divider: "|", padchar: " ", trim: false, **_kw) ⇒ Object

Print a Single-Vector stemplot to STDOUT.

  • Stem data is printed on the left.
  • Leaf data is printed on the right.
  • Key is printed at the bottom.

Parameters:

  • plt (Stemplot)

    Stemplot object

  • scale (Integer) (defaults to: 10)

    Scale, should be a power of 10

  • divider (String) (defaults to: "|")

    Divider character between stem and leaf

  • padchar (String) (defaults to: " ")

    Padding character

  • trim (Boolean) (defaults to: false)

    Trim missing stems from the plot



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'src/lib/unicode_plot/stemplot.rb', line 233

def stemplot1!(plt, 
               scale: 10,
               divider: "|",
               padchar: " ",
               trim: false,
               **_kw
              )

  stem_labels = plt.stems(all: !trim)
  label_len = stem_labels.map(&:length).max
  column_len = label_len + 1
  
  stem_labels.each do |stem|
    leaves = plt.leaves(stem).sort
    stemlbl = stem.rjust(label_len, padchar).ljust(column_len, padchar)
    puts stemlbl + divider + padchar + leaves.join
  end
  plt.print_key(scale, divider)
end

.stemplot2!(plt1, plt2, scale: 10, divider: "|", padchar: " ", trim: false, **_kw) ⇒ Object

Print a Back-to-Back Stemplot to STDOUT

  • +plt1+ Leaf data is printed on the left.
  • Common stem data is printed in the center.
  • +plt2+ Leaf data is printed on the right.
  • Key is printed at the bottom.

Parameters:

  • plt1 (Stemplot)

    Stemplot object for the left side

  • plt2 (Stemplot)

    Stemplot object for the right side

  • scale (Integer) (defaults to: 10)

    Scale, should be a power of 10

  • divider (String) (defaults to: "|")

    Divider character between stem and leaf

  • padchar (String) (defaults to: " ")

    Padding character

  • trim (Boolean) (defaults to: false)

    Trim missing stems from the plot



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'src/lib/unicode_plot/stemplot.rb', line 265

def stemplot2!(plt1, plt2,
               scale: 10,
               divider: "|",
               padchar: " ",
               trim: false,
               **_kw
              )
  stem_labels = plt1.class.sorted_stem_list( (plt1.raw_stems + plt2.raw_stems).uniq, all: !trim )
  label_len = stem_labels.map(&:length).max
  column_len = label_len + 1

  leftleaf_len = plt1.max_stem_length

  stem_labels.each do |stem|
    left_leaves = plt1.leaves(stem).sort.join('')
    right_leaves = plt2.leaves(stem).sort.join('')
    left_leaves_just = left_leaves.reverse.rjust(leftleaf_len, padchar)
    stem = stem.rjust(column_len, padchar).ljust(column_len+1, padchar)
    puts left_leaves_just + padchar + divider + stem + divider + padchar + right_leaves
  end

  plt1.print_key(scale, divider)

end