Class: RVGP::Plot

Inherits:
Object
  • Object
show all
Defined in:
lib/rvgp/plot.rb,
lib/rvgp/plot/gnuplot.rb,
lib/rvgp/plot/google-drive/sheet.rb,
lib/rvgp/plot/google-drive/output_csv.rb,
lib/rvgp/plot/google-drive/output_google_sheets.rb

Overview

This class assembles grids into a series of plots, given a plot specification yaml. Once a grid is assembled, it’s dispatched to a driver (GoogleDrive or Gnuplot) for rendering. Here’s an example plot specification, included in the default project build as wealth-growth.yml, as created by the new_project command: “‘ title: “Wealth Growth (%year)” glob: “%year-wealth-growth.csv” grid_hacks:

store_cell: !!proc >
  (cell) ? cell.to_f.abs : nil

google:

chart_type: area
axis:
  left: "Amount"
  bottom: "Date"

gnuplot:

chart_type: area
domain: monthly
axis:
  left: "Amount"
  bottom: "Date"
additional_lines: |+
  set xtics scale 0 rotate by 45 offset -1.4,-1.4
  set key title ' '
  set style fill transparent solid 0.7 border

“‘

The yaml file is required to have :title and :glob parameters. Additionally, the following parameter groups are supported: :grid_hacks, :gnuplot, and :google.

The :gnuplot section of this file, is merged with the contents of default.yml, and passed to the Gnuplot constructor. See the Gnuplot::Plot#initialize method for more details on what parameters are supported in this section. NOTE: Depending on the kind of chart being specified, some initialize options are specific to the chart being built, and those options will be documented in the constructor for that specific chart. ie: Gnuplot::AreaChart#initialize or Gnuplot::ColumnAndLineChart#initialize.

The :google section of this file, is provided to GoogleDrive::Sheet#initialize. See this method for details on supported options.

The :grid_hacks section of this file, contains miscellaneous hacks to the dataset. These include: :keystone, :store_cell, :select_rows, :sort_rows_by, :sort_columns_by, :truncate_rows, :switch_rows_columns, and :truncate_columns. These options are documented in: Utilities::GridQuery#initialize and Utilities::GridQuery#to_grid.

Defined Under Namespace

Modules: Gnuplot, GoogleDrive Classes: InvalidYamlGlob, MissingYamlAttribute

Constant Summary collapse

REQUIRED_FIELDS =

The required keys, expected to exist in the plot yaml

%i[glob title].freeze
GNUPLOT_RESOURCES_PATH =

The path to rvgp’s ‘default’ include file search path. Any ‘!!include’ directives encountered in the plot yaml, will search this location for targets.

[RVGP::Gem.root, '/resources/gnuplot'].join

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Plot

Create a plot, from a specification yaml

Parameters:

  • path (String)

    The path to a specification yaml

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rvgp/plot.rb', line 97

def initialize(path)
  @path = path

  @yaml = RVGP::Utilities::Yaml.new path, [RVGP.app.config.project_path, GNUPLOT_RESOURCES_PATH]

  missing_attrs = REQUIRED_FIELDS.reject { |f| yaml.key? f }
  raise MissingYamlAttribute, yaml.path, missing_attrs unless missing_attrs.empty?

  @glob = yaml[:glob] if yaml.key? :glob
  raise InvalidYamlGlob, yaml.path unless /%\{year\}/.match glob

  grids_corpus = Dir[RVGP.app.config.build_path('grids/*')]

  @variants ||= self.class.glob_variants(glob, grids_corpus) +
                self.class.glob_variants(glob, grids_corpus, year: 'all')

  @title = yaml[:title] if yaml.key? :title
end

Instance Attribute Details

#globString (readonly)

A string containing wildcards, used to match input grids in the filesystem. This parameter is expected to be found inside the yaml, and will generally look something like: “%{year}-wealth-growth.csv” or “%{year}-property-incomes-%{property}.csv”. The variables which are supported, include ‘the year’ of a plot, as well as whatever variables are defined in a plot’s glob_variants (‘property’, as was the case above.) glob_variants are output by grids, and detected in the filenames those grids produce by the glob_variants method.

Returns:

  • (String)

    the current value of glob



63
64
65
# File 'lib/rvgp/plot.rb', line 63

def glob
  @glob
end

#pathString (readonly)

A path to the location of the input yaml, as provided to #initialize

Returns:

  • (String)

    the current value of path



