Skip to content
Cecil Coupe edited this page Sep 24, 2016 · 57 revisions

Drawing a graph or a chart or a plot? Shoes 3.3.2 (beta r2649 or better) has a plot widget. It is not a replacement for programs like gnuplot, LibreOffice, OpenOffice, or other professional quality programs. Shoes has a limited set of options and they don't alway work as you might expect. You can always export your data to csv and use a better program to display the data.

Now that you know what to do when disappointed. Let's have fun!

Assume we have an array of numbers and we want to plot them.

Shoes.app width: 620, height: 500 do
  @values1 = [24, 22, 10, 13, 20, 8, 22]
  @grf = plot 600, 400, title: "My Graph", caption:  "my caption could be long", 
    font: "Helvetica"
  @grf.add num_obs: @values1.size, values: @values1,
       name: "foobar", minv: 6, maxv: 26
end

That is about as minimal as we can get. It looks like this: foo1

You'll notice that there are two parts. Creating the plot widget and saving it as @grf and adding the array to that @grf. You can create the widget with out adding a data series to display so in truth, you could use @grf = plot 600, 400, {} but that's boring and the fun is in the {options} which we'll get to soon after a brief (?) discussion about that add {}

You can graph multiple data series (up to 6). The {options} for add are important and not so obvious. I call them "data series" because there is more information needed than just a Ruby array of values. For example, what if you wanted your own x-axis labels instead of the generated 1,2,3...?

Shoes.app width: 620, height: 500 do
  @values1 = [24, 22, 10, 13, 20, 8, 22]
  @x_axis1 = ['a','b','c','d','e','f', 'g']
  @grf = plot 600, 400, title:"My Graph", caption:  "my caption could be long", 
    font: "Helvetica"
  @grf.add num_obs: @values1.size, values: @values1, xobs: @x_axis1,
       name: "foobar", minv: 6, maxv: 26
end

It's a major mistake if you don't have enough strings in the x observations (xobs: array) as you do in the value: array. Since xobs[] are strings they could be anything. Perhaps a Date or Time string? - up to you. You should have as many xobs[] and values[] as that num_obs: says. Yes, num_obs: is mandatory. Shoes will auto-size the data points (observations) to the range given. I could have used minv: @values1.min, maxv: @values1.max but specifying my own range is a very useful thing if your plot has two data series attached with very different scales.

Inside Shoes, each plot.add adds to an array so the first data series is 0 and the second is 1. Give the data series a name: "string". Mandatory. It displays in the legend part of the box.

The first data series added to the plot also controls what is displayed in the x-axis (there is only one of those) and will control the number of ticks marks and labels drawn on the left side and right side y-axis. Ticks? I don't see any ticks? Add auto_grid: true to the plot creation.

  @grf = plot 600, 400, title: "My Graph", caption:  "my caption could be long", 
    font: "Helvetica", auto_grid: true

foo2

Let's use many of the options for plot and add options with a program that creates two data series, one of which has a missing value (it's nil) and an xobs is nil too. foo-3

See gr1.rb

Now you can see why you have to give a name (a long_name is optional) to each data series. They are displayed in the legend and colored to match the data series. Look harder and you can see that a script can get the Shoes internal data series index from the name, and the number of series.

[Aug-29-2016]

Names and things could change. It's only a beta!. Try setting :missing to "min" or "max", the default is "skip" when creating the plot. One could also imagine that click, hover, and keydown Shoes events might return something useful.

Adding data points or chopping data points doesn't work unless you cover the window with a different window. You might imagine that is a big bug I want to fix soon. Correct.

I think it is possible add export/save methods like the svg widget has if you want a png/ps/pdf/svg file but there is more to do before that.

[Aug-30-2016]

There are two other options for the plot widget creation. Shoes doesn't know the pixel width of the 'typical' xobs string. By default it's somewhere around 6 characters but you do get situations where the auto_sizing needs help (depends on your data and strings, the size of the widget....). So { x_ticks: 4, y_ticks: 20} would only draw four vertical lines on the grid and 20 horizontal lines, for example. This also introduces it's own visual artifacts so you'll need to play with what works best for you.

[Sep-01-2016]

There is a beta to match at the Usual place.

There's more options! If you don't like the default colors, you can specify your own using the predefined names in Shoes (Manual->Shoes->Color) when you add a data series to the plot with the {color: "shoes_color_string"} option.

You can also set a wider line width for a data series with { strokewidth: small-int }. Anything less that one will be set to one, otherwise it wouldn't be visible.

plot-nubs

You might notice in the picture there is some sort of dot drawn at each data point in the picture above. That's option { nubs: boolean }. It only looks like a circle - it's really a wee small '+' or box. The nubs , even if specified will only show up if Shoes thinks it has enough space for the given number of data points (not that many - if width can handle 1/10 of the data - you can get nubs). A data series with lots of observations (values) will turn off the nub display. It just makes it slightly easier to visually find the point and figure out the data value.

Clever people would notice that if they set_first and and set_last that would zoom or unzoom or scroll left or right and if you get too zoomy you'll get the nubs. But only if you ask for them. Design goal?

[Sep-02-2016]

methods set_first and set_last change the display of data points. It does not change the size of values and xobs arrays. They change the display of every data series on the plot/graph. You use then to zoom in and scroll left or right. Expect a zoom method soon that sets both start and last.

redraw_to(index) is working, so lets mention why it exists and how to use it. It exists to append new data to the currently drawn data series - perhaps you've collected a data value in real time from a sensor or some web app. IT IS VERY IMPORTANT that you must append the data to the values array and [create] append and string for the xobs: array for all displayed data series BEFORE calling redraw_to.

