youplot 0.3.5 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bfd3292028fb88c3a5c902884d6c3dd17f59292eb4980b09a75396c921cb46f
4
- data.tar.gz: '00524329ddcea39543d4a064e5f930c417544f86a853f538823eb6ece37ac255'
3
+ metadata.gz: 83145e216c0d124d537c8a01d63f9203730a74191fefcb9de5743b08e0e504bd
4
+ data.tar.gz: a246eab22f11ab8c526cc838d9f2fbf7d249721b4782287087c08a5410f8647b
5
5
  SHA512:
6
- metadata.gz: 3b37389aab9904bc7129397a8c04b6a1d6e244bc1af986f57d21131eec55cf7051308a7eabc2e81d17e8e4838d45d9c5d6cc65169375329e9884726a2ea49408
7
- data.tar.gz: 6a1c8605980520e8d0bae43b0302d6e07d5511e6bf85d0fd3def1c6c994ed5e1375a076af69ef843b2a1a0f5e7bbbb877148ef03bf833a734c53c1115e59c0fd
6
+ metadata.gz: d9138c26fade36d7d0ca19100b47dcb8e65c8c884d13b74d258dcae9ea304ced2fc783b08e871d76607e57e394e6b861c81f1f5030caba53493b2f4740a05cd2
7
+ data.tar.gz: f293d686791ba1481948877e534c9085e99c24ab9c36026979002ecaa9ee2ece6f12ad7193cda936c62e472e2f94008466cc666e9405157bfcc348c4c21c4579
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <img src="logo.svg" width="60%" height="60%" />
3
- </7>
3
+ </p>
4
4
 
