module UnicodePlot

Constants

BORDER_MAP
VERSION

Public Class Methods

border_types() click to toggle source
# File lib/unicode_plot/renderer.rb, line 43
def self.border_types
  BORDER_MAP.keys
end
canvas_types() click to toggle source
# File lib/unicode_plot/canvas.rb, line 167
def self.canvas_types
  Canvas::CANVAS_CLASS_MAP.keys
end
stemplot(*args, scale: 10, **kw) click to toggle source

Generates one or more {Stemplot} objects from the input data and prints a Single or Double stemplot using {stemplot1!} or {stemplot2!} @see Stemplot @example 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 |

@example 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 |
# File 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 ) click to toggle source

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.

@param plt [Stemplot] Stemplot object @param scale [Integer] Scale, should be a power of 10 @param divider [String] Divider character between stem and leaf @param padchar [String] Padding character @param trim [Boolean] Trim missing stems from the plot

# File 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 ) click to toggle source

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.

@param plt1 [Stemplot] Stemplot object for the left side @param plt2 [Stemplot] Stemplot object for the right side @param scale [Integer] Scale, should be a power of 10 @param divider [String] Divider character between stem and leaf @param padchar [String] Padding character @param trim [Boolean] Trim missing stems from the plot

# File 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

Public Instance Methods

barplot(*args, width: Plot::DEFAULT_WIDTH, color: Barplot::DEFAULT_COLOR, symbol: Barplot::DEFAULT_SYMBOL, border: :barplot, xscale: nil, xlabel: nil, data: nil, **kw) click to toggle source

@overload 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)

Draws a horizontal barplot.

@param text [Array<String>] The lables / captions of the bars.
@param heights [Array<Numeric>] The values / heights of the bars.
@param xscale [nil,:log,:ln,:log10,:lg,:log2,:lb,callable]
       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.
@param title
@param xlabel
@param ylabel
@param labels
@param border
@param margin
@param padding
@param color
@param width
@param symbol [String] Specifies the character that should be used
       to render the bars.

@return [Barplot] A plot object.

@example 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

@see Plot
@see histogram
@see Barplot

@overload barplot(data, **kwargs)

The different variation of barplot described above.

@param data [Hash] A hash in which the keys will be used as `text` and
       the values will be utilized as `heights`.
@param kwargs Optional keyword arguments same as ones described above.
@return [Barplot] A plot object.
# File lib/unicode_plot/barplot.rb, line 123
                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, *args, data: nil) click to toggle source

@overload barplot!(plot, text, heights)

Draw additional bars on the given existing plot.

@param plot [Barplot] the existing plot.
@return [Barplot] A plot object.

@see barplot

@overload barplot!(plot, data)

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

@param plot [Barplot] the existing plot.
@return [Barplot] A plot object.
# File lib/unicode_plot/barplot.rb, line 177
                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
boxplot(*args, data: nil, border: :corners, color: Boxplot::DEFAULT_COLOR, width: Plot::DEFAULT_WIDTH, xlim: [0, 0], **kw) click to toggle source
# File lib/unicode_plot/boxplot.rb, line 94
                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) click to toggle source
# File lib/unicode_plot/boxplot.rb, line 157
                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
compute_stair_lines(x, y, style: :post) click to toggle source
# File lib/unicode_plot/stairs.rb, line 63
                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) click to toggle source
# File lib/unicode_plot/densityplot.rb, line 2
                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) click to toggle source
# File lib/unicode_plot/densityplot.rb, line 7
                def densityplot!(plot, x, y, **kw)
  scatterplot!(plot, x, y, **kw)
end
histogram(x, nbins: nil, closed: :left, symbol: "▇", **kw) click to toggle source
# File lib/unicode_plot/histogram.rb, line 4
                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(*args, canvas: :braille, color: :auto, name: "", **kw) click to toggle source

@overload lineplot(, 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)

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.

@param x [Array<Numeric>] Optional. The horizontal position for each point. If omitted, the axes of `y` will be used as `x`.
@param y [Array<Numeric>] The vertical position for each point.
@param name [String] Annotation of the current drawing to be displayed on the right.
@param title
@param xlabel
@param ylabel
@param labels
@param border
@param margin
@param padding
@param color
@param width
@param height
@param xlim
@param ylim
@param canvas [Symbol] The type of canvas that should be used for drawing.
@param grid [true,false] If `true`, draws grid-lines at the origin.
@return [Lineplot] A plot object.
# File lib/unicode_plot/lineplot.rb, line 33
                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, *args, color: :auto, name: "") click to toggle source

@overload lineplot!(plot, [x], y, name: “”, color: :auto)

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

@param plot [Lineplot] The plot object.
@param x [Array<Numeric>] Optional. The horizontal position for each point. If omitted, the axes of `y` will be used as `x`.
@param y [Array<Numeric>] The vertical position for each point.
@param name [String] Annotation of the current drawing to be displayed on the right.
@param color
@return [Lineplot] The plot object same as the `plot` parameter.
# File lib/unicode_plot/lineplot.rb, line 80
                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) click to toggle source
# File lib/unicode_plot/scatterplot.rb, line 5
                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: "") click to toggle source
# File lib/unicode_plot/scatterplot.rb, line 27
                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(xvec, yvec, style: :post, **kw) click to toggle source

@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)

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.

@param x [Array<Numeric>] The horizontal position for each point.
@param y [Array<Numeric>] The vertical position for each point.
@param style [Symbol] Specifies where the transition of the stair takes place. Can be either `:pre` or `:post`.
@param name [String] Annotation of the current drawing to be displayed on the right.
@param height [Integer] Number of character rows that should be used for plotting.
@param xlim [Array<Numeric>] Plotting range for the x axis. `[0, 0]` stands for automatic.
@param ylim [Array<Numeric>] Plotting range for the y axis. `[0, 0]` stands for automatic.
@param canvas [Symbol] The type of canvas that should be used for drawing.
@param grid [Boolean] If `true`, draws grid-lines at the origin.

@return [Plot] A plot object.

@example 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

@see Plot
@see scatterplot
@see lineplot
# File lib/unicode_plot/stairs.rb, line 52
                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) click to toggle source

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

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

Private Instance Methods

stemplot(*args, scale: 10, **kw) click to toggle source

Generates one or more {Stemplot} objects from the input data and prints a Single or Double stemplot using {stemplot1!} or {stemplot2!} @see Stemplot @example 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 |

@example 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 |
# File 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 ) click to toggle source

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.

@param plt [Stemplot] Stemplot object @param scale [Integer] Scale, should be a power of 10 @param divider [String] Divider character between stem and leaf @param padchar [String] Padding character @param trim [Boolean] Trim missing stems from the plot

# File 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 ) click to toggle source

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.

@param plt1 [Stemplot] Stemplot object for the left side @param plt2 [Stemplot] Stemplot object for the right side @param scale [Integer] Scale, should be a power of 10 @param divider [String] Divider character between stem and leaf @param padchar [String] Padding character @param trim [Boolean] Trim missing stems from the plot

# File 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