Simply, redraw_to(index) sets the num_obs: of the plot and it also sets the plot's visual end_point (display) to the new position. If you're managing zooming or scrolling, you need to know that.

[Sep-04-2016]

Nearly done. I've added a plot.zoom(first, last) method which is the preferred method for zooming, unzooming, or horizontal scrolling around. You do have to manage the first and last indexes yourself. See this code (grtest.rb) with deals with a csv file with 1000's of data points and date. Performance is quite good.

require 'csv'


def load_test (filename, vals, obvs)
  hdr_name = ''
  CSV.foreach(filename, 'r') do |row|
      hdr_name = row[0]
      dts = row[1].to_str
      #y = dts[0..3].to_i
      #m = dts[4..5].to_i
      #d = dts[6..7].to_i
      #dt = DateTime.new(y,m,d,16,0,0,'-4')
      v = row[2].to_f
      obvs << "#{dts[2..3]}-#{dts[4..5]}-#{dts[6..7]}"
      vals << v
  end
  return hdr_name
end

Shoes.app width: 620, height: 610 do
  @vals = []
  @obvs = []
  @name = ''
  zooming = false;
  zoom_beg = 0
  zoom_end = 0;
  stack do
    flow do 
      button "quit" do Shoes.quit end
      button "load csv..." do 
        #short_name = load_test "/home/ccoupe/Projects/JModel-1.4/ruby/tstest.csv",  @vals, @obvs
        filename = ask_open_file
        if filename 
          short_name = load_test filename, @vals, @obvs
          para "loaded #{@vals.size}"
          @grf.add  num_obs: @vals.size, values: @vals, maxv: @vals.max * 1.01,
              minv: @vals.min, name: short_name, xobs: @obvs, nubs: :true
          zoom_end = @vals.size
        end
      end
      button "in %25" do
       range = zoom_end - zoom_beg
       zoom_beg = (zoom_beg + (range * 0.125)).to_i
       zoom_end = (zoom_end - (range * 0.125)).to_i
       @grf.zoom zoom_beg, zoom_end
      end
      button "out %25" do
        range = zoom_end - zoom_beg
        zoom_beg = (zoom_beg - (range * 0.125)).to_i
        zoom_end = (zoom_end + (range * 0.125)).to_i
        @grf.zoom zoom_beg, zoom_end
      end
      button "reset" do
        zoom_beg = 0
        zoom_end = @vals.size
        @grf.zoom zoom_beg, zoom_end
      end
    end
    @grf = plot 600, 400,  title: "Test - ^SPX ", caption: "1993 Jan 4 to May 25",
      x_ticks: 8, y_ticks: 10,  auto_grid: true
  end
end

In this example I want to zoom in (25%) by upping the start index 12.5% and reducing the end index by 12.5%. zoom out just reverses that. NOTE: 12.5% is going to be result in some rounding fun depending on the length of the data. You'll learn to ignore that because it's just not worth your effort. Then again, it will be your code and your data so go for it. The zoom method does have one bounds checks - you can't zoom any smaller than 3 data points

One other recent enhancement is the plot.save_as method

      button "save as" do
        file = ask_save_file
        @grf.save_as file if file
      end
```
Allows you to save the plot (aka) graph as a png, pdf, ps or svg. Just make sure the filename extension is one of those four lower case, strings following the last `.` - it does parse the extension. The vector formats like .pdf are a lot of fun because you can stretch them or shrink them without pixel jaggies. Impressive. There might be a performance or file size issue with a 6 data series of 5000 data points each drawn on the screen. They draw just fine, but I haven't tried saving that to a vector fix because I can't fix whatever might be wrong with the file.

**[Sep 17, 2016]**

Lots of options have been added since the last update!!

You can fill the background with a Shoes color name. You can specify the color name for a data series. While the
default colors can be used, you probably want one from the Shoes pallet.

In the option hash, you can specify `chart: "column"` (default is `chart: "line"`). Here's two plot widgets
[source: gr3.rb](https://github.com/Shoes3/shoes3/blob/master/Tests/plot/gr3.rb):
![bar-graph](https://cloud.githubusercontent.com/assets/222691/18612528/827e11b0-7d19-11e6-9609-f041c99b1931.png)
Looking at the code, it draws the same data into two different plots widgets, one line chart and one column chart. 
If you're paying attention, you'll notice there is no boundary box drawn around the column chart (aka plot). It's an option `{boundary_box: boolean}`, default is `:true". 

A note or two about column graphs: It would be _your mistake_ to cram too many bars in a small space. strokewidth: is how you specify the width of column. Refer to that Shoes code link. If a column value matches the given minimum value, there is no column to draw so set your min and max by hand. Just a friendly warning if you think it's not displaying a value. Been there, done that.

[gr2.rb](https://github.com/Shoes3/shoes3/blob/master/Tests/plot/gr2.rb) shows how Shoes line chart can handle missing data points and missing observation labels. **Top Tip:** Fix your data and don't depend on Shoes drawing what you think it should do if you data is wrong.

**[Sep-19-2016]**

The nubs: hash argument can now take a string argument as well as a :true, :fail", or nil. The string argument is one of "dot", "circle", "box", or "rect". Dot and box are filled with series color and Circle and Box are hollow so the background shows inside. The size of of the dot/circle/box/rect is related to or controlled by the strokewidth: setting and they only become visible if Shoes thinks the chart is "not too busy".

**[Sep-23-2016]**
Many changes. 

* added chart: "scatter"
* added chart: "timeseries"

Menu

In This Section:

Clone this wiki locally