youplot 0.3.4 → 0.4.2
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 +4 -4
- data/README.md +47 -31
- data/lib/youplot/backends/processing.rb +22 -10
- data/lib/youplot/backends/{unicode_plot_backend.rb → unicode_plot.rb} +31 -34
- data/lib/youplot/command.rb +61 -16
- data/lib/youplot/dsv.rb +58 -48
- data/lib/youplot/options.rb +18 -0
- data/lib/youplot/parameters.rb +34 -0
- data/lib/youplot/parser.rb +310 -0
- data/lib/youplot/version.rb +1 -1
- data/lib/youplot.rb +4 -4
- metadata +8 -8
- data/lib/youplot/command/options.rb +0 -19
- data/lib/youplot/command/parser.rb +0 -297
- data/lib/youplot/command/plot_params.rb +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04d898a5d15b1a38a92bf3823b6c1a971d17acf6000740867c09f35fa78a2a06
|
|
4
|
+
data.tar.gz: 9c5353687b33c75e953458fc824b1fa7386f980fbd9b11994f94bb8f06e6a387
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5677d91bdd78e7de332557434fe377d316e302abb7476ee99fef0fab3dee03a40c702c10d7e1b70cc1cb22e8c5049236408849cc3f208c8372c70ce2dcb251d6
|
|
7
|
+
data.tar.gz: a5948c77d03ec831357c9774e8ef3346b7684944b606b5598620e4723fd233160af6a32377436d1ef4271655cbd3078572cb5f88c14de4ddd63043778dc99c00
|
data/README.md
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
<
|
|
2
|
-
<img src="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[
|
|
7
|
+
[](https://badge.fury.io/rb/youplot)
|
|
8
|
+
[](https://rubydoc.info/gems/youplot)
|
|
9
|
+
[](LICENSE.txt)
|
|
10
|
+
[](https://zenodo.org/badge/latestdoi/283230219)
|
|
11
|
+
|
|
12
|
+
YouPlot is a command line tool that draws plots in a terminal.
|
|
13
|
+
|
|
14
|
+
:bar_chart: Powered by [UnicodePlot](https://github.com/red-data-tools/unicode_plot.rb)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
16
19
|
|
|
@@ -106,8 +109,8 @@ In this example, YouPlot counts the number of chromosomes where the gene is loca
|
|
|
106
109
|
|
|
107
110
|
```sh
|
|
108
111
|
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
|
|
112
|
+
| grep -v '#' | grep 'gene' | cut -f1 \
|
|
113
|
+
| uplot count -t "The number of human gene annotations per chromosome" -c blue
|
|
111
114
|
```
|
|
112
115
|
|
|
113
116
|
<p align="center">
|
|
@@ -119,7 +122,7 @@ This is fine in most cases, as long as the data size is small. If you want to vi
|
|
|
119
122
|
|
|
120
123
|
```sh
|
|
121
124
|
cat gencode.v35.annotation.gff3 | grep -v '#' | grep 'gene' | cut -f1 \
|
|
122
|
-
|sort | uniq -c | sort -nrk2 | awk '{print $2,$1}' \
|
|
125
|
+
| sort | uniq -c | sort -nrk2 | awk '{print $2,$1}' \
|
|
123
126
|
| uplot bar -d ' ' -t "The number of human gene annotations per chromosome" -c blue
|
|
124
127
|
```
|
|
125
128
|
|
|
@@ -130,15 +133,15 @@ cat gencode.v35.annotation.gff3 | grep -v '#' | grep 'gene' | cut -f1 \
|
|
|
130
133
|
Wouldn't it be a pain to have to run R, Python, Julia, gnuplot or whatever REPL just to check your data?
|
|
131
134
|
YouPlot is a command line tool for this purpose. With YouPlot, you can continue working without leaving your terminal and shell.
|
|
132
135
|
|
|
133
|
-
###
|
|
136
|
+
### How to use YouPlot?
|
|
134
137
|
|
|
135
138
|
`uplot` is the shortened form of `youplot`. You can use either.
|
|
136
139
|
|
|
137
|
-
|
|
|
138
|
-
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
140
|
+
| Command | Description |
|
|
141
|
+
|------------------------------------------------|-----------------------------------|
|
|
142
|
+
| `cat data.tsv \| uplot <command> [options]` | Take input from stdin |
|
|
143
|
+
| `uplot <command> [options] data.tsv ...` | Take input from files |
|
|
144
|
+
| `pipeline1 \| uplot <command> -O \| pipeline2` | Outputs data from stdin to stdout |
|
|
142
145
|
|
|
143
146
|
### Where to output the plot?
|
|
144
147
|
|
|
@@ -148,7 +151,8 @@ The output file or stream for the plot can be specified with the `-o` option.
|
|
|
148
151
|
### Where to output the input data?
|
|
149
152
|
|
|
150
153
|
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.
|
|
154
|
+
The `-O` option, with no arguments, outputs the input data directly to the standard output.
|
|
155
|
+
This is useful when passing data to a subsequent pipeline.
|
|
152
156
|
|
|
153
157
|
### What types of plots are available?
|
|
154
158
|
|
|
@@ -176,11 +180,21 @@ If your input data contains a header line, you need to specify the `-H` option.
|
|
|
176
180
|
|
|
177
181
|
### How to specify the delimiter?
|
|
178
182
|
|
|
179
|
-
Use the `-d` option. To specify a blank space, you can use `uplot bar -d ' ' data.txt`.
|
|
183
|
+
Use the `-d` option. To specify a blank space, you can use `uplot bar -d ' ' data.txt`.
|
|
184
|
+
You do not need to use `-d` option for tab-delimited text since the default value is tab.
|
|
180
185
|
|
|
181
186
|
### Is there a way to specify a column as the x-axis or y-axis?
|
|
182
187
|
|
|
183
|
-
Not yet.
|
|
188
|
+
Not yet.
|
|
189
|
+
YouPlot treats the first column as the X axis and the second column as the Y axis.
|
|
190
|
+
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.
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
* `--fmt xyy` `--fmt xyxy` `--fmt yx` options give you a few more choices.
|
|
194
|
+
See `youplot <command> --help` for more details.
|
|
195
|
+
|
|
196
|
+
* Use `awk '{print $2, $1}'` to swap lines.
|
|
197
|
+
* Use `paste` to concatenate series.
|
|
184
198
|
|
|
185
199
|
### How to plot real-time data?
|
|
186
200
|
|
|
@@ -216,18 +230,21 @@ uplot colors
|
|
|
216
230
|
|
|
217
231
|
## Contributing
|
|
218
232
|
|
|
219
|
-
|
|
220
|
-
|
|
233
|
+
YouPlot is a library under development, so even small improvements like typofix are welcome!
|
|
234
|
+
Please feel free to send us your pull requests.
|
|
235
|
+
|
|
236
|
+
* [Report bugs](https://github.com/red-data-tools/YouPlot/issues)
|
|
237
|
+
* Fix bugs and [submit pull requests](https://github.com/red-data-tools/YouPlot/pulls)
|
|
221
238
|
* Write, clarify, or fix documentation
|
|
222
239
|
* English corrections by native speakers are welcome.
|
|
223
240
|
* Suggest or add new features
|
|
224
|
-
|
|
241
|
+
* Make a donation
|
|
225
242
|
|
|
226
243
|
### Development
|
|
227
244
|
|
|
228
245
|
```sh
|
|
229
|
-
|
|
230
|
-
|
|
246
|
+
# fork the main repository by clicking the Fork button.
|
|
247
|
+
git clone https://github.com/your_name/YouPlot
|
|
231
248
|
bundle install # Install the gem dependencies
|
|
232
249
|
bundle exec rake test # Run the test
|
|
233
250
|
bundle exec rake install # Installation from source code
|
|
@@ -235,7 +252,6 @@ bundle exec rake install # Installation from source code
|
|
|
235
252
|
|
|
236
253
|
### Acknowledgements
|
|
237
254
|
|
|
238
|
-
* [Red Data Tools](https://github.com/red-data-tools) - Technical support
|
|
239
255
|
* [sampo grafiikka](https://jypg.net/sampo_grafiikka) - Project logo creation
|
|
240
256
|
* [yutaas](https://github.com/yutaas) - English proofreading
|
|
241
257
|
|
|
@@ -6,18 +6,30 @@ module YouPlot
|
|
|
6
6
|
module Processing
|
|
7
7
|
module_function
|
|
8
8
|
|
|
9
|
-
def count_values(arr, tally: true)
|
|
9
|
+
def count_values(arr, tally: true, reverse: false)
|
|
10
10
|
# tally was added in Ruby 2.7
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
result = \
|
|
12
|
+
if tally && Enumerable.method_defined?(:tally)
|
|
13
|
+
arr.tally
|
|
14
|
+
else
|
|
15
|
+
# value_counts Enumerable::Statistics
|
|
16
|
+
arr.value_counts(dropna: false)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# sorting
|
|
20
|
+
result = result.sort do |a, b|
|
|
21
|
+
# compare values
|
|
22
|
+
r = b[1] <=> a[1]
|
|
23
|
+
# If the values are the same, compare by name
|
|
24
|
+
r = a[0] <=> b[0] if r.zero?
|
|
25
|
+
r
|
|
17
26
|
end
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
|
|
28
|
+
# --reverse option
|
|
29
|
+
result.reverse! if reverse
|
|
30
|
+
|
|
31
|
+
# prepare for barplot
|
|
32
|
+
result.transpose
|
|
21
33
|
end
|
|
22
34
|
end
|
|
23
35
|
end
|
|
@@ -1,22 +1,25 @@
|
|
|
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
|
|
12
|
+
module UnicodePlot
|
|
10
13
|
class Error < StandardError; end
|
|
11
14
|
|
|
12
15
|
module_function
|
|
13
16
|
|
|
14
|
-
def barplot(data, params, fmt = nil, count: false)
|
|
17
|
+
def barplot(data, params, fmt = nil, count: false, reverse: false)
|
|
15
18
|
headers = data.headers
|
|
16
19
|
series = data.series
|
|
17
20
|
# `uplot count`
|
|
18
21
|
if count
|
|
19
|
-
series = Processing.count_values(series[0])
|
|
22
|
+
series = Processing.count_values(series[0], reverse: reverse)
|
|
20
23
|
params.title = headers[0] if headers
|
|
21
24
|
end
|
|
22
25
|
if series.size == 1
|
|
@@ -39,19 +42,7 @@ module YouPlot
|
|
|
39
42
|
labels = series[x_col]
|
|
40
43
|
values = series[y_col].map(&:to_f)
|
|
41
44
|
end
|
|
42
|
-
|
|
43
|
-
UnicodePlot.barplot(labels, values, **params.to_hc)
|
|
44
|
-
# UnicodePlot error:
|
|
45
|
-
# All values have to be positive. Negative bars are not supported.
|
|
46
|
-
rescue ArgumentError => e
|
|
47
|
-
if YouPlot.run_as_executable?
|
|
48
|
-
warn e.backtrace[0]
|
|
49
|
-
warn "\e[35m#{e}\e[0m"
|
|
50
|
-
exit 1
|
|
51
|
-
else
|
|
52
|
-
raise e
|
|
53
|
-
end
|
|
54
|
-
end
|
|
45
|
+
::UnicodePlot.barplot(labels, values, **params.to_hc)
|
|
55
46
|
end
|
|
56
47
|
|
|
57
48
|
def histogram(data, params)
|
|
@@ -59,7 +50,7 @@ module YouPlot
|
|
|
59
50
|
series = data.series
|
|
60
51
|
params.title ||= data.headers[0] if headers
|
|
61
52
|
values = series[0].map(&:to_f)
|
|
62
|
-
UnicodePlot.histogram(values, **params.to_hc)
|
|
53
|
+
::UnicodePlot.histogram(values, **params.to_hc)
|
|
63
54
|
end
|
|
64
55
|
|
|
65
56
|
def line(data, params, fmt = nil)
|
|
@@ -69,7 +60,7 @@ module YouPlot
|
|
|
69
60
|
# If there is only one series, it is assumed to be sequential data.
|
|
70
61
|
params.ylabel ||= headers[0] if headers
|
|
71
62
|
y = series[0].map(&:to_f)
|
|
72
|
-
UnicodePlot.lineplot(y, **params.to_hc)
|
|
63
|
+
::UnicodePlot.lineplot(y, **params.to_hc)
|
|
73
64
|
else
|
|
74
65
|
# If there are 2 or more series...
|
|
75
66
|
if fmt == 'yx'
|
|
@@ -87,7 +78,7 @@ module YouPlot
|
|
|
87
78
|
end
|
|
88
79
|
x = series[x_col].map(&:to_f)
|
|
89
80
|
y = series[y_col].map(&:to_f)
|
|
90
|
-
UnicodePlot.lineplot(x, y, **params.to_hc)
|
|
81
|
+
::UnicodePlot.lineplot(x, y, **params.to_hc)
|
|
91
82
|
end
|
|
92
83
|
end
|
|
93
84
|
|
|
@@ -106,9 +97,9 @@ module YouPlot
|
|
|
106
97
|
end
|
|
107
98
|
params.xlim ||= series[0].flatten.minmax # why need?
|
|
108
99
|
params.ylim ||= series[1..-1].flatten.minmax # why need?
|
|
109
|
-
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)
|
|
110
101
|
2.upto(series.size - 1) do |i|
|
|
111
|
-
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))
|
|
112
103
|
end
|
|
113
104
|
plot
|
|
114
105
|
end
|
|
@@ -124,9 +115,9 @@ module YouPlot
|
|
|
124
115
|
params.xlim ||= series2.map(&:first).flatten.minmax # why need?
|
|
125
116
|
params.ylim ||= series2.map(&:last).flatten.minmax # why need?
|
|
126
117
|
x1, y1 = series2.shift
|
|
127
|
-
plot = UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
|
|
118
|
+
plot = ::UnicodePlot.public_send(method1, x1, y1, **params.to_hc)
|
|
128
119
|
series2.each_with_index do |(xi, yi), i|
|
|
129
|
-
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))
|
|
130
121
|
end
|
|
131
122
|
plot
|
|
132
123
|
end
|
|
@@ -164,13 +155,13 @@ module YouPlot
|
|
|
164
155
|
series = data.series
|
|
165
156
|
headers ||= (1..series.size).map(&:to_s)
|
|
166
157
|
series.map! { |s| s.map(&:to_f) }
|
|
167
|
-
UnicodePlot.boxplot(headers, series, **params.to_hc)
|
|
158
|
+
::UnicodePlot.boxplot(headers, series, **params.to_hc)
|
|
168
159
|
end
|
|
169
160
|
|
|
170
161
|
def colors(color_names = false)
|
|
171
162
|
# FIXME
|
|
172
163
|
s = String.new
|
|
173
|
-
UnicodePlot::StyledPrinter::TEXT_COLORS.each do |k, v|
|
|
164
|
+
::UnicodePlot::StyledPrinter::TEXT_COLORS.each do |k, v|
|
|
174
165
|
s << v
|
|
175
166
|
s << k.to_s
|
|
176
167
|
unless color_names
|
|
@@ -190,18 +181,24 @@ module YouPlot
|
|
|
190
181
|
def check_series_size(data, fmt)
|
|
191
182
|
series = data.series
|
|
192
183
|
if series.size == 1
|
|
193
|
-
warn
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
184
|
+
warn <<~EOS
|
|
185
|
+
youplot: There is only one series of input data. Please check the delimiter.
|
|
186
|
+
|
|
187
|
+
Headers: \e[35m#{data.headers.inspect}\e[0m
|
|
188
|
+
The first item is: \e[35m\"#{series[0][0]}\"\e[0m
|
|
189
|
+
The last item is : \e[35m\"#{series[0][-1]}\"\e[0m
|
|
190
|
+
EOS
|
|
191
|
+
# NOTE: Error messages cannot be colored.
|
|
198
192
|
YouPlot.run_as_executable ? exit(1) : raise(Error)
|
|
199
193
|
end
|
|
200
194
|
if fmt == 'xyxy' && series.size.odd?
|
|
201
|
-
warn
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
195
|
+
warn <<~EOS
|
|
196
|
+
YouPlot: In the xyxy format, the number of series must be even.
|
|
197
|
+
|
|
198
|
+
Number of series: \e[35m#{series.size}\e[0m
|
|
199
|
+
Headers: \e[35m#{data.headers.inspect}\e[0m
|
|
200
|
+
EOS
|
|
201
|
+
# NOTE: Error messages cannot be colored.
|
|
205
202
|
YouPlot.run_as_executable ? exit(1) : raise(Error)
|
|
206
203
|
end
|
|
207
204
|
end
|
data/lib/youplot/command.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'dsv'
|
|
4
|
-
require_relative '
|
|
4
|
+
require_relative 'parser'
|
|
5
5
|
|
|
6
6
|
# FIXME
|
|
7
|
-
require_relative 'backends/
|
|
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::
|
|
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
|
-
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# progressive mode
|
|
44
|
+
if options[:progressive]
|
|
40
45
|
stop = false
|
|
41
46
|
Signal.trap(:INT) { stop = true }
|
|
42
|
-
|
|
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
|
-
|
|
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,13 +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 =
|
|
78
|
+
@data = parse_dsv(input)
|
|
65
79
|
|
|
80
|
+
# Debug mode, show parsed results
|
|
66
81
|
pp @data if options[:debug]
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
# When run as a program instead of a library
|
|
84
|
+
if YouPlot.run_as_executable?
|
|
85
|
+
begin
|
|
86
|
+
plot = create_plot
|
|
87
|
+
rescue ArgumentError => e
|
|
88
|
+
# Show only one line of error.
|
|
89
|
+
warn e.backtrace[0]
|
|
90
|
+
# Show error message in purple
|
|
91
|
+
warn "\e[35m#{e}\e[0m"
|
|
92
|
+
# Explicitly terminated with exit code: 1
|
|
93
|
+
exit 1
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# When running YouPlot as a library (e.g. for testing)
|
|
97
|
+
else
|
|
98
|
+
plot = create_plot
|
|
99
|
+
end
|
|
100
|
+
|
|
69
101
|
output_plot(plot)
|
|
70
102
|
end
|
|
71
103
|
|
|
@@ -85,15 +117,28 @@ module YouPlot
|
|
|
85
117
|
@raw_data << input
|
|
86
118
|
|
|
87
119
|
# FIXME
|
|
88
|
-
@data =
|
|
120
|
+
@data = parse_dsv(@raw_data)
|
|
89
121
|
|
|
90
122
|
plot = create_plot
|
|
91
123
|
output_plot_progressive(plot)
|
|
92
124
|
end
|
|
93
125
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
97
142
|
end
|
|
98
143
|
|
|
99
144
|
def create_plot
|
|
@@ -101,7 +146,7 @@ module YouPlot
|
|
|
101
146
|
when :bar, :barplot
|
|
102
147
|
@backend.barplot(data, params, options[:fmt])
|
|
103
148
|
when :count, :c
|
|
104
|
-
@backend.barplot(data, params, count: true)
|
|
149
|
+
@backend.barplot(data, params, count: true, reverse: options[:reverse])
|
|
105
150
|
when :hist, :histogram
|
|
106
151
|
@backend.histogram(data, params)
|
|
107
152
|
when :line, :lineplot
|
|
@@ -137,9 +182,9 @@ module YouPlot
|
|
|
137
182
|
|
|
138
183
|
def output_plot(plot)
|
|
139
184
|
case options[:output]
|
|
140
|
-
when IO
|
|
185
|
+
when IO, StringIO
|
|
141
186
|
plot.render(options[:output])
|
|
142
|
-
|
|
187
|
+
when String, Tempfile
|
|
143
188
|
File.open(options[:output], 'w') do |f|
|
|
144
189
|
plot.render(f)
|
|
145
190
|
end
|
|
@@ -148,7 +193,7 @@ module YouPlot
|
|
|
148
193
|
|
|
149
194
|
def output_plot_progressive(plot)
|
|
150
195
|
case options[:output]
|
|
151
|
-
when IO
|
|
196
|
+
when IO, StringIO
|
|
152
197
|
# RefactorMe
|
|
153
198
|
out = StringIO.new(String.new)
|
|
154
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|