5
5
  ![Build Status](https://github.com/kojix2/youplot/workflows/test/badge.svg)
6
6
  [![Gem Version](https://badge.fury.io/rb/youplot.svg)](https://badge.fury.io/rb/youplot)
@@ -106,8 +106,8 @@ In this example, YouPlot counts the number of chromosomes where the gene is loca
106
106
 
107
107
  ```sh
108
108
  cat gencode.v35.annotation.gff3 \
109
- | grep -v '#' | grep 'gene' | cut -f1 | \
110
- uplot count -t "The number of human gene annotations per chromosome" -c blue
109
+ | grep -v '#' | grep 'gene' | cut -f1 \
110
+ | uplot count -t "The number of human gene annotations per chromosome" -c blue
111
111
  ```
112
112
 
113
113
  <p align="center">
@@ -119,7 +119,7 @@ This is fine in most cases, as long as the data size is small. If you want to vi
119
119
 
120
120
  ```sh
121
121
  cat gencode.v35.annotation.gff3 | grep -v '#' | grep 'gene' | cut -f1 \
122
- |sort | uniq -c | sort -nrk2 | awk '{print $2,$1}' \
122
+ | sort | uniq -c | sort -nrk2 | awk '{print $2,$1}' \
123
123
  | uplot bar -d ' ' -t "The number of human gene annotations per chromosome" -c blue
124
124
  ```
125
125
 
@@ -130,15 +130,15 @@ cat gencode.v35.annotation.gff3 | grep -v '#' | grep 'gene' | cut -f1 \
130
130
  Wouldn't it be a pain to have to run R, Python, Julia, gnuplot or whatever REPL just to check your data?
131
131
  YouPlot is a command line tool for this purpose. With YouPlot, you can continue working without leaving your terminal and shell.
132
132
 
133
- ### how to use YouPlot?
133
+ ### How to use YouPlot?
134
134
 
135
135
  `uplot` is the shortened form of `youplot`. You can use either.
136
136
 
137
- | | |
138
- |-----------------------------------|------------------------------------------------|
139
- | Reads data from standard input | `cat data.tsv \| uplot <command> [options]` |
140
- | Reads data from files | `uplot <command> [options] data.tsv ...` |
141
- | Outputs data from stdin to stdout | `pipeline1 \| uplot <command> -O \| pipeline2` |
137
+ | Command | Description |
138
+ |------------------------------------------------|-----------------------------------|
139
+ | `cat data.tsv \| uplot <command> [options]` | Take input from stdin |
140
+ | `uplot <command> [options] data.tsv ...` | Take input from files |
141
+ | `pipeline1 \| uplot <command> -O \| pipeline2` | Outputs data from stdin to stdout |
142
142
 
143
143
  ### Where to output the plot?
144
144
 
@@ -148,7 +148,8 @@ The output file or stream for the plot can be specified with the `-o` option.
148
148
  ### Where to output the input data?
149
149
 
150
150
  By default, the input data is not shown anywhere.
151
- The `-O` option, with no arguments, outputs the input data directly to the standard output. This is useful when passing data to a subsequent pipeline.
151
+ The `-O` option, with no arguments, outputs the input data directly to the standard output.
152
+ This is useful when passing data to a subsequent pipeline.
152
153
 
153
154
  ### What types of plots are available?
154
155
 
@@ -176,11 +177,21 @@ If your input data contains a header line, you need to specify the `-H` option.
176
177
 
177
178
  ### How to specify the delimiter?
178
179
 
179
- Use the `-d` option. To specify a blank space, you can use `uplot bar -d ' ' data.txt`. You do not need to use `-d` option for tab-delimited text since the default value is tab.
180
+ Use the `-d` option. To specify a blank space, you can use `uplot bar -d ' ' data.txt`.
181
+ You do not need to use `-d` option for tab-delimited text since the default value is tab.
180
182
 
181
183
  ### Is there a way to specify a column as the x-axis or y-axis?
182
184
 
183
- Not yet. In principle, YouPlot treats the first column as the X axis and the second column as the Y axis. When working with multiple series, the first row is the X axis, the second row is series 1, the third row is series 2, and so on. If you pass only one column of data for `line` and `bar`, YouPlot will automatically use a sequential number starting from 1 as the X-axis. The `--fmt xyy`, `--fmt xyxy` and `--fmt yx` options give you a few more choices. See `youplot <command> --help` for more details. YouPlot has limited functionalities, but you can use shell scripts such as `awk '{print $2, $1}'` to swap lines.
185
+ Not yet.
186
+ YouPlot treats the first column as the X axis and the second column as the Y axis.
187
+ When working with multiple series, the first column is the X axis, the second column is series Y1, the third column is series Y2, and so on.
188
+ If you pass only one column of data for `line` and `bar`, YouPlot will automatically use a sequential number starting from 1 as the X-axis.
189
+
190
+ * `--fmt xyy` `--fmt xyxy` `--fmt yx` options give you a few more choices.
191
+ See `youplot <command> --help` for more details.
192
+
193
+ * Use `awk '{print $2, $1}'` to swap lines.
194
+ * Use `paste` to concatenate series.
184
195
 
185
196
  ### How to plot real-time data?
186
197
 
@@ -216,14 +227,15 @@ uplot colors
216
227
 
217
228
  ## Contributing
218
229
 
219
- YouPlot is a library under development, so even small improvements like typofix are welcome! Please feel free to send us your pull requests.
230
+ YouPlot is a library under development, so even small improvements like typofix are welcome!
231
+ Please feel free to send us your pull requests.
220
232
 
221
233
  * [Report bugs](https://github.com/kojix2/youplot/issues)
222
234
  * Fix bugs and [submit pull requests](https://github.com/kojix2/youplot/pulls)
223
235
  * Write, clarify, or fix documentation
224
236
  * English corrections by native speakers are welcome.
225
237
  * Suggest or add new features
226
-
238
+ * Make a donation
227
239
 
228
240
  ### Development
229
241
 
data/lib/youplot.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'unicode_plot'
4
- require 'youplot/version'
5
- require 'youplot/dsv'
6
- require 'youplot/command'
3
+ require_relative 'youplot/version'
4
+ require_relative 'youplot/dsv'
5
+ require_relative 'youplot/parameters'
6
+ require_relative 'youplot/command'
7
7
 
8
8
  module YouPlot
9
9
  class << self
@@ -11,12 +11,16 @@ module YouPlot
11
11
  if tally && Enumerable.method_defined?(:tally)
12
12
  arr.tally
13
13
  else
14
- # https://github.com/marcandre/backports
15
- arr.each_with_object(Hash.new(0)) { |item, res| res[item] += 1 }
16
- .tap { |h| h.default = nil }
14
+ # value_counts Enumerable::Statistics
15
+ arr.value_counts(dropna: false)
17
16
  end
18
- .sort { |a, b| a[1] <=> b[1] }
19
- .reverse
17
+ .sort do |a, b|
18
+ # compare values
19
+ r = b[1] <=> a[1]
20
+ # If the values are the same, compare by name
21
+ r = a[0] <=> b[0] if r == 0
22
+ r
23
+ end
20
24
  .transpose
21
25
  end
22
26
  end
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # UnicodePlot - Plot your data by Unicode characters
4
+ # https://github.com/red-data-tools/unicode_plot.rb
5
+
3
6
  require_relative 'processing'
4
7
  require 'unicode_plot'
5
8
 
6
9
  module YouPlot
7
10
  # plotting functions.
8
11
  module Backends
9
- module UnicodePlotBackend
12
+ module UnicodePlot
10
13
  class Error < StandardError; end
11
14
 
12
15
  module_function
@@ -39,7 +42,7 @@ module YouPlot
39
42
  labels = series[x_col]
40
43
  values = series[y_col].map(&:to_f)
41
44
  end
42
- UnicodePlot.barplot(labels, values, **params.to_hc)
45
+ ::UnicodePlot.barplot(labels, values, **params.to_hc)
43
46
  end
44
47
 
45
48
  def histogram(data, params)
@@ -47,7 +50,7 @@ module YouPlot
47
50
  series = data.series
48
51
  params.title ||= data.headers[0] if headers
49
52
  values = series[0].map(&:to_f)
50
- UnicodePlot.histogram(values, **params.to_hc)
53
+ ::UnicodePlot.histogram(values, **params.to_hc)
51
54
  end
52
55
 
53
56
  def line(data, params, fmt = nil)
@@ -57,7 +60,7 @@ module YouPlot
57
60
  # If there is only one series, it is assumed to be sequential data.
58
61
  params.ylabel ||= headers[0] if headers
59
62
  y = series[0].map(&:to_f)
60
- UnicodePlot.lineplot(y, **params.to_hc)
63
+ ::UnicodePlot.lineplot(y, **params.to_hc)
61
64
  else
62
65
  # If there are 2 or more series...
63
66
  if fmt == 'yx'
@@ -75,7 +78,7 @@ module YouPlot
75
78
  end
76
79
  x = series[x_col].map(&:to_f)
77
80
  y = series[y_col].map(&:to_f)
78
- UnicodePlot.lineplot(x, y, **params.to_hc)
81
+ ::UnicodePlot.lineplot(x, y, **params.to_hc)
79
82
  end
80
83
  end
81
84
 
@@ -94,9 +97,9 @@ module YouPlot
94
97
  end
95
98
  params.xlim ||= series[0].flatten.minmax # why need?
96
99
  params.ylim ||= series[1..-1].flatten.minmax # why need?
97
- plot = UnicodePlot.public_send(method1, series[0], series[1], **params.to_hc)
100
+ plot = ::UnicodePlot.public_send(method1, series[0], series[1], **params.to_hc)
98
101
  2.upto(series.size - 1) do |i|
99
- UnicodePlot.public_send(method2, plot, series[0], series[i], name: headers&.[](i))
102
+ ::UnicodePlot.public_send(method2, plot, series[0], series[i], name: headers&.[](i))
100
103
  end
101
104
  plot
102
105
  end
@@ -112,9 +115,9 @@ module YouPlot
112
115
  params.xlim ||= series2.map(&:first).flatten.minmax # why need?
113
116
  params.ylim ||= series2.map(&:last).flatten.minmax # why need?
114
117
  x1, y1 = series2.shift
115
- plot = UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
118
+ plot = ::UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
116
119
  series2.each_with_index do |(xi, yi), i|
117
- UnicodePlot.public_send(method2, plot, xi, yi, name: headers&.[]((i + 1) * 2))
120
+ ::UnicodePlot.public_send(method2, plot, xi, yi, name: headers&.[]((i + 1) * 2))
118
121
  end
119
122
  plot
120
123
  end
@@ -152,13 +155,13 @@ module YouPlot
152
155
  series = data.series
153
156
  headers ||= (1..series.size).map(&:to_s)
154
157
  series.map! { |s| s.map(&:to_f) }
155
- UnicodePlot.boxplot(headers, series, **params.to_hc)
158
+ ::UnicodePlot.boxplot(headers, series, **params.to_hc)
156
159
  end
157
160
 
158
161
  def colors(color_names = false)
159
162
  # FIXME
160
163
  s = String.new
161
- UnicodePlot::StyledPrinter::TEXT_COLORS.each do |k, v|
164
+ ::UnicodePlot::StyledPrinter::TEXT_COLORS.each do |k, v|
162
165
  s << v
163
166
  s << k.to_s
164
167
  unless color_names
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'dsv'
4
- require_relative 'command/parser'
4
+ require_relative 'parser'
5
5
 
6
6
  # FIXME
7
- require_relative 'backends/unicode_plot_backend'
7
+ require_relative 'backends/unicode_plot'
8
8
 
9
9
  module YouPlot
10
10
  Data = Struct.new(:headers, :series)
@@ -19,7 +19,7 @@ module YouPlot
19
19
  @command = nil
20
20
  @params = nil
21
21
  @options = nil
22
- @backend = YouPlot::Backends::UnicodePlotBackend
22
+ @backend = YouPlot::Backends::UnicodePlot
23
23
  end
24
24
 
25
25
  def run_as_executable
@@ -33,21 +33,34 @@ module YouPlot
33
33
  @options ||= parser.options
34
34
  @params ||= parser.params
35
35
 
36
+ # color command
36
37
  if %i[colors color colours colour].include? @command
37
38
  plot = create_plot
38
39
  output_plot(plot)
39
- elsif options[:progressive]
40
+ return
41
+ end
42
+
43
+ # progressive mode
44
+ if options[:progressive]
40
45
  stop = false
41
46
  Signal.trap(:INT) { stop = true }
42
- options[:output].print "\e[?25l" # make cursor invisible
47
+
48
+ # make cursor invisible
49
+ options[:output].print "\e[?25l"
50
+
51
+ # mainloop
43
52
  while (input = Kernel.gets)
44
53
  n = main_progressive(input)
45
54
  break if stop
46
55
 
47
56
  options[:output].print "\e[#{n}F"
48
57
  end
58
+
49
59
  options[:output].print "\e[0J"
50
- options[:output].print "\e[?25h" # make cursor visible
60
+ # make cursor visible
61
+ options[:output].print "\e[?25h"
62
+
63
+ # normal mode
51
64
  else
52
65
  # Sometimes the input file does not end with a newline code.
53
66
  while (input = Kernel.gets(nil))
@@ -59,23 +72,32 @@ module YouPlot
59
72
  private
60
73
 
61
74
  def main(input)
75
+ # Outputs input data to a file or stdout.
62
76
  output_data(input)
63
77
 
64
- @data = read_dsv(input)
78
+ @data = parse_dsv(input)
65
79
 
80
+ # Debug mode, show parsed results
66
81
  pp @data if options[:debug]
67
82
 
83
+ # When run as a program instead of a library
68
84
  if YouPlot.run_as_executable?
69
85
  begin
70
86
  plot = create_plot
71
87
  rescue ArgumentError => e
88
+ # Show only one line of error.
72
89
  warn e.backtrace[0]
90
+ # Show error message in purple
73
91
  warn "\e[35m#{e}\e[0m"
92
+ # Explicitly terminated with exit code: 1
74
93
  exit 1
75
94
  end
95
+
96
+ # When running YouPlot as a library (e.g. for testing)
76
97
  else
77
98
  plot = create_plot
78
99
  end
100
+
79
101
  output_plot(plot)
80
102
  end
81
103
 
@@ -95,15 +117,28 @@ module YouPlot
95
117
  @raw_data << input
96
118
 
97
119
  # FIXME
98
- @data = read_dsv(@raw_data)
120
+ @data = parse_dsv(@raw_data)
99
121
 
100
122
  plot = create_plot
101
123
  output_plot_progressive(plot)
102
124
  end
103
125
 
104
- def read_dsv(input)
105
- input = input.dup.force_encoding(options[:encoding]).encode('utf-8') if options[:encoding]
106
- DSV.parse(input, options[:delimiter], options[:headers], options[:transpose])
126
+ def parse_dsv(input)
127
+ # If encoding is specified, convert to UTF-8
128
+ if options[:encoding]
129
+ input.force_encoding(options[:encoding])
130
+ .encode!('utf-8')
131
+ end
132
+
133
+ begin
134
+ data = DSV.parse(input, options[:delimiter], options[:headers], options[:transpose])
135
+ rescue CSV::MalformedCSVError => e
136
+ warn 'Failed to parse the text. '
137
+ warn 'Please try to set the correct character encoding with --encoding option.'
138
+ raise e
139
+ end
140
+
141
+ data
107
142
  end
108
143
 
109
144
  def create_plot
@@ -147,9 +182,9 @@ module YouPlot
147
182
 
148
183
  def output_plot(plot)
149
184
  case options[:output]
150
- when IO
185
+ when IO, StringIO
151
186
  plot.render(options[:output])
152
- else
187
+ when String, Tempfile
153
188
  File.open(options[:output], 'w') do |f|
154
189
  plot.render(f)
155
190
  end
@@ -158,7 +193,7 @@ module YouPlot
158
193
 
159
194
  def output_plot_progressive(plot)
160
195
  case options[:output]
161
- when IO
196
+ when IO, StringIO
162
197
  # RefactorMe
163
198
  out = StringIO.new(String.new)
164
199
  def out.tty?
data/lib/youplot/dsv.rb CHANGED
@@ -3,41 +3,49 @@
3
3
  require 'csv'
4
4
 
5
5
  module YouPlot
6
- # Read and interpret Delimiter-separated values format file or stream.
6
+ # Module to handle DSV (Delimiter-separated values) format.
7
+ # Extract header and series.
7
8
  module DSV
8
9
  module_function
9
10
 
10
11
  def parse(input, delimiter, headers, transpose)
11
- arr = parse_as_csv(input, delimiter)
12
+ # Parse as CSV
13
+ arr = CSV.parse(input, col_sep: delimiter)
14
+
15
+ # Remove blank lines
16
+ arr.delete_if do |i|
17
+ i == [] or i.all? nil
18
+ end
19
+
20
+ # get header
12
21
  headers = get_headers(arr, headers, transpose)
22
+
23
+ # get series
13
24
  series = get_series(arr, headers, transpose)
14
- if headers.nil?
15
- Data.new(headers, series)
16
- else
17
- if headers.include?(nil)
18
- warn "\e[35mHeaders contains nil in it.\e[0m"
19
- elsif headers.include? ''
20
- warn "\e[35mHeaders contains \"\" in it.\e[0m"
21
- end
22
- h_size = headers.size
23
- s_size = series.size
24
- if h_size == s_size
25
- Data.new(headers, series)
26
- elsif h_size > s_size
27
- warn "\e[35mThe number of headers is greater than the number of series.\e[0m"
28
- exit 1 if YouPlot.run_as_executable?
29
- elsif h_size < s_size
30
- warn "\e[35mThe number of headers is less than the number of series.\e[0m"
31
- exit 1 if YouPlot.run_as_executable?
32
- end
25
+
26
+ # Return if No header
27
+ return Data.new(headers, series) if headers.nil?
28
+
29
+ # Warn if header contains nil
30
+ warn "\e[35mHeaders contains nil in it.\e[0m" if headers.include?(nil)
31
+
32
+ # Warn if header contains ''
33
+ warn "\e[35mHeaders contains \"\" in it.\e[0m" if headers.include? ''
34
+
35
+ # Make sure the number of elements in the header matches the number of series.
36
+ h_size = headers.size
37
+ s_size = series.size
38
+
39
+ if h_size > s_size
40
+ warn "\e[35mThe number of headers is greater than the number of series.\e[0m"
41
+ exit 1 if YouPlot.run_as_executable?
42
+
43
+ elsif h_size < s_size
44
+ warn "\e[35mThe number of headers is less than the number of series.\e[0m"
45
+ exit 1 if YouPlot.run_as_executable?
33
46
  end
34
- end
35
47
 
36
- def parse_as_csv(input, delimiter)
37
- CSV.parse(input, col_sep: delimiter)
38
- .delete_if do |i|
39
- i == [] or i.all? nil
40
- end
48
+ Data.new(headers, series) if h_size == s_size
41
49
  end
42
50
 
43
51
  # Transpose different sized ruby arrays
@@ -47,31 +55,33 @@ module YouPlot
47
55
  end
48
56
 
49
57
  def get_headers(arr, headers, transpose)
50
- if headers
51
- if transpose
52
- arr.map(&:first)
53
- else
54
- arr[0]
55
- end
56
- end
58
+ # header(-)
59
+ return nil unless headers
60
+
61
+ # header(+) trenspose(+)
62
+ return arr.map(&:first) if transpose
63
+
64
+ # header(+) transpose(-)
65
+ arr[0]
57
66
  end
58
67
 
59
68
  def get_series(arr, headers, transpose)
60
- if headers
61
- if arr.size > 1
62
- if transpose
63
- arr.map { |row| row[1..-1] }
64
- else
65
- transpose2(arr[1..-1])
66
- end
67
- else
68
- Array.new(arr[0].size, [])
69
- end
70
- elsif transpose
71
- arr
72
- else
73
- transpose2(arr)
69
+ # header(-)
70
+ unless headers
71
+ return arr if transpose
72
+
73
+ return transpose2(arr)
74
74
  end
75
+
76
+ # header(+) but no element in the series.
77
+ # TODO: should raise error?
78
+ return Array.new(arr[0].size, []) if arr.size == 1
79
+
80
+ # header(+) transpose(+)
81
+ return arr.map { |row| row[1..-1] } if transpose
82
+
83
+ # header(+) transpose(-)
84
+ transpose2(arr[1..-1])
75
85
  end
76
86
  end
77
87
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YouPlot
4
+ # Command line options that are not Plot parameters
5
+ Options = Struct.new(
6
+ :delimiter,
7
+ :transpose,
8
+ :headers,
9
+ :pass,
10
+ :output,
11
+ :fmt,
12
+ :progressive,
13
+ :encoding,
14
+ :color_names,
15
+ :debug,
16
+ keyword_init: true
17
+ )
18
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YouPlot
4
+ # UnicodePlot parameters.
5
+ # Why Struct, not Hash?
6
+ # * The keys are static in Struct.
7
+ # * Struct does not conflict with keyword arguments. Hash dose.
8
+ Parameters = Struct.new(
9
+ # Sort me!
10
+ :title,
11
+ :width,
12
+ :height,
13
+ :border,
14
+ :margin,
15
+ :padding,
16
+ :color,
17
+ :xlabel,
18
+ :ylabel,
19
+ :labels,
20
+ :symbol,
21
+ :xscale,
22
+ :nbins,
23
+ :closed,
24
+ :canvas,
25
+ :xlim,
26
+ :ylim,
27
+ :grid,
28
+ :name
29
+ ) do
30
+ def to_hc
31
+ to_h.compact
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,306 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require_relative 'options'
5
+
6
+ module YouPlot
7
+ # Class for parsing command line options
8
+ class Parser
9
+ class Error < StandardError; end
10
+
11
+ attr_reader :command, :options, :params,
12
+ :main_parser, :sub_parser
13
+
14
+ def initialize
15
+ @command = nil
16
+
17
+ @options = Options.new(
18
+ delimiter: "\t",
19
+ transpose: false,
20
+ headers: nil,
21
+ pass: false,
22
+ output: $stderr,
23
+ fmt: 'xyy',
24
+ progressive: false,
25
+ encoding: nil,
26
+ color_names: false,
27
+ debug: false
28
+ )
29
+
30
+ @params = Parameters.new
31
+ end
32
+
33
+ def create_base_parser
34
+ OptionParser.new do |parser|
35
+ parser.program_name = 'YouPlot'
36
+ parser.version = YouPlot::VERSION
37
+ parser.summary_width = 23
38
+ parser.on_tail('') # Add a blank line at the end
39
+ parser.separator('')
40
+ parser.on('Common options:')
41
+ parser.on('-O', '--pass [FILE]', 'file to output input data to [stdout]',
42
+ 'for inserting YouPlot in the middle of Unix pipes') do |v|
43
+ options[:pass] = v || $stdout
44
+ end
45
+ parser.on('-o', '--output [FILE]', 'file to output plots to [stdout]',
46
+ 'If no option is specified, plot will print to stderr') do |v|
47
+ options[:output] = v || $stdout
48
+ end
49
+ parser.on('-d', '--delimiter DELIM', String, 'use DELIM instead of [TAB] for field delimiter') do |v|
50
+ options[:delimiter] = v
51
+ end
52
+ parser.on('-H', '--headers', TrueClass, 'specify that the input has header row') do |v|
53
+ options[:headers] = v
54
+ end
55
+ parser.on('-T', '--transpose', TrueClass, 'transpose the axes of the input data') do |v|
56
+ options[:transpose] = v
57
+ end
58
+ parser.on('-t', '--title STR', String, 'print string on the top of plot') do |v|
59
+ params.title = v
60
+ end
61
+ parser.on('-x', '--xlabel STR', String, 'print string on the bottom of the plot') do |v|
62
+ params.xlabel = v
63
+ end
64
+ parser.on('-y', '--ylabel STR', String, 'print string on the far left of the plot') do |v|
65
+ params.ylabel = v
66
+ end
67
+ parser.on('-w', '--width INT', Integer, 'number of characters per row') do |v|
68
+ params.width = v
69
+ end
70
+ parser.on('-h', '--height INT', Numeric, 'number of rows') do |v|
71
+ params.height = v
72
+ end
73
+ border_options = UnicodePlot::BORDER_MAP.keys.join(', ')
74
+ parser.on('-b', '--border STR', String, 'specify the style of the bounding box', "(#{border_options})") do |v|
75
+ params.border = v.to_sym
76
+ end
77
+ parser.on('-m', '--margin INT', Numeric, 'number of spaces to the left of the plot') do |v|
78
+ params.margin = v
79
+ end
80
+ parser.on('--padding INT', Numeric, 'space of the left and right of the plot') do |v|
81
+ params.padding = v
82
+ end
83
+ parser.on('-c', '--color VAL', String, 'color of the drawing') do |v|
84
+ params.color = v =~ /\A[0-9]+\z/ ? v.to_i : v.to_sym
85
+ end
86
+ parser.on('--[no-]labels', TrueClass, 'hide the labels') do |v|
87
+ params.labels = v
88
+ end
89
+ parser.on('-p', '--progress', TrueClass, 'progressive mode [experimental]') do |v|
90
+ options[:progressive] = v
91
+ end
92
+ parser.on('-C', '--color-output', TrueClass, 'colorize even if writing to a pipe') do |_v|
93
+ UnicodePlot::IOContext.define_method(:color?) { true } # FIXME
94
+ end
95
+ parser.on('-M', '--monochrome', TrueClass, 'no colouring even if writing to a tty') do |_v|
96
+ UnicodePlot::IOContext.define_method(:color?) { false } # FIXME
97
+ end
98
+ parser.on('--encoding STR', String, 'Specify the input encoding') do |v|
99
+ options[:encoding] = v
100
+ end
101
+ # Optparse adds the help option, but it doesn't show up in usage.
102
+ # This is why you need the code below.
103
+ parser.on('--help', 'print sub-command help menu') do
104
+ puts parser.help
105
+ exit if YouPlot.run_as_executable?
106
+ end
107
+ parser.on('--debug', TrueClass, 'print preprocessed data') do |v|
108
+ options[:debug] = v
109
+ end
110
+ # yield opt if block_given?
111
+ end
112
+ end
113
+
114
+ def create_main_parser
115
+ @main_parser = create_base_parser
116
+ main_parser.banner = \
117
+ <<~MSG
118
+
119
+ Program: YouPlot (Tools for plotting on the terminal)
120
+ Version: #{YouPlot::VERSION} (using UnicodePlot #{UnicodePlot::VERSION})
121
+ Source: https://github.com/kojix2/youplot
122
+
123
+ Usage: uplot <command> [options] <in.tsv>
124
+
125
+ Commands:
126
+ barplot bar draw a horizontal barplot
127
+ histogram hist draw a horizontal histogram
128
+ lineplot line draw a line chart
129
+ lineplots lines draw a line chart with multiple series
130
+ scatter s draw a scatter plot
131
+ density d draw a density plot
132
+ boxplot box draw a horizontal boxplot
133
+ colors color show the list of available colors
134
+
135
+ count c draw a baplot based on the number of
136
+ occurrences (slow)
137
+
138
+ General options:
139
+ --help print command specific help menu
140
+ --version print the version of YouPlot
141
+ MSG
142
+
143
+ # Help for the main parser is simple.
144
+ # Simply show the banner above.
145
+ main_parser.on('--help', 'print sub-command help menu') do
146
+ puts main_parser.banner
147
+ puts
148
+ exit if YouPlot.run_as_executable?
149
+ end
150
+ end
151
+
152
+ def sub_parser_add_symbol
153
+ sub_parser.on_head('--symbol STR', String, 'character to be used to plot the bars') do |v|
154
+ params.symbol = v
155
+ end
156
+ end
157
+
158
+ def sub_parser_add_xscale
159
+ xscale_options = UnicodePlot::ValueTransformer::PREDEFINED_TRANSFORM_FUNCTIONS.keys.join(', ')
160
+ sub_parser.on_head('--xscale STR', String, "axis scaling (#{xscale_options})") do |v|
161
+ params.xscale = v.to_sym
162
+ end
163
+ end
164
+
165
+ def sub_parser_add_canvas
166
+ sub_parser.on_head('--canvas STR', String, 'type of canvas') do |v|
167
+ params.canvas = v.to_sym
168
+ end
169
+ end
170
+
171
+ def sub_parser_add_xlim
172
+ sub_parser.on_head('--xlim FLOAT,FLOAT', Array, 'plotting range for the x coordinate') do |v|
173
+ params.xlim = v
174
+ end
175
+ end
176
+
177
+ def sub_parser_add_ylim
178
+ sub_parser.on_head('--ylim FLOAT,FLOAT', Array, 'plotting range for the y coordinate') do |v|
179
+ params.ylim = v
180
+ end
181
+ end
182
+
183
+ def sub_parser_add_grid
184
+ sub_parser.on_head('--[no-]grid', TrueClass, 'draws grid-lines at the origin') do |v|
185
+ params.grid = v
186
+ end
187
+ end
188
+
189
+ def sub_parser_add_fmt_xyxy
190
+ sub_parser.on_head('--fmt STR', String,
191
+ 'xyxy : header is like x1, y1, x2, y2, x3, y3...',
192
+ 'xyy : header is like x, y1, y2, y2, y3...') do |v|
193
+ options[:fmt] = v
194
+ end
195
+ end
196
+
197
+ def sub_parser_add_fmt_yx
198
+ sub_parser.on_head('--fmt STR', String,
199
+ 'xy : header is like x, y...',
200
+ 'yx : header is like y, x...') do |v|
201
+ options[:fmt] = v
202
+ end
203
+ end
204
+
205
+ def create_sub_parser
206
+ @sub_parser = create_base_parser
207
+ sub_parser.banner = \
208
+ <<~MSG
209
+
210
+ Usage: YouPlot #{command} [options] <in.tsv>
211
+
212
+ Options for #{command}:
213
+ MSG
214
+
215
+ case command
216
+
217
+ # If you type only `uplot` in the terminal.
218
+ when nil
219
+ warn main_parser.banner
220
+ warn "\n"
221
+ exit 1 if YouPlot.run_as_executable?
222
+
223
+ when :barplot, :bar
224
+ sub_parser_add_symbol
225
+ sub_parser_add_fmt_yx
226
+ sub_parser_add_xscale
227
+
228
+ when :count, :c
229
+ sub_parser_add_symbol
230
+ sub_parser_add_xscale
231
+
232
+ when :histogram, :hist
233
+ sub_parser_add_symbol
234
+ sub_parser.on_head('--closed STR', String, 'side of the intervals to be closed [left]') do |v|
235
+ params.closed = v
236
+ end
237
+ sub_parser.on_head('-n', '--nbins INT', Numeric, 'approximate number of bins') do |v|
238
+ params.nbins = v
239
+ end
240
+
241
+ when :lineplot, :line
242
+ sub_parser_add_canvas
243
+ sub_parser_add_grid
244
+ sub_parser_add_fmt_yx
245
+ sub_parser_add_ylim
246
+ sub_parser_add_xlim
247
+
248
+ when :lineplots, :lines
249
+ sub_parser_add_canvas
250
+ sub_parser_add_grid
251
+ sub_parser_add_fmt_xyxy
252
+ sub_parser_add_ylim
253
+ sub_parser_add_xlim
254
+
255
+ when :scatter, :s
256
+ sub_parser_add_canvas
257
+ sub_parser_add_grid
258
+ sub_parser_add_fmt_xyxy
259
+ sub_parser_add_ylim
260
+ sub_parser_add_xlim
261
+
262
+ when :density, :d
263
+ sub_parser_add_canvas
264
+ sub_parser_add_grid
265
+ sub_parser_add_fmt_xyxy
266
+ sub_parser_add_ylim
267
+ sub_parser_add_xlim
268
+
269
+ when :boxplot, :box
270
+ sub_parser_add_xlim
271
+
272
+ when :colors, :color, :colours, :colour
273
+ sub_parser.on_head('-n', '--names', 'show color names only', TrueClass) do |v|
274
+ options[:color_names] = v
275
+ end
276
+
277
+ else
278
+ error_message = "uplot: unrecognized command '#{command}'"
279
+ if YouPlot.run_as_executable?
280
+ warn error_message
281
+ exit 1
282
+ else
283
+ raise Error, error_message
284
+ end
285
+ end
286
+ end
287
+
288
+ def parse_options(argv = ARGV)
289
+ begin
290
+ create_main_parser.order!(argv)
291
+ rescue OptionParser::ParseError => e
292
+ warn "uplot: #{e.message}"
293
+ exit 1 if YouPlot.run_as_executable?
294
+ end
295
+
296
+ @command = argv.shift&.to_sym
297
+
298
+ begin
299
+ create_sub_parser&.parse!(argv)
300
+ rescue OptionParser::ParseError => e
301
+ warn "uplot: #{e.message}"
302
+ exit 1 if YouPlot.run_as_executable?
303
+ end
304
+ end
305
+ end
306
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YouPlot
4
- VERSION = '0.3.5'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: youplot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kojix2
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-21 00:00:00.000000000 Z
11
+ date: 2021-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: unicode_plot
@@ -109,12 +109,12 @@ files:
109
109
  - exe/youplot
110
110
  - lib/youplot.rb
111
111
  - lib/youplot/backends/processing.rb
112
- - lib/youplot/backends/unicode_plot_backend.rb
112
+ - lib/youplot/backends/unicode_plot.rb
113
113
  - lib/youplot/command.rb
114
- - lib/youplot/command/options.rb
115
- - lib/youplot/command/parser.rb
116
- - lib/youplot/command/plot_params.rb
117
114
  - lib/youplot/dsv.rb
115
+ - lib/youplot/options.rb
116
+ - lib/youplot/parameters.rb
117
+ - lib/youplot/parser.rb
118
118
  - lib/youplot/version.rb
119
119
  homepage: https://github.com/kojix2/youplot
120
120
  licenses:
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YouPlot
4
- class Command
5
- Options = Struct.new(
6
- :delimiter,
7
- :transpose,
8
- :headers,
9
- :pass,
10
- :output,
11
- :fmt,
12
- :progressive,
13
- :encoding,
14
- :color_names,
15
- :debug,
16
- keyword_init: true
17
- )
18
- end
19
- end
@@ -1,305 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'optparse'
4
- require_relative 'options'
5
- require_relative 'plot_params'
6
-
7
- module YouPlot
8
- class Command
9
- class Parser
10
- class Error < StandardError; end
11
-
12
- attr_reader :command, :options, :params,
13
- :main_parser, :sub_parser
14
-
15
- def initialize
16
- @command = nil
17
-
18
- @options = Options.new(
19
- delimiter: "\t",
20
- transpose: false,
21
- headers: nil,
22
- pass: false,
23
- output: $stderr,
24
- fmt: 'xyy',
25
- progressive: false,
26
- encoding: nil,
27
- color_names: false,
28
- debug: false
29
- )
30
-
31
- @params = PlotParams.new
32
- end
33
-
34
- def create_default_parser
35
- OptionParser.new do |parser|
36
- parser.program_name = 'YouPlot'
37
- parser.version = YouPlot::VERSION
38
- parser.summary_width = 24
39
- parser.on_tail('') # Add a blank line at the end
40
- parser.separator('')
41
- parser.on('Common options:')
42
- parser.on('-O', '--pass [FILE]', 'file to output input data to [stdout]',
43
- 'for inserting YouPlot in the middle of Unix pipes') do |v|
44
- options[:pass] = v || $stdout
45
- end
46
- parser.on('-o', '--output [FILE]', 'file to output plots to [stdout]',
47
- 'If no option is specified, plot will print to stderr') do |v|
48
- options[:output] = v || $stdout
49
- end
50
- parser.on('-d', '--delimiter DELIM', String, 'use DELIM instead of [TAB] for field delimiter') do |v|
51
- options[:delimiter] = v
52
- end
53
- parser.on('-H', '--headers', TrueClass, 'specify that the input has header row') do |v|
54
- options[:headers] = v
55
- end
56
- parser.on('-T', '--transpose', TrueClass, 'transpose the axes of the input data') do |v|
57
- options[:transpose] = v
58
- end
59
- parser.on('-t', '--title STR', String, 'print string on the top of plot') do |v|
60
- params.title = v
61
- end
62
- parser.on('-x', '--xlabel STR', String, 'print string on the bottom of the plot') do |v|
63
- params.xlabel = v
64
- end
65
- parser.on('-y', '--ylabel STR', String, 'print string on the far left of the plot') do |v|
66
- params.ylabel = v
67
- end
68
- parser.on('-w', '--width INT', Integer, 'number of characters per row') do |v|
69
- params.width = v
70
- end
71
- parser.on('-h', '--height INT', Numeric, 'number of rows') do |v|
72
- params.height = v
73
- end
74
- border_options = UnicodePlot::BORDER_MAP.keys.join(', ')
75
- parser.on('-b', '--border STR', String, 'specify the style of the bounding box', "(#{border_options})") do |v|
76
- params.border = v.to_sym
77
- end
78
- parser.on('-m', '--margin INT', Numeric, 'number of spaces to the left of the plot') do |v|
79
- params.margin = v
80
- end
81
- parser.on('--padding INT', Numeric, 'space of the left and right of the plot') do |v|
82
- params.padding = v
83
- end
84
- parser.on('-c', '--color VAL', String, 'color of the drawing') do |v|
85
- params.color = v =~ /\A[0-9]+\z/ ? v.to_i : v.to_sym
86
- end
87
- parser.on('--[no-]labels', TrueClass, 'hide the labels') do |v|
88
- params.labels = v
89
- end
90
- parser.on('-p', '--progress', TrueClass, 'progressive mode [experimental]') do |v|
91
- options[:progressive] = v
92
- end
93
- parser.on('-C', '--color-output', TrueClass, 'colorize even if writing to a pipe') do |v|
94
- UnicodePlot::StyledPrinter.define_method(:color?){ |o| true }
95
- end
96
- parser.on('-M', '--monochrome', TrueClass, 'no colouring even if writing to a tty') do |v|
97
- UnicodePlot::StyledPrinter.define_method(:color?){ |o| false }
98
- end
99
- parser.on('--encoding STR', String, 'Specify the input encoding') do |v|
100
- options[:encoding] = v
101
- end
102
- # Optparse adds the help option, but it doesn't show up in usage.
103
- # This is why you need the code below.
104
- parser.on('--help', 'print sub-command help menu') do
105
- puts parser.help
106
- exit if YouPlot.run_as_executable?
107
- end
108
- parser.on('--debug', TrueClass, 'print preprocessed data') do |v|
109
- options[:debug] = v
110
- end
111
- # yield opt if block_given?
112
- end
113
- end
114
-
115
- def create_main_parser
116
- @main_parser = create_default_parser
117
- main_parser.banner = \
118
- <<~MSG
119
-
120
- Program: YouPlot (Tools for plotting on the terminal)
121
- Version: #{YouPlot::VERSION} (using UnicodePlot #{UnicodePlot::VERSION})
122
- Source: https://github.com/kojix2/youplot
123
-
124
- Usage: uplot <command> [options] <in.tsv>
125
-
126
- Commands:
127
- barplot bar draw a horizontal barplot
128
- histogram hist draw a horizontal histogram
129
- lineplot line draw a line chart
130
- lineplots lines draw a line chart with multiple series
131
- scatter s draw a scatter plot
132
- density d draw a density plot
133
- boxplot box draw a horizontal boxplot
134
- colors color show the list of available colors
135
-
136
- count c draw a baplot based on the number of
137
- occurrences (slow)
138
-
139
- General options:
140
- --help print command specific help menu
141
- --version print the version of YouPlot
142
- MSG
143
-
144
- # Help for the main parser is simple.
145
- # Simply show the banner above.
146
- main_parser.on('--help', 'print sub-command help menu') do
147
- puts main_parser.banner
148
- puts
149
- exit if YouPlot.run_as_executable?
150
- end
151
- end
152
-
153
- def sub_parser_add_symbol
154
- sub_parser.on_head('--symbol STR', String, 'character to be used to plot the bars') do |v|
155
- params.symbol = v
156
- end
157
- end
158
-
159
- def sub_parser_add_xscale
160
- xscale_options = UnicodePlot::ValueTransformer::PREDEFINED_TRANSFORM_FUNCTIONS.keys.join(', ')
161
- sub_parser.on_head('--xscale STR', String, "axis scaling (#{xscale_options})") do |v|
162
- params.xscale = v.to_sym
163
- end
164
- end
165
-
166
- def sub_parser_add_canvas
167
- sub_parser.on_head('--canvas STR', String, 'type of canvas') do |v|
168
- params.canvas = v.to_sym
169
- end
170
- end
171
-
172
- def sub_parser_add_xlim
173
- sub_parser.on_head('--xlim FLOAT,FLOAT', Array, 'plotting range for the x coordinate') do |v|
174
- params.xlim = v
175
- end
176
- end
177
-
178
- def sub_parser_add_ylim
179
- sub_parser.on_head('--ylim FLOAT,FLOAT', Array, 'plotting range for the y coordinate') do |v|
180
- params.ylim = v
181
- end
182
- end
183
-
184
- def sub_parser_add_grid
185
- sub_parser.on_head('--[no-]grid', TrueClass, 'draws grid-lines at the origin') do |v|
186
- params.grid = v
187
- end
188
- end
189
-
190
- def create_sub_parser
191
- @sub_parser = create_default_parser
192
- sub_parser.banner = \
193
- <<~MSG
194
-
195
- Usage: YouPlot #{command} [options] <in.tsv>
196
-
197
- Options for #{command}:
198
- MSG
199
-
200
- case command
201
-
202
- # If you type only `uplot` in the terminal.
203
- when nil
204
- warn main_parser.banner
205
- warn "\n"
206
- exit 1 if YouPlot.run_as_executable?
207
-
208
- when :barplot, :bar
209
- sub_parser_add_symbol
210
- sub_parser.on_head('--fmt STR', String, 'xy : header is like x, y...', 'yx : header is like y, x...') do |v|
211
- options[:fmt] = v
212
- end
213
- sub_parser_add_xscale
214
-
215
- when :count, :c
216
- sub_parser_add_symbol
217
- sub_parser_add_xscale
218
-
219
- when :histogram, :hist
220
- sub_parser_add_symbol
221
- sub_parser.on_head('--closed STR', String, 'side of the intervals to be closed [left]') do |v|
222
- params.closed = v
223
- end
224
- sub_parser.on_head('-n', '--nbins INT', Numeric, 'approximate number of bins') do |v|
225
- params.nbins = v
226
- end
227
-
228
- when :lineplot, :line
229
- sub_parser_add_canvas
230
- sub_parser_add_grid
231
- sub_parser.on_head('--fmt STR', String, 'xy : header is like x, y...', 'yx : header is like y, x...') do |v|
232
- options[:fmt] = v
233
- end
234
- sub_parser_add_ylim
235
- sub_parser_add_xlim
236
-
237
- when :lineplots, :lines
238
- sub_parser_add_canvas
239
- sub_parser_add_grid
240
- sub_parser.on_head('--fmt STR', String, 'xyxy : header is like x1, y1, x2, y2, x3, y3...',
241
- 'xyy : header is like x, y1, y2, y2, y3...') do |v|
242
- options[:fmt] = v
243
- end
244
- sub_parser_add_ylim
245
- sub_parser_add_xlim
246
-
247
- when :scatter, :s
248
- sub_parser_add_canvas
249
- sub_parser_add_grid
250
- sub_parser.on_head('--fmt STR', String, 'xyxy : header is like x1, y1, x2, y2, x3, y3...',
251
- 'xyy : header is like x, y1, y2, y2, y3...') do |v|
252
- options[:fmt] = v
253
- end
254
- sub_parser_add_ylim
255
- sub_parser_add_xlim
256
-
257
- when :density, :d
258
- sub_parser_add_canvas
259
- sub_parser_add_grid
260
- sub_parser.on('--fmt STR', String, 'xyxy : header is like x1, y1, x2, y2, x3, y3...',
261
- 'xyy : header is like x, y1, y2, y2, y3...') do |v|
262
- options[:fmt] = v
263
- end
264
- sub_parser_add_ylim
265
- sub_parser_add_xlim
266
-
267
- when :boxplot, :box
268
- sub_parser_add_xlim
269
-
270
- when :colors, :color, :colours, :colour
271
- sub_parser.on_head('-n', '--names', 'show color names only', TrueClass) do |v|
272
- options[:color_names] = v
273
- end
274
-
275
- else
276
- error_message = "uplot: unrecognized command '#{command}'"
277
- if YouPlot.run_as_executable?
278
- warn error_message
279
- exit 1
280
- else
281
- raise Error, error_message
282
- end
283
- end
284
- end
285
-
286
- def parse_options(argv = ARGV)
287
- begin
288
- create_main_parser.order!(argv)
289
- rescue OptionParser::ParseError => e
290
- warn "uplot: #{e.message}"
291
- exit 1 if YouPlot.run_as_executable?
292
- end
293
-
294
- @command = argv.shift&.to_sym
295
-
296
- begin
297
- create_sub_parser&.parse!(argv)
298
- rescue OptionParser::ParseError => e
299
- warn "uplot: #{e.message}"
300
- exit 1 if YouPlot.run_as_executable?
301
- end
302
- end
303
- end
304
- end
305
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YouPlot
4
- class Command
5
- # UnicodePlot parameters.
6
- # * Normally in a Ruby program, you might use hash for the parameter object.
7
- # * Here, I use Struct for 2 safety reason.
8
- # * The keys are static in Struct.
9
- # * Struct does not conflict with keyword arguments. Hash dose.
10
- PlotParams = Struct.new(
11
- # Sort me!
12
- :title,
13
- :width,
14
- :height,
15
- :border,
16
- :margin,
17
- :padding,
18
- :color,
19
- :xlabel,
20
- :ylabel,
21
- :labels,
22
- :symbol,
23
- :xscale,
24
- :nbins,
25
- :closed,
26
- :canvas,
27
- :xlim,
28
- :ylim,
29
- :grid,
30
- :name
31
- ) do
32
- def to_hc
33
- to_h.compact
34
- end
35
- end
36
- end
37
- end