63
64
65
# File 'lib/rvgp/plot.rb', line 63

def path
  @path
end

#yamlRVGP::Utilities::Yaml (readonly)

The yaml object, containing the parameters of this plot

Returns:



63
64
65
# File 'lib/rvgp/plot.rb', line 63

def yaml
  @yaml
end

Class Method Details

.all(plot_directory_path) ⇒ Array<RVGP::Plot>

Return all the plot objects, initialized from the yaml files in the plot_directory_path

Parameters:

  • plot_directory_path (String)

    A path to search, for (plot) yml files

Returns:

  • (Array<RVGP::Plot>)

    An array of the plots, available in the provided directory



275
276
277
# File 'lib/rvgp/plot.rb', line 275

def self.all(plot_directory_path)
  Dir.glob(format('%s/*.yml', plot_directory_path)).map { |path| new path }
end

.glob_variants(glob, corpus, pair_values = {}) ⇒ Array<Hash<Symbol,Object>>

This returns what plot variants are possible, given a glob, when matched against the provided file names. If pair_values contains key: value combinations, then, any of the returned variants will be sorted under the key:value provided . (Its really just meant for year: ‘all’, atm..)

Parameters:

  • glob (String)

    A string that matches ‘variables’ in the form of {variablename} specifiers

  • corpus (Array<String>)

    An array of file paths. The paths are matched against the glob, and separated based on the variables found, in their names.

Returns:

  • (Array<Hash<Symbol,Object>>)

    An array of Hashes, containing :name, :pairs, and :files components



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rvgp/plot.rb', line 244

def self.glob_variants(glob, corpus, pair_values = {})
  variant_names = glob.scan(/%\{([^ }]+)/).flatten.map(&:to_sym)

  glob_vars = variant_names.to_h { |key| [key, '(.+)'] }
  variant_matcher = Regexp.new format(glob, glob_vars)

  corpus.each_with_object([]) do |file, ret|
    matches = variant_matcher.match File.basename(file)

    if matches
      pairs = variant_names.map.with_index do |key, i|
        [key, pair_values.key?(key.to_sym) ? pair_values[key] : matches[i + 1]]
      end.to_h

      pair_i = ret.find_index { |variant| variant[:pairs] == pairs }
      if pair_i
        ret[pair_i][:files] << file
      else
        ret << { name: File.basename(glob % pairs, '.*'),
                 pairs: pairs,
                 files: [file] }
      end
    end

    ret
  end.compact
end

Instance Method Details

#column_titles(variant_name) ⇒ Array<String>

Return the column titles, on the plot for a given variant

Parameters:

  • variant_name (String)

    The :name of the variant you’re looking for

Returns:

  • (Array<String>)

    An array of strings, representing the column titles



188
189
190
# File 'lib/rvgp/plot.rb', line 188

def column_titles(variant_name)
  grid(variant_name)[0]
end

#gnuplot(name) ⇒ RVGP::Plot::Gnuplot::Plot

Return the gnuplot object, for a given variant

Parameters:

  • name (String)

    The :name of the variant you’re looking for

Returns:



208
209
210
211
# File 'lib/rvgp/plot.rb', line 208

def gnuplot(name)
  @gnuplots ||= {}
  @gnuplots[name] ||= RVGP::Plot::Gnuplot::Plot.new title(name), grid(name), gnuplot_options
end

#google_optionsHash

Return the google plot options, from the yaml of this plot.

Returns:

  • (Hash)

    The contents of the google: section of this plot’s yml



201
202
203
# File 'lib/rvgp/plot.rb', line 201

def google_options
  @google_options = yaml[:google] if yaml.key? :google
end

#grid(variant_name) ⇒ Array<Array<Object>>

Generate and return, a plot grid, for the given variant.

Parameters:

  • variant_name (String)

    The :name of the variant you’re looking for

Returns:

  • (Array<Array<Object>>)

    The grid, as an array of arrays.



152
153
154
155
156
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
# File 'lib/rvgp/plot.rb', line 152

