youplot 0.3.5 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ :reverse, # count
15
+ :color_names, # color
16
+ :debug
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,309 @@
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
+ "\t", # elimiter:
19
+ false, # transpose:
20
+ nil, # headers:
21
+ false, # pass:
22
+ $stderr, # output:
23
+ 'xyy', # fmt:
24
+ false, # progressive:
25
+ nil, # encoding:
26
+ false, # color_names:
27
+ false # debug:
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('--xlabel STR', String, 'print string on the bottom of the plot') do |v|
62
+ params.xlabel = v
63
+ end
64
+ parser.on('--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', Numeric, '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/red-data-tools/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
+ count c draw a baplot based on the number of
134
+ occurrences (slow)
135
+ colors color show the list of available colors
136
+
137
+ General options:
138
+ --help print command specific help menu
139
+ --version print the version of YouPlot
140
+ MSG
141
+
142
+ # Help for the main parser is simple.
143
+ # Simply show the banner above.
144
+ main_parser.on('--help', 'print sub-command help menu') do
145
+ puts main_parser.banner
146
+ puts
147
+ exit if YouPlot.run_as_executable?
148
+ end
149
+ end
150
+
151
+ def sub_parser_add_symbol
152
+ sub_parser.on_head('--symbol STR', String, 'character to be used to plot the bars') do |v|
153
+ params.symbol = v
154
+ end
155
+ end
156
+
157
+ def sub_parser_add_xscale
158
+ xscale_options = UnicodePlot::ValueTransformer::PREDEFINED_TRANSFORM_FUNCTIONS.keys.join(', ')
159
+ sub_parser.on_head('--xscale STR', String, "axis scaling (#{xscale_options})") do |v|
160
+ params.xscale = v.to_sym
161
+ end
162
+ end
163
+
164
+ def sub_parser_add_canvas
165
+ canvas_types = UnicodePlot::Canvas::CANVAS_CLASS_MAP.keys.join(', ')
166
+ sub_parser.on_head('--canvas STR', String, 'type of canvas', "(#{canvas_types})") 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.map(&:to_f)
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.map(&:to_f)
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.on_head('-r', '--reverse', TrueClass, 'reverse the result of comparisons') do |v|
230
+ options.reverse = v
231
+ end
232
+ sub_parser_add_symbol
233
+ sub_parser_add_xscale
234
+
235
+ when :histogram, :hist
236
+ sub_parser_add_symbol
237
+ sub_parser.on_head('--closed STR', String, 'side of the intervals to be closed [left]') do |v|
238
+ params.closed = v
239
+ end
240
+ sub_parser.on_head('-n', '--nbins INT', Numeric, 'approximate number of bins') do |v|
241
+ params.nbins = v
242
+ end
243
+
244
+ when :lineplot, :line
245
+ sub_parser_add_canvas
246
+ sub_parser_add_grid
247
+ sub_parser_add_fmt_yx
248
+ sub_parser_add_ylim
249
+ sub_parser_add_xlim
250
+
251
+ when :lineplots, :lines
252
+ sub_parser_add_canvas
253
+ sub_parser_add_grid
254
+ sub_parser_add_fmt_xyxy
255
+ sub_parser_add_ylim
256
+ sub_parser_add_xlim
257
+
258
+ when :scatter, :s
259
+ sub_parser_add_canvas
260
+ sub_parser_add_grid
261
+ sub_parser_add_fmt_xyxy
262
+ sub_parser_add_ylim
263
+ sub_parser_add_xlim
264
+
265
+ when :density, :d
266
+ sub_parser_add_canvas
267
+ sub_parser_add_grid
268
+ sub_parser_add_fmt_xyxy
269
+ sub_parser_add_ylim
270
+ sub_parser_add_xlim
271
+
272
+ when :boxplot, :box
273
+ sub_parser_add_xlim
274
+
275
+ when :colors, :color, :colours, :colour
276
+ sub_parser.on_head('-n', '--names', TrueClass, 'show color names only') do |v|
277
+ options[:color_names] = v
278
+ end
279
+
280
+ else
281
+ error_message = "uplot: unrecognized command '#{command}'"
282
+ if YouPlot.run_as_executable?
283
+ warn error_message
284
+ exit 1
285
+ else
286
+ raise Error, error_message
287
+ end
288
+ end
289
+ end
290
+
291
+ def parse_options(argv = ARGV)
292
+ begin
293
+ create_main_parser.order!(argv)
294
+ rescue OptionParser::ParseError => e
295
+ warn "uplot: #{e.message}"
296
+ exit 1 if YouPlot.run_as_executable?
297
+ end
298
+
299
+ @command = argv.shift&.to_sym
300
+
301
+ begin
302
+ create_sub_parser&.parse!(argv)
303
+ rescue OptionParser::ParseError => e
304
+ warn "uplot: #{e.message}"
305
+ exit 1 if YouPlot.run_as_executable?
306
+ end
307
+ end
308
+ end
309
+ 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.3'
5
5
  end
data/lib/youplot.rb CHANGED
@@ -1,11 +1,17 @@
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
+ # @run_as_executable = true / false
10
+ # YouPlot behaves slightly differently when run as a command line tool
11
+ # and when run as a script (e.g. for testing). In the event of an error,
12
+ # when run as a command line tool, YouPlot will display a short error message
13
+ # and exit abnormally. When run as a script, it will just raise an error.
14
+ @run_as_executable = false
9
15
  class << self
10
16
  attr_accessor :run_as_executable
11
17
 
@@ -13,5 +19,4 @@ module YouPlot
13
19
  @run_as_executable
14
20
  end
15
21
  end
16
- @run_as_executable = false
17
22
  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.3
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-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: unicode_plot
@@ -16,84 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.0.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: simplecov
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: test-unit
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
26
+ version: 0.0.5
97
27
  description: A command line tool for Unicode Plotting
98
28
  email:
99
29
  - 2xijok@gmail.com
@@ -109,14 +39,14 @@ files:
109
39
  - exe/youplot
110
40
  - lib/youplot.rb
111
41
  - lib/youplot/backends/processing.rb
112
- - lib/youplot/backends/unicode_plot_backend.rb
42
+ - lib/youplot/backends/unicode_plot.rb
113
43
  - lib/youplot/command.rb
114
- - lib/youplot/command/options.rb
115
- - lib/youplot/command/parser.rb
116
- - lib/youplot/command/plot_params.rb
117
44
  - lib/youplot/dsv.rb
45
+ - lib/youplot/options.rb
46
+ - lib/youplot/parameters.rb
47
+ - lib/youplot/parser.rb
118
48
  - lib/youplot/version.rb
119
- homepage: https://github.com/kojix2/youplot
49
+ homepage: https://github.com/red-data-tools/YouPlot
120
50
  licenses:
121
51
  - MIT
122
52
  metadata: {}
@@ -135,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
65
  - !ruby/object:Gem::Version
136
66
  version: '0'
137
67
  requirements: []
138
- rubygems_version: 3.2.15
68
+ rubygems_version: 3.2.26
139
69
  signing_key:
140
70
  specification_version: 4
141
71
  summary: A command line tool for Unicode Plotting
@@ -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