youplot 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d270d30bd2d7d0766d6e26704383ec25fd742d2ed12b8e3738f91c985e2b4f80
4
+ data.tar.gz: '01149b5a5bd05dbbfc544e207ea4345c602468f68e39d3965025c4f7c1ddb13f'
5
+ SHA512:
6
+ metadata.gz: 80c2894983ca5cc3a9e9833a95f7d11a8928db454fecc0b97b585a4903dd0ae77cf89a1929dc4cb526dc6e7b0f8b0019d5d0769ab6f1a63ab4acb3db8a125c35
7
+ data.tar.gz: 4c6b532a8b95ab2b9ba8aa5def4b368edd93fe5e9af224d1f7fc4f87a4b40d0a07cf7f874565c10f7630d257e393fb8175ec087e6da9e4740b4e31e76c3d1d12
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 kojix2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,148 @@
1
+ # YouPlot
2
+
3
+ ![Build Status](https://github.com/kojix2/youplot/workflows/test/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/youplot.svg)](https://badge.fury.io/rb/youplot)
5
+ [![Docs Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://rubydoc.info/gems/youplot)
6
+ [![The MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
7
+
8
+ Create ASCII charts on the terminal with data from standard streams in the pipeline.
9
+
10
+ :bar_chart: Powered by [UnicodePlot](https://github.com/red-data-tools/unicode_plot.rb)
11
+
12
+ ## Installation
13
+
14
+ ```
15
+ gem install youplot
16
+ ```
17
+
18
+ ## Screenshots
19
+
20
+ **histogram**
21
+
22
+ ```sh
23
+ ruby -r numo/narray -e "puts Numo::DFloat.new(1000).rand_norm.to_a" \
24
+ | uplot hist --nbins 15
25
+ ```
26
+
27
+ <img src="https://i.imgur.com/wpsoGJq.png" width="75%" height="75%">
28
+
29
+ ```sh
30
+ echo -e "from numpy import random;" \
31
+ "n = random.randn(10000);" \
32
+ "print('\\\n'.join(str(i) for i in n))" \
33
+ | python \
34
+ | uplot hist --nbins 20
35
+ ```
36
+
37
+ <img src="https://i.imgur.com/97R2MQx.png" width="75%" height="75%">
38
+
39
+ **scatter**
40
+
41
+ ```sh
42
+ curl -s https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv \
43
+ | cut -f1-4 -d, \
44
+ | uplot scatter -H -d, -t IRIS
45
+ ```
46
+
47
+ <img src="https://i.imgur.com/STX7bFT.png" width="75%" height="75%">
48
+
49
+ **line**
50
+
51
+ ```sh
52
+ curl -s https://www.mhlw.go.jp/content/pcr_positive_daily.csv \
53
+ | cut -f2 -d, \
54
+ | uplot line -w 50 -h 15 -t 'PCR positive tests' --xlabel Date --ylabel number
55
+ ```
56
+
57
+ <img src="https://i.imgur.com/PVl5dsa.png" width="75%" height="75%">
58
+
59
+ **box**
60
+
61
+ ```sh
62
+ curl -s https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv \
63
+ | cut -f1-4 -d, \
64
+ | uplot box -H -d, -t IRIS
65
+ ```
66
+
67
+ <img src="https://i.imgur.com/sNI4SmN.png" width="75%" height="75%">
68
+
69
+ **colors**
70
+
71
+ ```sh
72
+ uplot colors
73
+ ```
74
+
75
+ <img src="https://i.imgur.com/LxyHQsz.png">
76
+
77
+ ## Usage
78
+
79
+ `uplot --help`
80
+
81
+ ```
82
+ Program: YouPlot (Tools for plotting on the terminal)
83
+ Version: 0.2.7 (using UnicodePlot 0.0.4)
84
+ Source: https://github.com/kojix2/youplot
85
+
86
+ Usage: uplot <command> [options] <in.tsv>
87
+
88
+ Commands:
89
+ barplot bar
90
+ histogram hist
91
+ lineplot line
92
+ lineplots lines
93
+ scatter s
94
+ density d
95
+ boxplot box
96
+ colors show the list of available colors
97
+
98
+ count c baplot based on the number of occurrences
99
+ (slower than `sort | uniq -c | sort -n -k1`)
100
+
101
+ Options:
102
+ -O, --pass [VAL] file to output standard input data to [stdout]
103
+ for inserting YouPlot in the middle of Unix pipes
104
+ -o, --output VAL file to output results to [stderr]
105
+ -d, --delimiter VAL use DELIM instead of TAB for field delimiter
106
+ -H, --headers specify that the input has header row
107
+ -T, --transpose transpose the axes of the input data
108
+ -t, --title VAL print string on the top of plot
109
+ -x, --xlabel VAL print string on the bottom of the plot
110
+ -y, --ylabel VAL print string on the far left of the plot
111
+ -w, --width VAL number of characters per row
112
+ -h, --height VAL number of rows
113
+ -b, --border VAL specify the style of the bounding box
114
+ -m, --margin VAL number of spaces to the left of the plot
115
+ -p, --padding VAL space of the left and right of the plot
116
+ -c, --color VAL color of the drawing
117
+ --[no-]labels hide the labels
118
+ --fmt VAL xyxy : header is like x1, y1, x2, y2, x3, y3...
119
+ xyy : header is like x, y1, y2, y2, y3...
120
+ ```
121
+
122
+ Use `--help` to print command-specific options.
123
+
124
+ `uplot hist --help`
125
+
126
+ ```
127
+ Usage: uplot histogram [options] <in.tsv>
128
+
129
+ Options for histogram:
130
+ --symbol VAL character to be used to plot the bars
131
+ --closed VAL
132
+ -n, --nbins VAL approximate number of bins
133
+
134
+ Options:
135
+ ...
136
+ ```
137
+
138
+ ## Development
139
+
140
+ Let's keep it simple.
141
+
142
+ ## Contributing
143
+
144
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/kojix2/youplot](https://github.com/kojix2/youplot).
145
+
146
+ ## License
147
+
148
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'youplot'
5
+
6
+ YouPlot::Command.new.run
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'youplot'
5
+
6
+ YouPlot::Command.new.run
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unicode_plot'
4
+ require 'youplot/version'
5
+ require 'youplot/preprocessing'
6
+ require 'youplot/command'
7
+
8
+ module YouPlot
9
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unicode_plot'
4
+
5
+ module YouPlot
6
+ # plotting functions.
7
+ module Backends
8
+ module UnicodePlotBackend
9
+ module_function
10
+
11
+ def barplot(data, params, count: false)
12
+ headers = data.headers
13
+ series = data.series
14
+ # `uplot count`
15
+ if count
16
+ series = Preprocessing.count_values(series[0])
17
+ params.title = headers[0] if headers
18
+ end
19
+ if series.size == 1
20
+ # If there is only one series, use the line number for label.
21
+ params.title ||= headers[0] if headers
22
+ labels = Array.new(series[0].size) { |i| (i + 1).to_s }
23
+ values = series[0].map(&:to_f)
24
+ else
25
+ params.title ||= headers[1] if headers
26
+ labels = series[0]
27
+ values = series[1].map(&:to_f)
28
+ end
29
+ UnicodePlot.barplot(labels, values, **params.to_hc)
30
+ end
31
+
32
+ def histogram(data, params)
33
+ headers = data.headers
34
+ series = data.series
35
+ params.title ||= data.headers[0] if headers
36
+ values = series[0].map(&:to_f)
37
+ UnicodePlot.histogram(values, **params.to_hc)
38
+ end
39
+
40
+ def line(data, params)
41
+ headers = data.headers
42
+ series = data.series
43
+ if series.size == 1
44
+ # If there is only one series, it is assumed to be sequential data.
45
+ params.ylabel ||= headers[0] if headers
46
+ y = series[0].map(&:to_f)
47
+ UnicodePlot.lineplot(y, **params.to_hc)
48
+ else
49
+ # If there are 2 or more series,
50
+ # assume that the first 2 series are the x and y series respectively.
51
+ if headers
52
+ params.xlabel ||= headers[0]
53
+ params.ylabel ||= headers[1]
54
+ end
55
+ x = series[0].map(&:to_f)
56
+ y = series[1].map(&:to_f)
57
+ UnicodePlot.lineplot(x, y, **params.to_hc)
58
+ end
59
+ end
60
+
61
+ def get_method2(method1)
62
+ "#{method1}!".to_sym
63
+ end
64
+
65
+ def plot_xyy(data, method1, params)
66
+ headers = data.headers
67
+ series = data.series
68
+ method2 = get_method2(method1)
69
+ series.map! { |s| s.map(&:to_f) }
70
+ if headers
71
+ params.name ||= headers[1]
72
+ params.xlabel ||= headers[0]
73
+ end
74
+ params.ylim ||= series[1..-1].flatten.minmax # why need?
75
+ plot = UnicodePlot.public_send(method1, series[0], series[1], **params.to_hc)
76
+ 2.upto(series.size - 1) do |i|
77
+ UnicodePlot.public_send(method2, plot, series[0], series[i], name: headers&.[](i))
78
+ end
79
+ plot
80
+ end
81
+
82
+ def plot_xyxy(data, method1, params)
83
+ headers = data.headers
84
+ series = data.series
85
+ method2 = get_method2(method1)
86
+ series.map! { |s| s.map(&:to_f) }
87
+ series = series.each_slice(2).to_a
88
+ params.name ||= headers[0] if headers
89
+ params.xlim = series.map(&:first).flatten.minmax # why need?
90
+ params.ylim = series.map(&:last).flatten.minmax # why need?
91
+ x1, y1 = series.shift
92
+ plot = UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
93
+ series.each_with_index do |(xi, yi), i|
94
+ UnicodePlot.public_send(method2, plot, xi, yi, name: headers&.[]((i + 1) * 2))
95
+ end
96
+ plot
97
+ end
98
+
99
+ def plot_fmt(data, fmt, method1, params)
100
+ case fmt
101
+ when 'xyy'
102
+ plot_xyy(data, method1, params)
103
+ when 'xyxy'
104
+ plot_xyxy(data, method1, params)
105
+ else
106
+ raise "Unknown format: #{fmt}"
107
+ end
108
+ end
109
+
110
+ def lines(data, params, fmt = 'xyy')
111
+ check_series_size(data, fmt)
112
+ plot_fmt(data, fmt, :lineplot, params)
113
+ end
114
+
115
+ def scatter(data, params, fmt = 'xyy')
116
+ check_series_size(data, fmt)
117
+ plot_fmt(data, fmt, :scatterplot, params)
118
+ end
119
+
120
+ def density(data, params, fmt = 'xyy')
121
+ check_series_size(data, fmt)
122
+ plot_fmt(data, fmt, :densityplot, params)
123
+ end
124
+
125
+ def boxplot(data, params)
126
+ headers = data.headers
127
+ series = data.series
128
+ headers ||= (1..series.size).map(&:to_s)
129
+ series.map! { |s| s.map(&:to_f) }
130
+ UnicodePlot.boxplot(headers, series, **params.to_hc)
131
+ end
132
+
133
+ def colors(color_names = false)
134
+ UnicodePlot::StyledPrinter::TEXT_COLORS.each do |k, v|
135
+ print v
136
+ print k
137
+ unless color_names
138
+ print "\t"
139
+ print ' ●'
140
+ end
141
+ print "\033[0m"
142
+ print "\t"
143
+ end
144
+ puts
145
+ end
146
+
147
+ def check_series_size(data, fmt)
148
+ series = data.series
149
+ if series.size == 1
150
+ warn 'youplot: There is only one series of input data. Please check the delimiter.'
151
+ warn ''
152
+ warn " Headers: \e[35m#{data.headers.inspect}\e[0m"
153
+ warn " The first item is: \e[35m\"#{series[0][0]}\"\e[0m"
154
+ warn " The last item is : \e[35m\"#{series[0][-1]}\"\e[0m"
155
+ exit 1
156
+ end
157
+ if fmt == 'xyxy' && series.size.odd?
158
+ warn 'YouPlot: In the xyxy format, the number of series must be even.'
159
+ warn ''
160
+ warn " Number of series: \e[35m#{series.size}\e[0m"
161
+ warn " Headers: \e[35m#{data.headers.inspect}\e[0m"
162
+ exit 1
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'preprocessing'
4
+ require_relative 'command/parser'
5
+
6
+ # FIXME
7
+ require_relative 'backends/unicode_plot_backend'
8
+
9
+ module YouPlot
10
+ Data = Struct.new(:headers, :series)
11
+
12
+ class Command
13
+ attr_accessor :params
14
+ attr_reader :data, :fmt, :parser
15
+
16
+ def initialize(argv = ARGV)
17
+ @argv = argv
18
+ @params = Params.new
19
+ @parser = Parser.new
20
+ @backend = YouPlot::Backends::UnicodePlotBackend
21
+ end
22
+
23
+ def run
24
+ parser.parse_options(@argv)
25
+ command = parser.command
26
+ params = parser.params
27
+ delimiter = parser.delimiter
28
+ transpose = parser.transpose
29
+ headers = parser.headers
30
+ pass = parser.pass
31
+ output = parser.output
32
+ fmt = parser.fmt
33
+ @debug = parser.debug
34
+
35
+ if command == :colors
36
+ @backend.colors(parser.color_names)
37
+ exit
38
+ end
39
+
40
+ # Sometimes the input file does not end with a newline code.
41
+ while (input = Kernel.gets(nil))
42
+ input.freeze
43
+ @data = Preprocessing.input(input, delimiter, headers, transpose)
44
+ pp @data if @debug
45
+ plot = case command
46
+ when :bar, :barplot
47
+ @backend.barplot(data, params)
48
+ when :count, :c
49
+ @backend.barplot(data, params, count: true)
50
+ when :hist, :histogram
51
+ @backend.histogram(data, params)
52
+ when :line, :lineplot
53
+ @backend.line(data, params)
54
+ when :lines, :lineplots
55
+ @backend.lines(data, params, fmt)
56
+ when :scatter, :s
57
+ @backend.scatter(data, params, fmt)
58
+ when :density, :d
59
+ @backend.density(data, params, fmt)
60
+ when :box, :boxplot
61
+ @backend.boxplot(data, params)
62
+ else
63
+ raise "unrecognized plot_type: #{command}"
64
+ end
65
+
66
+ case output
67
+ when IO
68
+ plot.render(output)
69
+ else
70
+ File.open(output, 'w') do |f|
71
+ plot.render(f)
72
+ end
73
+ end
74
+
75
+ case pass
76
+ when IO
77
+ pass.print(input)
78
+ else
79
+ if pass
80
+ File.open(pass, 'w') do |f|
81
+ f.print(input)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,37 @@
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
+ Params = 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
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require_relative 'params'
5
+
6
+ module YouPlot
7
+ class Command
8
+ class Parser
9
+ attr_reader :command, :params,
10
+ :delimiter, :transpose, :headers, :pass, :output, :fmt,
11
+ :color_names, :debug
12
+
13
+ def initialize
14
+ @command = nil
15
+ @params = Params.new
16
+
17
+ @delimiter = "\t"
18
+ @transpose = false
19
+ @headers = nil
20
+ @pass = false
21
+ @output = $stderr
22
+ @fmt = 'xyy'
23
+ @debug = false
24
+ @color_names = false
25
+ end
26
+
27
+ def create_default_parser
28
+ OptionParser.new do |opt|
29
+ opt.program_name = 'YouPlot'
30
+ opt.version = YouPlot::VERSION
31
+ opt.summary_width = 24
32
+ opt.on_tail('') # Add a blank line at the end
33
+ opt.separator('')
34
+ opt.on('Common options:')
35
+ opt.on('-O', '--pass [VAL]', 'file to output standard input data to [stdout]',
36
+ 'for inserting YouPlot in the middle of Unix pipes') do |v|
37
+ @pass = v || $stdout
38
+ end
39
+ opt.on('-o', '--output VAL', 'file to output results to [stderr]') do |v|
40
+ @output = v
41
+ end
42
+ opt.on('-d', '--delimiter VAL', String, 'use DELIM instead of TAB for field delimiter') do |v|
43
+ @delimiter = v
44
+ end
45
+ opt.on('-H', '--headers', TrueClass, 'specify that the input has header row') do |v|
46
+ @headers = v
47
+ end
48
+ opt.on('-T', '--transpose', TrueClass, 'transpose the axes of the input data') do |v|
49
+ @transpose = v
50
+ end
51
+ opt.on('-t', '--title VAL', String, 'print string on the top of plot') do |v|
52
+ params.title = v
53
+ end
54
+ opt.on('-x', '--xlabel VAL', String, 'print string on the bottom of the plot') do |v|
55
+ params.xlabel = v
56
+ end
57
+ opt.on('-y', '--ylabel VAL', String, 'print string on the far left of the plot') do |v|
58
+ params.ylabel = v
59
+ end
60
+ opt.on('-w', '--width VAL', Integer, 'number of characters per row') do |v|
61
+ params.width = v
62
+ end
63
+ opt.on('-h', '--height VAL', Numeric, 'number of rows') do |v|
64
+ params.height = v
65
+ end
66
+ opt.on('-b', '--border VAL', String, 'specify the style of the bounding box') do |v|
67
+ params.border = v.to_sym
68
+ end
69
+ opt.on('-m', '--margin VAL', Numeric, 'number of spaces to the left of the plot') do |v|
70
+ params.margin = v
71
+ end
72
+ opt.on('-p', '--padding VAL', Numeric, 'space of the left and right of the plot') do |v|
73
+ params.padding = v
74
+ end
75
+ opt.on('-c', '--color VAL', String, 'color of the drawing') do |v|
76
+ params.color = v =~ /\A[0-9]+\z/ ? v.to_i : v.to_sym
77
+ end
78
+ opt.on('--[no-]labels', TrueClass, 'hide the labels') do |v|
79
+ params.labels = v
80
+ end
81
+ opt.on('--fmt VAL', String, 'xyxy : header is like x1, y1, x2, y2, x3, y3...', 'xyy : header is like x, y1, y2, y2, y3...') do |v|
82
+ @fmt = v
83
+ end
84
+ # Optparse adds the help option, but it doesn't show up in usage.
85
+ # This is why you need the code below.
86
+ opt.on('--help', 'print sub-command help menu') do
87
+ puts opt.help
88
+ exit
89
+ end
90
+ opt.on('--debug', TrueClass, 'print preprocessed data') do |v|
91
+ @debug = v
92
+ end
93
+ yield opt if block_given?
94
+ end
95
+ end
96
+
97
+ def main_parser
98
+ @main_parser ||= create_default_parser do |main_parser|
99
+ # Here, help message is stored in the banner.
100
+ # Because help of main_parser may be referred by `sub_parser`.
101
+
102
+ main_parser.banner = \
103
+ <<~MSG
104
+
105
+ Program: YouPlot (Tools for plotting on the terminal)
106
+ Version: #{YouPlot::VERSION} (using UnicodePlot #{UnicodePlot::VERSION})
107
+ Source: https://github.com/kojix2/youplot
108
+
109
+ Usage: uplot <command> [options] <in.tsv>
110
+
111
+ Commands:
112
+ barplot bar draw a horizontal barplot
113
+ histogram hist draw a horizontal histogram
114
+ lineplot line draw a line chart
115
+ lineplots lines draw a line chart with multiple series
116
+ scatter s draw a scatter plot
117
+ density d draw a density plot
118
+ boxplot box draw a horizontal boxplot
119
+ colors show the list of available colors
120
+
121
+ count c draw a baplot based on the number of
122
+ occurrences (slow)
123
+
124
+ General options:
125
+ --help print command specific help menu
126
+ --version print the version of YouPlot
127
+ MSG
128
+
129
+ # Actually, main_parser can take common optional arguments.
130
+ # However, these options dose not be shown in the help menu.
131
+ # I think the main help should be simple.
132
+ main_parser.on('--help', 'print sub-command help menu') do
133
+ puts main_parser.banner
134
+ puts
135
+ exit
136
+ end
137
+ end
138
+ end
139
+
140
+ def sub_parser
141
+ @sub_parser ||= create_default_parser do |parser|
142
+ parser.banner = <<~MSG
143
+
144
+ Usage: YouPlot #{command} [options] <in.tsv>
145
+
146
+ Options for #{command}:
147
+ MSG
148
+
149
+ case command
150
+
151
+ # If you type only `uplot` in the terminal.
152
+ when nil
153
+ warn main_parser.banner
154
+ warn "\n"
155
+ exit 1
156
+
157
+ when :barplot, :bar
158
+ parser.on_head('--symbol VAL', String, 'character to be used to plot the bars') do |v|
159
+ params.symbol = v
160
+ end
161
+ parser.on_head('--xscale VAL', String, 'axis scaling') do |v|
162
+ params.xscale = v
163
+ end
164
+
165
+ when :count, :c
166
+ parser.on_head('--symbol VAL', String, 'character to be used to plot the bars') do |v|
167
+ params.symbol = v
168
+ end
169
+
170
+ when :histogram, :hist
171
+ parser.on_head('-n', '--nbins VAL', Numeric, 'approximate number of bins') do |v|
172
+ params.nbins = v
173
+ end
174
+ parser.on_head('--closed VAL', String) do |v|
175
+ params.closed = v
176
+ end
177
+ parser.on_head('--symbol VAL', String, 'character to be used to plot the bars') do |v|
178
+ params.symbol = v
179
+ end
180
+
181
+ when :lineplot, :line
182
+ parser.on_head('--canvas VAL', String, 'type of canvas') do |v|
183
+ params.canvas = v
184
+ end
185
+ parser.on_head('--xlim VAL', Array, 'plotting range for the x coordinate') do |v|
186
+ params.xlim = v.take(2)
187
+ end
188
+ parser.on_head('--ylim VAL', Array, 'plotting range for the y coordinate') do |v|
189
+ params.ylim = v.take(2)
190
+ end
191
+
192
+ when :lineplots, :lines
193
+ parser.on_head('--canvas VAL', String) do |v|
194
+ params.canvas = v
195
+ end
196
+ parser.on_head('--xlim VAL', Array, 'plotting range for the x coordinate') do |v|
197
+ params.xlim = v.take(2)
198
+ end
199
+ parser.on_head('--ylim VAL', Array, 'plotting range for the y coordinate') do |v|
200
+ params.ylim = v.take(2)
201
+ end
202
+
203
+ when :scatter, :s
204
+ parser.on_head('--canvas VAL', String) do |v|
205
+ params.canvas = v
206
+ end
207
+ parser.on_head('--xlim VAL', Array, 'plotting range for the x coordinate') do |v|
208
+ params.xlim = v.take(2)
209
+ end
210
+ parser.on_head('--ylim VAL', Array, 'plotting range for the y coordinate') do |v|
211
+ params.ylim = v.take(2)
212
+ end
213
+
214
+ when :density, :d
215
+ parser.on_head('--grid', TrueClass) do |v|
216
+ params.grid = v
217
+ end
218
+ parser.on_head('--xlim VAL', Array, 'plotting range for the x coordinate') do |v|
219
+ params.xlim = v.take(2)
220
+ end
221
+ parser.on_head('--ylim VAL', Array, 'plotting range for the y coordinate') do |v|
222
+ params.ylim = v.take(2)
223
+ end
224
+
225
+ when :boxplot, :box
226
+ parser.on_head('--xlim VAL', Array, 'plotting range for the x coordinate') do |v|
227
+ params.xlim = v.take(2)
228
+ end
229
+
230
+ when :colors
231
+ parser.on_head('-n', '--names', 'show color names only', TrueClass) do |v|
232
+ @color_names = v
233
+ end
234
+
235
+ else
236
+ warn "uplot: unrecognized command '#{command}'"
237
+ exit 1
238
+ end
239
+ end
240
+ end
241
+
242
+ def parse_options(argv = ARGV)
243
+ begin
244
+ main_parser.order!(argv)
245
+ rescue OptionParser::ParseError => e
246
+ warn "uplot: #{e.message}"
247
+ exit 1
248
+ end
249
+
250
+ @command = argv.shift&.to_sym
251
+
252
+ begin
253
+ sub_parser.parse!(argv)
254
+ rescue OptionParser::ParseError => e
255
+ warn "uplot: #{e.message}"
256
+ exit 1
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+
5
+ module YouPlot
6
+ module Preprocessing
7
+ module_function
8
+
9
+ def input(input, delimiter, headers, transpose)
10
+ arr = parse_as_csv(input, delimiter)
11
+ headers = get_headers(arr, headers, transpose)
12
+ series = get_series(arr, headers, transpose)
13
+ if headers.nil?
14
+ Data.new(headers, series)
15
+ else
16
+ if headers.include?(nil)
17
+ warn "\e[35mHeaders contains nil in it.\e[0m"
18
+ elsif headers.include? ''
19
+ warn "\e[35mHeaders contains \"\" in it.\e[0m"
20
+ end
21
+ h_size = headers.size
22
+ s_size = series.size
23
+ if h_size == s_size
24
+ Data.new(headers, series)
25
+ elsif h_size > s_size
26
+ warn "\e[35mThe number of headers is greater than the number of series.\e[0m"
27
+ exit 1
28
+ elsif h_size < s_size
29
+ warn "\e[35mThe number of headers is less than the number of series.\e[0m"
30
+ exit 1
31
+ end
32
+ end
33
+ end
34
+
35
+ def parse_as_csv(input, delimiter)
36
+ CSV.parse(input, col_sep: delimiter)
37
+ .delete_if do |i|
38
+ i == [] or i.all? nil
39
+ end
40
+ end
41
+
42
+ # Transpose different sized ruby arrays
43
+ # https://stackoverflow.com/q/26016632
44
+ def transpose2(arr)
45
+ Array.new(arr.map(&:length).max) { |i| arr.map { |e| e[i] } }
46
+ end
47
+
48
+ def get_headers(arr, headers, transpose)
49
+ if headers
50
+ if transpose
51
+ arr.map(&:first)
52
+ else
53
+ arr[0]
54
+ end
55
+ end
56
+ end
57
+
58
+ def get_series(arr, headers, transpose)
59
+ if transpose
60
+ if headers
61
+ arr.map { |row| row[1..-1] }
62
+ else
63
+ arr
64
+ end
65
+ elsif headers
66
+ transpose2(arr[1..-1])
67
+ else
68
+ transpose2(arr)
69
+ end
70
+ end
71
+
72
+ def count_values(arr)
73
+ # tally was added in Ruby 2.7
74
+ if Enumerable.method_defined? :tally
75
+ arr.tally
76
+ else
77
+ # https://github.com/marcandre/backports
78
+ arr.each_with_object(Hash.new(0)) { |item, res| res[item] += 1 }
79
+ .tap { |h| h.default = nil }
80
+ end
81
+ .sort { |a, b| a[1] <=> b[1] }
82
+ .reverse
83
+ .transpose
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YouPlot
4
+ VERSION = '0.3.0'
5
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: youplot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - kojix2
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-11-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: unicode_plot
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
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'
97
+ description: "Create ASCII charts on the terminal with data from standard streams
98
+ in the \npipeline. \n"
99
+ email:
100
+ - 2xijok@gmail.com
101
+ executables:
102
+ - uplot
103
+ - youplot
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - LICENSE.txt
108
+ - README.md
109
+ - exe/uplot
110
+ - exe/youplot
111
+ - lib/youplot.rb
112
+ - lib/youplot/backends/unicode_plot_backend.rb
113
+ - lib/youplot/command.rb
114
+ - lib/youplot/command/params.rb
115
+ - lib/youplot/command/parser.rb
116
+ - lib/youplot/preprocessing.rb
117
+ - lib/youplot/version.rb
118
+ homepage: https://github.com/kojix2/youplot
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 2.4.0
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 3.1.4
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Create Ascii charts on your terminal.
141
+ test_files: []