def grid(variant_name)
  @grid ||= {}
  @grid[variant_name] ||= begin
    gopts = {}
    rvopts = {
      store_cell: if grid_hacks.key?(:store_cell)
                    ->(cell) { grid_hacks[:store_cell].call cell: cell }
                  else
                    ->(cell) { cell ? cell.to_f : nil }
                  end
    }

    # Grid Reader Options:
    rvopts[:keystone] = grid_hacks[:keystone] if grid_hacks.key? :keystone

    if grid_hacks.key? :select_rows
      rvopts[:select_rows] = ->(name, data) { grid_hacks[:select_rows].call name: name, data: data }
    end

    # to_grid Options
    gopts[:truncate_rows] = grid_hacks[:truncate_rows].to_i if grid_hacks.key? :truncate_rows
    gopts[:truncate_columns] = grid_hacks[:truncate_columns].to_i if grid_hacks.key? :truncate_columns
    gopts[:switch_rows_columns] = grid_hacks[:switch_rows_columns] if grid_hacks.key? :switch_rows_columns
    gopts[:sort_rows_by] = ->(row) { grid_hacks[:sort_rows_by].call row: row } if grid_hacks.key? :sort_rows_by

    if grid_hacks.key? :sort_columns_by
      gopts[:sort_cols_by] = ->(column) { grid_hacks[:sort_columns_by].call column: column }
    end

    RVGP::Utilities::GridQuery.new(variant_files(variant_name), rvopts).to_grid(gopts)
  end
end

#output_file(name, ext) ⇒ String

Generate an output file path, for the given variant. Typically this is a .csv grid, in the build/grids subdirectory of your project folder.

Parameters:

  • name (String)

    The :name of the variant you’re looking for

  • ext (String)

    The file extension you wish to append, to the return

Returns:

  • (String)

    The path to the output file, of the plot



145
146
147
# File 'lib/rvgp/plot.rb', line 145

def output_file(name, ext)
  RVGP.app.config.build_path format('plots/%<name>s.%<ext>s', name: name, ext: ext)
end

#script(name) ⇒ String

Return the rendered gnuplot code, for a given variant

Parameters:

  • name (String)

    The :name of the variant you’re looking for

Returns:

  • (String)

    The gnuplot code that represents this variant



216
217
218
# File 'lib/rvgp/plot.rb', line 216

def script(name)
  gnuplot(name).script
end

#series(variant_name) ⇒ Array<Array<Object>>

Return the portion of the grid, containing series labels, and their data.

Parameters:

  • variant_name (String)

    The :name of the variant you’re looking for

Returns:

  • (Array<Array<Object>>)

    The portion of the grid, that contains series data



195
196
197
# File 'lib/rvgp/plot.rb', line 195

def series(variant_name)
  grid(variant_name)[1..]
end

#show(name) ⇒ void

This method returns an undefined value.

Execute the rendered gnuplot code, for a given variant. Typically, this opens a gnuplot window.

Parameters:

  • name (String)

    The :name of the variant you’re looking for



224
225
226
# File 'lib/rvgp/plot.rb', line 224

def show(name)
  gnuplot(name).execute!
end

#title(variant_name) ⇒ String

The plot title, of the given variant

Parameters:

  • variant_name (String)

    The :name of the variant you’re looking to title

Returns:

  • (String)

    The title of the plot



136
137
138
# File 'lib/rvgp/plot.rb', line 136

def title(variant_name)
  @title % variants(variant_name)[:pairs]
end

#variant_files(variant_name) ⇒ Array<String>

This method returns only the :files parameter, of the #variants return.

Parameters:

  • variant_name (String)

    (nil) Limit the return to this variant, if set

Returns:

  • (Array<String>)

    An array of grid paths



129
130
131
# File 'lib/rvgp/plot.rb', line 129

def variant_files(variant_name)
  variants(variant_name)[:files]
end

#variants(name = nil) ⇒ Hash<Symbol, Object>

In the case that a name is provided, limit the return to the variant of the provided :name. If no name is provided, all variants in this plot are returned. Variants are determined by the yaml parameter :glob, as applied to the grids found in the build/grids/* path.

Parameters:

  • name (String) (defaults to: nil)

    (nil) Limit the return to this variant, if set

Returns:

  • (Hash<Symbol, Object>)

    The hash will return name, :pairs, and :files keys, that contain the variant details.



122
123
124
# File 'lib/rvgp/plot.rb', line 122

def variants(name = nil)
  name ? @variants.find { |v| v[:name] == name } : @variants
end

#write!(name) ⇒ void

This method returns an undefined value.

Write the gnuplot code, for a given variant, to the :output_file

Parameters:

  • name (String)

    The :name of the variant you’re looking for



231
232
233
# File 'lib/rvgp/plot.rb', line 231

def write!(name)
  File.write output_file(name, 'gpi'), gnuplot(name).script
end