grada 1.1.1 → 2.0.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.
- data/lib/grada/gnuplot.rb +226 -0
- data/lib/grada.rb +32 -32
- metadata +2 -17
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
class NoGnuPlotExecutableFound < RuntimeError; end
|
4
|
+
|
5
|
+
class Gnuplot
|
6
|
+
def self.candidate?(candidate)
|
7
|
+
return candidate if File::executable? candidate
|
8
|
+
|
9
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
10
|
+
possible_candidate = File::join dir, candidate.strip
|
11
|
+
|
12
|
+
return possible_candidate if File::executable? possible_candidate
|
13
|
+
end
|
14
|
+
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find_exec(bin)
|
19
|
+
bin_list = RUBY_PLATFORM =~ /mswin|mingw/ ? [bin, "#{bin}.exe"] : [bin]
|
20
|
+
|
21
|
+
bin_list.each do |c|
|
22
|
+
exec = candidate?(c)
|
23
|
+
return exec if exec
|
24
|
+
end
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.gnuplot(persist)
|
30
|
+
gnu_exec = find_exec( ENV['RB_GNUPLOT'] || 'gnuplot' )
|
31
|
+
|
32
|
+
raise NoGnuPlotExecutableFound unless gnu_exec
|
33
|
+
|
34
|
+
"#{gnu_exec} #{'-persist' if persist}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.open(persist = true)
|
38
|
+
output = nil
|
39
|
+
gnuplot_cmd = gnuplot(persist)
|
40
|
+
|
41
|
+
IO::popen( gnuplot_cmd, 'w+' ) do |io|
|
42
|
+
yield io
|
43
|
+
io.close_write
|
44
|
+
output = io.read
|
45
|
+
end
|
46
|
+
|
47
|
+
output
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Plot
|
52
|
+
attr_accessor :cmd, :data, :settings, :arbitrary_lines
|
53
|
+
|
54
|
+
QUOTED_METHODS = [ "title", "output", "xlabel", "x2label", "ylabel", "y2label", "clabel", "cblabel", "zlabel" ]
|
55
|
+
|
56
|
+
def initialize(io = nil, cmd = 'plot')
|
57
|
+
@cmd = cmd
|
58
|
+
@settings = []
|
59
|
+
@arbitrary_lines = []
|
60
|
+
@data = []
|
61
|
+
@styles = []
|
62
|
+
yield self if block_given?
|
63
|
+
|
64
|
+
if io
|
65
|
+
io << to_gplot
|
66
|
+
io << store_datasets
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_missing(meth, *args)
|
71
|
+
set meth.id2name, *args
|
72
|
+
end
|
73
|
+
|
74
|
+
def set ( var, value = "" )
|
75
|
+
value = "\"#{value}\"" if QUOTED_METHODS.include? var unless value =~ /^'.*'$/
|
76
|
+
@settings << [ :set, var, value ]
|
77
|
+
end
|
78
|
+
|
79
|
+
def unset (var)
|
80
|
+
@settings << [ :unset, var ]
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_gplot(io = '')
|
84
|
+
@settings.each { |setting| io += setting.map(&:to_s).join(' ') + "\n" }
|
85
|
+
@styles.each{ |style| io += style.to_s + "\n" }
|
86
|
+
@arbitrary_lines.each{ |line| io += line + "\n" }
|
87
|
+
|
88
|
+
io
|
89
|
+
end
|
90
|
+
|
91
|
+
def store_datasets(io = '')
|
92
|
+
if @data.size > 0
|
93
|
+
io += @cmd + " #{ @data.map { |element| element.plot_args }.join(', ') } \n"
|
94
|
+
io += @data.map { |ds| ds.to_gplot }.compact.join("e\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
io
|
98
|
+
end
|
99
|
+
|
100
|
+
def style(&blk)
|
101
|
+
@styles << Style.new(&blk)
|
102
|
+
end
|
103
|
+
|
104
|
+
class Style
|
105
|
+
attr_accessor :linestyle, :linetype, :linewidth, :linecolor, :pointtype, :pointsize, :fill, :index
|
106
|
+
|
107
|
+
alias :ls :linestyle
|
108
|
+
alias :lt :linetype
|
109
|
+
alias :lw :linewidth
|
110
|
+
alias :lc :linecolor
|
111
|
+
alias :pt :pointtype
|
112
|
+
alias :ps :pointsize
|
113
|
+
alias :fs :fill
|
114
|
+
|
115
|
+
STYLES = [:ls, :lt, :lw, :lc, :pt, :ps, :fs]
|
116
|
+
|
117
|
+
def self.increment_index
|
118
|
+
@index ||= 0
|
119
|
+
@index += 1
|
120
|
+
end
|
121
|
+
|
122
|
+
def initialize
|
123
|
+
STYLES.each do |style|
|
124
|
+
send("#{style}=", nil)
|
125
|
+
end
|
126
|
+
|
127
|
+
yield self if block_given?
|
128
|
+
|
129
|
+
self.increment_index
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_s
|
133
|
+
str = "set style line #{@index}"
|
134
|
+
|
135
|
+
STYLES.each do |style|
|
136
|
+
str += " #{send(style)} #{style}" if send(style)
|
137
|
+
end
|
138
|
+
|
139
|
+
str
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class DataSet
|
145
|
+
attr_accessor :title, :with, :using, :data, :linewidth, :linecolor, :matrix, :smooth, :axes, :index, :linestyle
|
146
|
+
|
147
|
+
alias :ls :linestyle
|
148
|
+
|
149
|
+
def initialize(data = nil)
|
150
|
+
@data = data
|
151
|
+
@linestyle = nil
|
152
|
+
@title = nil
|
153
|
+
@with = nil
|
154
|
+
@using = nil
|
155
|
+
@linewidth = nil
|
156
|
+
@linecolor = nil
|
157
|
+
@matrix = nil
|
158
|
+
@smooth = nil
|
159
|
+
@axes = nil
|
160
|
+
@index = nil
|
161
|
+
|
162
|
+
yield self if block_given?
|
163
|
+
end
|
164
|
+
|
165
|
+
def notitle
|
166
|
+
'"No Title"'
|
167
|
+
end
|
168
|
+
|
169
|
+
def plot_args(io = '')
|
170
|
+
io += @data.is_a?(String) ? @data : "'-'"
|
171
|
+
io += " index #{@index}" if @index
|
172
|
+
io += " using #{@using}" if @using
|
173
|
+
io += " axes #{@axes}" if @axes
|
174
|
+
io += " title #{@title ? "\"#{@title}\"" : notitle}"
|
175
|
+
io += " matrix #{@matrix}" if @matrix
|
176
|
+
io += " smooth #{@smooth}" if @smooth
|
177
|
+
io += " with #{@with}" if @with
|
178
|
+
io += " linecolor #{@linecolor}" if @linecolor
|
179
|
+
io += " linewidth #{@linewidth}" if @linewidth
|
180
|
+
io += " linestyle #{@linestyle.index}" if @linestyle
|
181
|
+
|
182
|
+
io
|
183
|
+
end
|
184
|
+
|
185
|
+
def to_gplot
|
186
|
+
return nil if @data.nil? || @data.is_a?(String)
|
187
|
+
|
188
|
+
@data.to_gplot
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Array
|
193
|
+
def to_gplot
|
194
|
+
if number_series?(self)
|
195
|
+
series_for_plot = ''
|
196
|
+
self.each { |elem| series_for_plot += "#{elem}\n" }
|
197
|
+
series_for_plot + 'e'
|
198
|
+
else
|
199
|
+
self[0].zip(self[1]).map{ |elem| elem.join(' ') }.join("\n") + "\ne"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def number_series?(data)
|
206
|
+
data.each do |elem|
|
207
|
+
return false unless elem.is_a?(Numeric)
|
208
|
+
end
|
209
|
+
|
210
|
+
true
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class Matrix
|
215
|
+
def to_gplot
|
216
|
+
matrix_for_plot = ''
|
217
|
+
|
218
|
+
(0...self.column_size).each do |j|
|
219
|
+
(0...self.row_size).each do |i|
|
220
|
+
matrix_for_plot += "#{i} #{j} #{self[j,i]}\n" if self[j,i]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
matrix_for_plot
|
225
|
+
end
|
226
|
+
end
|
data/lib/grada.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
require 'gnuplot'
|
1
|
+
require 'grada/gnuplot'
|
2
2
|
|
3
3
|
class Grada
|
4
4
|
# Not valid the format of the object to construct the graph
|
5
5
|
#
|
6
6
|
class NotValidArrayError < RuntimeError; end
|
7
|
-
|
7
|
+
|
8
8
|
# Not valid the content of the array you're passing to build the graph
|
9
9
|
#
|
10
10
|
class NotValidDataError < RuntimeError; end
|
11
|
-
|
11
|
+
|
12
12
|
# Can't build the plot
|
13
13
|
#
|
14
14
|
class NoPlotDataError < RuntimeError; end
|
15
|
-
|
15
|
+
|
16
16
|
attr_reader :x
|
17
17
|
attr_reader :y
|
18
|
-
|
19
|
-
DEFAULT_OPTIONS = {width:
|
20
|
-
height:
|
18
|
+
|
19
|
+
DEFAULT_OPTIONS = {width: 1920,
|
20
|
+
height: 1080,
|
21
21
|
title: "Graph",
|
22
22
|
x_label: "X",
|
23
23
|
y_label: "Y",
|
@@ -26,7 +26,7 @@ class Grada
|
|
26
26
|
|
27
27
|
# Hello GraDA
|
28
28
|
#
|
29
|
-
|
29
|
+
|
30
30
|
def self.hi
|
31
31
|
puts "Hello GraDA"
|
32
32
|
end
|
@@ -45,11 +45,10 @@ class Grada
|
|
45
45
|
# y: (Array) *optional*
|
46
46
|
|
47
47
|
def initialize(x, y = nil)
|
48
|
-
|
49
48
|
@x = validate(x)
|
50
49
|
@y = y.nil? ? y : validate(y)
|
51
50
|
end
|
52
|
-
|
51
|
+
|
53
52
|
# Displays a graph in a window.
|
54
53
|
# You can specify all the options that you need:
|
55
54
|
# *width* (Integer)
|
@@ -68,7 +67,7 @@ class Grada
|
|
68
67
|
# => ""
|
69
68
|
# Arguments:
|
70
69
|
# opts: (Hash) *optional*
|
71
|
-
|
70
|
+
|
72
71
|
def display(opts = {})
|
73
72
|
@opts = DEFAULT_OPTIONS.merge(opts)
|
74
73
|
|
@@ -91,7 +90,7 @@ class Grada
|
|
91
90
|
end
|
92
91
|
end
|
93
92
|
end
|
94
|
-
|
93
|
+
|
95
94
|
# Save the graph in a png file.
|
96
95
|
# You can specify all the options that you need as _display_ but also need to specify the file
|
97
96
|
#
|
@@ -111,7 +110,7 @@ class Grada
|
|
111
110
|
|
112
111
|
plot_histogram do |plot|
|
113
112
|
plot.output @opts[:filename]
|
114
|
-
plot.set "terminal
|
113
|
+
plot.set "terminal png size #{@opts[:width]}, #{@opts[:height]} crop"
|
115
114
|
plot.terminal 'png'
|
116
115
|
end
|
117
116
|
elsif @opts[:graph_type] == :heatmap
|
@@ -120,6 +119,7 @@ class Grada
|
|
120
119
|
|
121
120
|
plot_heat_map do |plot|
|
122
121
|
plot.output @opts[:filename]
|
122
|
+
plot.set "terminal png size #{@opts[:width]}, #{@opts[:height]} crop"
|
123
123
|
plot.terminal 'png'
|
124
124
|
end
|
125
125
|
else
|
@@ -127,7 +127,7 @@ class Grada
|
|
127
127
|
|
128
128
|
plot_and do |plot|
|
129
129
|
plot.output @opts[:filename]
|
130
|
-
plot.set "terminal
|
130
|
+
plot.set "terminal png size #{@opts[:width]}, #{@opts[:height]} crop"
|
131
131
|
plot.terminal 'png'
|
132
132
|
end
|
133
133
|
end
|
@@ -137,7 +137,7 @@ class Grada
|
|
137
137
|
|
138
138
|
def validate(l)
|
139
139
|
raise NotValidArrayError if ! l.is_a?(Array)
|
140
|
-
|
140
|
+
|
141
141
|
l.each do |elem|
|
142
142
|
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer) || elem.is_a?(Array) || elem.is_a?(Hash))
|
143
143
|
end
|
@@ -145,7 +145,7 @@ class Grada
|
|
145
145
|
|
146
146
|
def population_data?(l)
|
147
147
|
raise NotValidArrayError if ! l.is_a?(Array)
|
148
|
-
|
148
|
+
|
149
149
|
l.each do |elem|
|
150
150
|
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer))
|
151
151
|
end
|
@@ -156,7 +156,7 @@ class Grada
|
|
156
156
|
l.each do |elem|
|
157
157
|
return false if ! elem.is_a?(Hash)
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
return true
|
161
161
|
end
|
162
162
|
|
@@ -164,22 +164,22 @@ class Grada
|
|
164
164
|
end
|
165
165
|
|
166
166
|
def plot_and(&block)
|
167
|
-
|
168
|
-
|
167
|
+
Gnuplot.open do |gp|
|
168
|
+
Gnuplot::Plot.new(gp) do |plot|
|
169
169
|
block[plot] if block
|
170
|
-
|
170
|
+
|
171
171
|
plot.title @opts[:title]
|
172
172
|
|
173
173
|
plot.xlabel @opts[:x_label]
|
174
174
|
plot.ylabel @opts[:y_label]
|
175
|
-
|
175
|
+
|
176
176
|
if multiple_data?(@y)
|
177
177
|
@y.each do |dic|
|
178
178
|
dic.each do |k, v|
|
179
179
|
if k.to_sym != :with
|
180
180
|
raise NoPlotDataError if ! v.nil? && @x.size != v.size
|
181
181
|
|
182
|
-
plot.data <<
|
182
|
+
plot.data << Gnuplot::DataSet.new([@x,v]) do |ds|
|
183
183
|
ds.with = dic[:with] || @opts[:with]
|
184
184
|
ds.title = "#{k}"
|
185
185
|
end
|
@@ -189,7 +189,7 @@ class Grada
|
|
189
189
|
else
|
190
190
|
raise NoPlotDataError if ! @y.nil? && @x.size != @y.size
|
191
191
|
|
192
|
-
plot.data <<
|
192
|
+
plot.data << Gnuplot::DataSet.new([@x,@y]) do |ds|
|
193
193
|
ds.with = @opts[:with]
|
194
194
|
end
|
195
195
|
end
|
@@ -198,19 +198,19 @@ class Grada
|
|
198
198
|
end
|
199
199
|
|
200
200
|
def plot_histogram(&block)
|
201
|
-
|
202
|
-
|
201
|
+
Gnuplot.open do |gp|
|
202
|
+
Gnuplot::Plot.new(gp) do |plot|
|
203
203
|
block[plot] if block
|
204
|
-
|
204
|
+
|
205
205
|
plot.title @opts[:title]
|
206
206
|
|
207
207
|
plot.set "style data histogram"
|
208
208
|
plot.xlabel @opts[:x_label]
|
209
209
|
plot.ylabel "Frecuency"
|
210
|
-
|
210
|
+
|
211
211
|
x = @x.sort.group_by { |xi| xi }.map{|k,v| v.count }
|
212
212
|
|
213
|
-
plot.data <<
|
213
|
+
plot.data << Gnuplot::DataSet.new(x) do |ds|
|
214
214
|
ds.with = @opts[:with]
|
215
215
|
end
|
216
216
|
end
|
@@ -218,8 +218,8 @@ class Grada
|
|
218
218
|
end
|
219
219
|
|
220
220
|
def plot_heat_map(&block)
|
221
|
-
|
222
|
-
|
221
|
+
Gnuplot.open do |gp|
|
222
|
+
Gnuplot::Plot.new(gp) do |plot|
|
223
223
|
block[plot] if block
|
224
224
|
|
225
225
|
plot.set "pm3d map"
|
@@ -232,9 +232,9 @@ class Grada
|
|
232
232
|
plot.set "palette define"
|
233
233
|
|
234
234
|
plot.title @opts[:title]
|
235
|
-
plot.data
|
235
|
+
plot.data << Gnuplot::DataSet.new(Matrix.columns(@x)) do |ds|
|
236
236
|
ds.with = @opts[:with]
|
237
|
-
end
|
237
|
+
end
|
238
238
|
end
|
239
239
|
end
|
240
240
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grada
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -43,22 +43,6 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: gnuplot
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
|
-
requirements:
|
51
|
-
- - ! '>='
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 2.6.2
|
54
|
-
type: :runtime
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 2.6.2
|
62
46
|
description: Graphic Data Analysis gem
|
63
47
|
email: hard_rock15@msn.com
|
64
48
|
executables: []
|
@@ -66,6 +50,7 @@ extensions: []
|
|
66
50
|
extra_rdoc_files: []
|
67
51
|
files:
|
68
52
|
- lib/grada.rb
|
53
|
+
- lib/grada/gnuplot.rb
|
69
54
|
homepage: https://github.com/emfigo/grada
|
70
55
|
licenses: []
|
71
56
|
post_install_message:
|