graphkit 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/graphkit.gemspec +4 -4
- data/lib/graphkit.rb +121 -121
- data/lib/graphkit/csv.rb +16 -14
- data/test/helper.rb +1 -5
- data/test/test_graphkit.rb +97 -84
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 040f0f9ed1623e873447df38aa1ae23d73e16c62
|
4
|
+
data.tar.gz: cd7cbaf8050afa54a55e1cefce4e38a9db00b535
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e35603a343c141f67c25368e2e028de736ff2d560cb53864373bc137bb5f547c150d13103403d108df739dff14db2280e449a1fe825c7671e9115aaf7a12c49
|
7
|
+
data.tar.gz: d1ccb0f78bbf11e7a54a7283eec2309f72124bf66f3bd550303a2a784dc93388461c5242efaa4ffdd63c45ebac53f97c98cd2d958fdaa2f32405bbea50110ce6
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/graphkit.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: graphkit 0.
|
5
|
+
# stub: graphkit 0.5.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "graphkit"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.5.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Edmund Highcock"]
|
14
|
-
s.date = "2016-
|
14
|
+
s.date = "2016-03-08"
|
15
15
|
s.description = "A GraphKit is a device independent intelligent data container that allows the easy sharing, combining and plotting of graphic data representations. Easily created from data, they can be output in a variety of formats using packages such as gnuplot. "
|
16
16
|
s.email = "edmundhighcock@sourceforge.net"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -39,7 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.homepage = "http://github.com/edmundhighcock/graphkit"
|
40
40
|
s.licenses = ["GPLv3"]
|
41
41
|
s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
|
42
|
-
s.rubygems_version = "2.
|
42
|
+
s.rubygems_version = "2.4.8"
|
43
43
|
s.summary = "A GraphKit is a device independent intelligent data container for generating and plotting graphs."
|
44
44
|
|
45
45
|
if s.respond_to? :specification_version then
|
data/lib/graphkit.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
script_folder = File.dirname(File.expand_path(__FILE__))
|
1
|
+
# script_folder = File.dirname(File.expand_path(__FILE__))
|
2
2
|
|
3
3
|
require 'pp'
|
4
4
|
require 'rubyhacks'
|
@@ -40,29 +40,29 @@ end
|
|
40
40
|
class SparseTensor < Hash
|
41
41
|
class RankError < StandardError
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
# class Key < Array
|
45
45
|
# def ==
|
46
|
-
#
|
46
|
+
#
|
47
47
|
# end
|
48
|
-
|
49
|
-
|
48
|
+
|
49
|
+
|
50
50
|
attr_reader :rank, :shape
|
51
51
|
#attr_accessor :default_val
|
52
|
-
|
52
|
+
|
53
53
|
# Create a new tensor.
|
54
|
-
|
54
|
+
|
55
55
|
def initialize(rank = 2)
|
56
56
|
@rank = rank
|
57
57
|
@shape = [0]*rank
|
58
58
|
super()
|
59
59
|
end
|
60
|
-
|
61
|
-
# Create a new diagonal tensor from an array. E.g. if rank was 2, then
|
60
|
+
|
61
|
+
# Create a new diagonal tensor from an array. E.g. if rank was 2, then
|
62
62
|
# tensor[0,0] = array[0]
|
63
63
|
# tensor[1,1] = array[1]
|
64
64
|
# Etc.
|
65
|
-
|
65
|
+
|
66
66
|
def self.diagonal(rank, array)
|
67
67
|
tensor = new(rank)
|
68
68
|
for index in 0...array.size
|
@@ -70,11 +70,11 @@ class SparseTensor < Hash
|
|
70
70
|
end
|
71
71
|
tensor
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# Access an element of the tensor. E.g. for a rank 2 tensor
|
75
75
|
#
|
76
76
|
# a = tensor[1,3]
|
77
|
-
|
77
|
+
|
78
78
|
def [](*args)
|
79
79
|
args = args[0] if args.size == 1 and not args.size == @rank and args[0].size == @rank
|
80
80
|
# p args
|
@@ -91,11 +91,11 @@ class SparseTensor < Hash
|
|
91
91
|
#end
|
92
92
|
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
# Set an element of the tensor. E.g. for a rank 2 tensor
|
96
96
|
#
|
97
97
|
# tensor[1,3] = a_variable
|
98
|
-
|
98
|
+
|
99
99
|
def []=(*args)
|
100
100
|
value = args.pop
|
101
101
|
args = args[0] if args.size == 1 and args[0].size == @rank
|
@@ -105,18 +105,18 @@ class SparseTensor < Hash
|
|
105
105
|
end
|
106
106
|
super(args, value)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
# Perform some action involving all the elements of this tensor and another.
|
110
|
-
#
|
110
|
+
#
|
111
111
|
# E.g.
|
112
112
|
# tensor_1.scalar_binary(tensor_2) do |element_1, element_2|
|
113
113
|
# element_1 + element_2
|
114
114
|
# end
|
115
115
|
#
|
116
116
|
# will add every element of tensor_1 to every corresponding element of tensor_2.
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
|
118
|
+
|
119
|
+
|
120
120
|
def scalar_binary(other, &block)
|
121
121
|
raise ArgumentError unless other.class == self.class
|
122
122
|
raise RankError.new("Different ranks: #@rank, #{other.rank}") unless other.rank == @rank
|
@@ -138,21 +138,21 @@ class SparseTensor < Hash
|
|
138
138
|
def -(other)
|
139
139
|
scalar_binary(other){|a, b| a - b}
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
# Find the maximum element of the tensor. See Enumerable#max.
|
143
|
-
|
143
|
+
|
144
144
|
def max(&block)
|
145
145
|
return self.values.max(&block)
|
146
146
|
end
|
147
|
-
|
147
|
+
|
148
148
|
# Find the minimum element of the tensor. See Enumerable#max.
|
149
|
-
|
149
|
+
|
150
150
|
def min(&block)
|
151
151
|
return self.values.min(&block)
|
152
152
|
end
|
153
153
|
|
154
154
|
|
155
|
-
|
155
|
+
|
156
156
|
def alter!(&block)
|
157
157
|
self.keys.each do |k|
|
158
158
|
self[k] = yield(self[k])
|
@@ -170,17 +170,17 @@ class SparseTensor < Hash
|
|
170
170
|
|
171
171
|
end
|
172
172
|
|
173
|
-
|
174
|
-
|
173
|
+
|
174
|
+
|
175
175
|
|
176
176
|
# To be mixed in to a Hash. Basically allows access the elements of a hash via method names rather than brackets.
|
177
177
|
|
178
178
|
|
179
179
|
module Kit
|
180
|
-
|
180
|
+
|
181
181
|
class IntegrityError < StandardError
|
182
182
|
end
|
183
|
-
|
183
|
+
|
184
184
|
def method_missing(method, *args)
|
185
185
|
# p method, args
|
186
186
|
m = method.to_s
|
@@ -193,10 +193,10 @@ module Kit
|
|
193
193
|
return send(method)
|
194
194
|
end
|
195
195
|
end
|
196
|
-
|
196
|
+
|
197
197
|
def check(*values, &block)
|
198
198
|
values.each do |arr|
|
199
|
-
case arr.size
|
199
|
+
case arr.size
|
200
200
|
when 2
|
201
201
|
expression, test_data = arr
|
202
202
|
if block
|
@@ -218,11 +218,11 @@ module Kit
|
|
218
218
|
end
|
219
219
|
end
|
220
220
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
221
|
|
225
|
-
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
|
226
226
|
end
|
227
227
|
|
228
228
|
# See Kit
|
@@ -251,26 +251,26 @@ end
|
|
251
251
|
# GraphKit also allows you access any property, e.g. title, as
|
252
252
|
#
|
253
253
|
# graphkit.title
|
254
|
-
#
|
255
|
-
# graphkit.title =
|
254
|
+
#
|
255
|
+
# graphkit.title =
|
256
256
|
#
|
257
257
|
# as well as the hash form:
|
258
258
|
#
|
259
259
|
# graphkit[:title]
|
260
260
|
# graphkit[:title] =
|
261
|
-
#
|
261
|
+
#
|
262
262
|
# GraphKits have methods which allow the graphs to be rendered using standard visualisation packages. At present only gnuplot is supported, but others are planned.
|
263
263
|
#
|
264
264
|
# GraphKits overload certain operators, for example <tt>+</tt>, which mean that they can be combined easily and intuitively. This makes plotting graphs from different sets of results on the same page as easy as adding 2+2!
|
265
|
-
#
|
265
|
+
#
|
266
266
|
# GraphKits define a minimum set of keys which are guaranteed to be meaningful and work on all platforms. If you stick to using just these properties, you'll be able to easily plot basic graphs. If you need more control and customisation, you need to look at the documentation both for the visualisation package (e.g. gnuplot) and the module which allows a GraphKit to interface with that package (e.g. GraphKit::Gnuplot).
|
267
267
|
#
|
268
268
|
# Here is the specification for the standard keys:
|
269
|
-
#
|
269
|
+
#
|
270
270
|
# * title (String): the title of the graph
|
271
271
|
# * xlabel, ylabel, zlabel (String): axis labels
|
272
272
|
# * xrange, yrange, zrange, frange (Array): ranges of the three dimensions and possibly the function as well.
|
273
|
-
# * data (Array of GraphKit::DataKits): the lines of data to be plotted.
|
273
|
+
# * data (Array of GraphKit::DataKits): the lines of data to be plotted.
|
274
274
|
|
275
275
|
|
276
276
|
class GraphKit < KitHash
|
@@ -290,7 +290,7 @@ class GraphKit < KitHash
|
|
290
290
|
self[i] += other[i] if other[i]
|
291
291
|
end
|
292
292
|
self
|
293
|
-
end
|
293
|
+
end
|
294
294
|
|
295
295
|
def +(other)
|
296
296
|
merge(other)
|
@@ -304,8 +304,8 @@ class GraphKit < KitHash
|
|
304
304
|
end
|
305
305
|
MultiWindow = MultiKit # Backwards compatibility
|
306
306
|
|
307
|
-
|
308
|
-
|
307
|
+
|
308
|
+
|
309
309
|
include Kit
|
310
310
|
include Log
|
311
311
|
AXES = [:x, :y, :z, :f]
|
@@ -313,73 +313,73 @@ class GraphKit < KitHash
|
|
313
313
|
DEFAULT_COLOURS_GNUPLOT = DEFAULT_COLOURS
|
314
314
|
DEFAULT_COLOURS_MATHEMATICA = DEFAULT_COLOURS.inject({}) do |hash, (i, coll)|
|
315
315
|
hash[i] = coll.sub(/#/, '').scan(/.{2}/).map{|str| (eval("0x#{str}").to_f / 255.0).round(2)}
|
316
|
-
hash
|
316
|
+
hash
|
317
317
|
end
|
318
318
|
|
319
319
|
# attr_reader :gnuplot_options
|
320
|
-
|
320
|
+
|
321
321
|
alias :hash_key :key
|
322
322
|
undef :key
|
323
|
-
|
323
|
+
|
324
324
|
# Greate a new graphkit. Rarely used: see GraphKit.autocreate and GraphKit.quick_create
|
325
|
-
|
325
|
+
|
326
326
|
def initialize(naxes=0, hash = {})
|
327
327
|
logf :initialize
|
328
328
|
super()
|
329
|
-
self[:naxes] = naxes
|
329
|
+
self[:naxes] = naxes
|
330
330
|
self[:data] = []
|
331
331
|
hash
|
332
332
|
# @gnuplot_options = GnuplotOptions.new
|
333
333
|
end
|
334
|
-
|
334
|
+
|
335
335
|
# def gnuplot_options
|
336
336
|
# @gnuplot_options ||= GnuplotOptions.new
|
337
337
|
# @gnuplot_options
|
338
338
|
# end
|
339
|
-
|
339
|
+
|
340
340
|
# alias :gp :gnuplot_options
|
341
|
-
|
341
|
+
|
342
342
|
class DataError < StandardError
|
343
343
|
end
|
344
|
-
|
344
|
+
|
345
345
|
# Create a new graphkit with one hash for every datakit (each datakit corresponds to a line or surface on the graph). Each hash should contain specifications for the axes of the graph (see AxisKit#autocreate).
|
346
346
|
#
|
347
347
|
# E.g.
|
348
348
|
# kit = GraphKit.autocreate(
|
349
349
|
# {
|
350
|
-
# x: {data: [1,2,3], title 'x', units: 'm'},
|
350
|
+
# x: {data: [1,2,3], title 'x', units: 'm'},
|
351
351
|
# y: {data: [1,4,9], title 'x^2', units: 'm^2'}
|
352
352
|
# }
|
353
353
|
# )
|
354
354
|
#
|
355
355
|
# will create a two dimensional graph that plots x^2 against x.
|
356
|
-
|
356
|
+
|
357
357
|
def self.autocreate(*hashes)
|
358
358
|
Log.logf :autocreate
|
359
359
|
new(hashes[0].size).autocreate(*hashes)
|
360
360
|
end
|
361
|
-
|
362
|
-
|
361
|
+
|
362
|
+
|
363
363
|
def lx(*args) # :nodoc:
|
364
364
|
log_axis(*args)
|
365
365
|
end
|
366
|
-
|
366
|
+
|
367
367
|
def lx=(val) # :nodoc: (deprecated)
|
368
368
|
self.log_axis = val
|
369
369
|
end
|
370
|
-
|
370
|
+
|
371
371
|
# Create a new graphkit without providing any labels.
|
372
372
|
#
|
373
373
|
# E.g.
|
374
374
|
# kit = GraphKit.quick_create(
|
375
375
|
# [
|
376
|
-
# [1,2,3],
|
376
|
+
# [1,2,3],
|
377
377
|
# [1,4,9]
|
378
378
|
# ]
|
379
379
|
# )
|
380
380
|
#
|
381
381
|
# will create a two dimensional graph that plots x^2 against x.
|
382
|
-
|
382
|
+
|
383
383
|
def self.quick_create(*datasets)
|
384
384
|
hashes = datasets.map do |data|
|
385
385
|
hash = {}
|
@@ -390,14 +390,14 @@ class GraphKit < KitHash
|
|
390
390
|
end
|
391
391
|
autocreate(*hashes)
|
392
392
|
end
|
393
|
-
|
394
|
-
|
395
|
-
|
393
|
+
|
394
|
+
|
395
|
+
|
396
396
|
def autocreate(*hashes) # :nodoc: (see GraphKit.autocreate)
|
397
397
|
logf :autocreate
|
398
398
|
hashes.each{|hash| data.push DataKit.autocreate(hash)}
|
399
399
|
# pp data
|
400
|
-
[:title, :label, :units, :range].each do |option|
|
400
|
+
[:title, :label, :units, :range].each do |option|
|
401
401
|
data[0].axes.each do |key, axiskit|
|
402
402
|
# next unless AXES.include? key
|
403
403
|
self[key + option] = axiskit[option].dup if axiskit[option]
|
@@ -407,9 +407,9 @@ class GraphKit < KitHash
|
|
407
407
|
check_integrity
|
408
408
|
self
|
409
409
|
end
|
410
|
-
|
410
|
+
|
411
411
|
# Check that the graphkit conforms to specification; that the data has dimensions that make sense and that the titles and ranges have the right types.
|
412
|
-
|
412
|
+
|
413
413
|
def check_integrity
|
414
414
|
logf :check_integrity
|
415
415
|
check(['data.class', Array], ['title.class', [String, NilClass]], ['has_legend.class', [Hash, NilClass]])
|
@@ -420,7 +420,7 @@ class GraphKit < KitHash
|
|
420
420
|
# p instance_eval(prop.to_s + "[#{key.inspect}]"), 'ebb'
|
421
421
|
check(["#{key + prop}.class", klass])
|
422
422
|
# check(["a key from #{prop}", key, AXES + [:f]])
|
423
|
-
end
|
423
|
+
end
|
424
424
|
end
|
425
425
|
data.each do |datakit|
|
426
426
|
check(['class of a member of the data array', datakit.class, DataKit])
|
@@ -429,28 +429,28 @@ class GraphKit < KitHash
|
|
429
429
|
end
|
430
430
|
return true
|
431
431
|
end
|
432
|
-
|
432
|
+
|
433
433
|
# AXES.each do |axisname|
|
434
434
|
# [:name, :label, :units, :range].each do |option|
|
435
435
|
# define_method(axisname + option){self[option][axisname]}
|
436
436
|
# define_method(axisname + option + '='.to_sym){|value| self[option][axisname] = value}
|
437
437
|
# end
|
438
438
|
# end
|
439
|
-
|
439
|
+
|
440
440
|
@@old_gnuplot_sets = [ :dgrid3d, :title, :style, :term, :terminal, :pointsize, :log_axis, :key, :pm3d, :palette, :view, :cbrange, :contour, :nosurface, :cntrparam, :preamble, :xtics, :ytics]
|
441
|
-
|
442
|
-
|
441
|
+
|
442
|
+
|
443
443
|
# @@gnuplot_sets.uniq!
|
444
|
-
|
444
|
+
|
445
445
|
# Duplicate the graphkit.
|
446
|
-
|
446
|
+
|
447
447
|
def dup
|
448
448
|
#logf :dup
|
449
449
|
#self.class.new(naxes, self)
|
450
450
|
eval(inspect)
|
451
451
|
end
|
452
|
-
|
453
|
-
# Combine with another graph; titles and labels from the first graph will override the second.
|
452
|
+
|
453
|
+
# Combine with another graph; titles and labels from the first graph will override the second.
|
454
454
|
|
455
455
|
def +(other)
|
456
456
|
check(['other.naxes', other.naxes, self.naxes])
|
@@ -460,7 +460,7 @@ class GraphKit < KitHash
|
|
460
460
|
new.data = self.data + other.data
|
461
461
|
new
|
462
462
|
end
|
463
|
-
|
463
|
+
|
464
464
|
def extend_using(other, mapping = nil)
|
465
465
|
if mapping
|
466
466
|
mapping.each do |mine, others|
|
@@ -473,7 +473,7 @@ class GraphKit < KitHash
|
|
473
473
|
end
|
474
474
|
end
|
475
475
|
end
|
476
|
-
|
476
|
+
|
477
477
|
def each_axiskit(*axes, &block)
|
478
478
|
axes = AXES unless axes.size > 0
|
479
479
|
axes.each do |axis|
|
@@ -495,7 +495,7 @@ class GraphKit < KitHash
|
|
495
495
|
shapes
|
496
496
|
|
497
497
|
end
|
498
|
-
|
498
|
+
|
499
499
|
def transpose!
|
500
500
|
data.each do |datakit|
|
501
501
|
datakit.transpose!
|
@@ -503,7 +503,7 @@ class GraphKit < KitHash
|
|
503
503
|
self.xlabel, self.ylabel = ylabel, xlabel
|
504
504
|
self.xrange, self.yrange = xrange, yrange
|
505
505
|
end
|
506
|
-
|
506
|
+
|
507
507
|
def convert(&block)
|
508
508
|
#ep 'Converting graph...'
|
509
509
|
kit = self.dup
|
@@ -521,7 +521,7 @@ class GraphKit < KitHash
|
|
521
521
|
# E.g. convert a line of values of [x, y, z] with rank [1,1,2]
|
522
522
|
# to a matrix of values [x, y, z] with rank [1, 1, 2]
|
523
523
|
# convert_rank!([[1,1,1], [1,1,2]])
|
524
|
-
|
524
|
+
|
525
525
|
def convert_rank!(from_to, options={})
|
526
526
|
ep "Converting Rank"
|
527
527
|
case from_to
|
@@ -566,21 +566,21 @@ class DataKit < KitHash
|
|
566
566
|
self.axes[:y].data=ynew
|
567
567
|
#p 'dk', self
|
568
568
|
self
|
569
|
-
|
569
|
+
|
570
570
|
end
|
571
|
-
|
571
|
+
|
572
572
|
# include Kit
|
573
573
|
include Log
|
574
574
|
AXES = GraphKit::AXES
|
575
|
-
AXES.each{|ax| define_method(ax){self.axes[ax]}}
|
576
|
-
AXES.each{|ax| define_method(ax + "=".to_sym){|val| self.axes[ax] = val}}
|
577
|
-
|
575
|
+
AXES.each{|ax| define_method(ax){self.axes[ax]}}
|
576
|
+
AXES.each{|ax| define_method(ax + "=".to_sym){|val| self.axes[ax] = val}}
|
577
|
+
|
578
578
|
# attr_accessor :labels, :ranges, :has_legend, :units, :dimensions
|
579
|
-
|
580
579
|
|
581
|
-
|
580
|
+
|
581
|
+
def axes_array
|
582
582
|
self.axes.values_at(*AXES).compact
|
583
|
-
|
583
|
+
end
|
584
584
|
def initialize(options = {})
|
585
585
|
super()
|
586
586
|
self[:axes] = {}
|
@@ -590,14 +590,14 @@ class DataKit < KitHash
|
|
590
590
|
def self.autocreate(hash)
|
591
591
|
new.autocreate(hash)
|
592
592
|
end
|
593
|
-
|
593
|
+
|
594
594
|
def autocreate(hash)
|
595
595
|
logf :autocreate
|
596
596
|
hash.each do |key, value|
|
597
597
|
# puts value.inspect
|
598
598
|
if AXES.include? key
|
599
599
|
self[:axes][key] = AxisKit.autocreate(value)
|
600
|
-
else
|
600
|
+
else
|
601
601
|
raise ArgumentError.new("bad key value pair in autocreate: #{key.inspect}, #{value.inspect}")
|
602
602
|
end
|
603
603
|
# puts self[key].inspect
|
@@ -611,7 +611,7 @@ class DataKit < KitHash
|
|
611
611
|
check_integrity
|
612
612
|
self
|
613
613
|
end
|
614
|
-
|
614
|
+
|
615
615
|
def check_integrity
|
616
616
|
logf :check_integrity
|
617
617
|
check(['title.class', [String, NilClass]], ['with.class', [String, NilClass]], ['axes.class', Hash])
|
@@ -626,7 +626,7 @@ class DataKit < KitHash
|
|
626
626
|
# end
|
627
627
|
# puts 'checking f.class', f.class
|
628
628
|
# check(['f.class', CodeRunner::FunctionKit])
|
629
|
-
|
629
|
+
|
630
630
|
# shape = f.shape
|
631
631
|
log 'checking ranks'
|
632
632
|
rnks = ranks
|
@@ -651,17 +651,17 @@ class DataKit < KitHash
|
|
651
651
|
#end
|
652
652
|
passed = false unless axes[:f].shape == [axes[:x].shape[0], axes[:y].shape[0], axes[:z].shape[0]]
|
653
653
|
end
|
654
|
-
raise IntegrityError.new(%[The dimensions of this data do not match: \n#{axes.inject(""){|str, (axis, axiskit)| str + "#{axis}: #{axiskit.shape}\n"}}\nranks: #{rnks}]) unless passed
|
654
|
+
raise IntegrityError.new(%[The dimensions of this data do not match: \n#{axes.inject(""){|str, (axis, axiskit)| str + "#{axis}: #{axiskit.shape}\n"}}\nranks: #{rnks}]) unless passed
|
655
655
|
# log 'finished checking ranks'
|
656
656
|
logfc :check_integrity
|
657
657
|
# raise IntegrityError.new("function data must be a vector, or have the correct dimensions (or shape) for the axes: function dimensions: #{shape}; axes dimesions: #{axes_shape}") unless shape.size == 1 or axes_shape == shape
|
658
658
|
return true
|
659
659
|
end
|
660
|
-
|
660
|
+
|
661
661
|
#ALLOWED_RANKS = [[1], [1,1], [1,1,1], [1,1,2], [1,1,1,1], [1,1,2,2], [1,1,1,3]]
|
662
662
|
ALLOWED_RANKS = [[1], [1,1], [1,1,1], [1,1,2], [2,2,2], [2,2,2,2], [1,1,1,1], [1,1,2,2], [1,1,1,3], [3,3,3,3]]
|
663
663
|
|
664
|
-
def allowed_ranks
|
664
|
+
def allowed_ranks
|
665
665
|
ALLOWED_RANKS
|
666
666
|
end
|
667
667
|
#def ranks_c_switch_hash
|
@@ -678,13 +678,13 @@ class DataKit < KitHash
|
|
678
678
|
logfc :shapes
|
679
679
|
return ans
|
680
680
|
end
|
681
|
-
|
681
|
+
|
682
682
|
def rank_c_switch
|
683
683
|
#i = -1
|
684
684
|
#puts ALLOWED_RANKS.map{|r| i+=1;"#{i} --> #{r}"}
|
685
685
|
switch = ALLOWED_RANKS.index(ranks)
|
686
686
|
switch
|
687
|
-
|
687
|
+
|
688
688
|
end
|
689
689
|
def ranks
|
690
690
|
logf :ranks
|
@@ -692,14 +692,14 @@ class DataKit < KitHash
|
|
692
692
|
logfc :ranks
|
693
693
|
return ans
|
694
694
|
end
|
695
|
-
|
695
|
+
|
696
696
|
def extend_using(other)
|
697
697
|
raise "A dataset can only be extended using another dataset with the same ranks: the ranks of this dataset are #{ranks} and the ranks of the other dataset are #{other.ranks}" unless ranks == other.ranks
|
698
698
|
axes.each do |key, axiskit|
|
699
699
|
axiskit.extend_using(other.axes[key])
|
700
700
|
end
|
701
701
|
end
|
702
|
-
|
702
|
+
|
703
703
|
# def gnuplot_ranks
|
704
704
|
# case axes.size
|
705
705
|
# when 1,2
|
@@ -715,7 +715,7 @@ class DataKit < KitHash
|
|
715
715
|
# end
|
716
716
|
# end
|
717
717
|
# end
|
718
|
-
#
|
718
|
+
#
|
719
719
|
# def gnuplot
|
720
720
|
# # p axes.values_at(*AXES).compact.zip(gnuplot_ranks)
|
721
721
|
# Gnuplot::DataSet.new(axes.values_at(*AXES).compact.zip(gnuplot_ranks).map{|axis, rank| axis.data_for_gnuplot(rank) }) do |ds|
|
@@ -734,15 +734,15 @@ class DataKit < KitHash
|
|
734
734
|
# # ds.linewidth = 4
|
735
735
|
# end
|
736
736
|
# end
|
737
|
-
#
|
737
|
+
#
|
738
738
|
# def gnuplot_options
|
739
739
|
# @gnuplot_options ||= GnuplotPlotOptions.new
|
740
740
|
# @gnuplot_options
|
741
741
|
# end
|
742
|
-
#
|
742
|
+
#
|
743
743
|
# alias :gp :gnuplot_options
|
744
|
-
#
|
745
|
-
|
744
|
+
#
|
745
|
+
|
746
746
|
AXES.each do |axisname|
|
747
747
|
define_method(axisname + :axis){self[:axes][axisname]}
|
748
748
|
define_method(axisname + :axis + '='.to_sym){|value| self[:axes][axisname] = value}
|
@@ -782,7 +782,7 @@ class DataKit < KitHash
|
|
782
782
|
if axes.size == 1
|
783
783
|
data = axes[:x].data
|
784
784
|
i = 0
|
785
|
-
loop do
|
785
|
+
loop do
|
786
786
|
break if i > data.size - 2
|
787
787
|
should_be = (data[i+1] + data[i-1]) / 2.0
|
788
788
|
deviation = (should_be - data[i]).abs / data[i].abs
|
@@ -796,9 +796,9 @@ class DataKit < KitHash
|
|
796
796
|
x_data = axes[:x].data
|
797
797
|
data = axes[:y].data
|
798
798
|
i = 0
|
799
|
-
loop do
|
799
|
+
loop do
|
800
800
|
jump = 1
|
801
|
-
loop do
|
801
|
+
loop do
|
802
802
|
break if i > data.size - 1 - jump
|
803
803
|
break unless x_data[i+jump] == x_data[i-jump]
|
804
804
|
jump += 1
|
@@ -816,7 +816,7 @@ class DataKit < KitHash
|
|
816
816
|
end
|
817
817
|
# p self.outliers
|
818
818
|
end
|
819
|
-
|
819
|
+
|
820
820
|
# def exclude_outliers
|
821
821
|
# raise "Can only get rid of outliers for 1D or 2D data" if axes.size > 2
|
822
822
|
# # self.outliers = []
|
@@ -829,25 +829,25 @@ class DataKit < KitHash
|
|
829
829
|
# axes[:x].data.delete_at(index)
|
830
830
|
# axes[:y].data.delete_at(index)
|
831
831
|
# end
|
832
|
-
#
|
832
|
+
#
|
833
833
|
# end
|
834
834
|
# check_integrity
|
835
835
|
# end
|
836
836
|
|
837
|
-
|
838
|
-
|
837
|
+
|
838
|
+
|
839
839
|
end
|
840
840
|
|
841
841
|
|
842
842
|
class AxisKit < KitHash
|
843
|
-
|
843
|
+
|
844
844
|
# include Kit
|
845
845
|
include Log
|
846
846
|
AXES = GraphKit::AXES
|
847
847
|
|
848
|
-
|
848
|
+
|
849
849
|
# attr_accessor :labels, :ranges, :has_legend, :units, :dimensions
|
850
|
-
|
850
|
+
|
851
851
|
def initialize(hash = {})
|
852
852
|
super()
|
853
853
|
self.title = ""
|
@@ -855,25 +855,25 @@ class AxisKit < KitHash
|
|
855
855
|
self.
|
856
856
|
absorb hash
|
857
857
|
end
|
858
|
-
|
858
|
+
|
859
859
|
def check_integrity
|
860
860
|
check(['units.class', [String]], ['scaling.class', [Float, NilClass]], ['label.class', [String, NilClass]], ['title.class', [String]])
|
861
861
|
check(['data.to_a.class', Array])
|
862
862
|
end
|
863
|
-
|
863
|
+
|
864
864
|
def dup
|
865
865
|
# puts 'i was called'
|
866
866
|
new = self.class.new(self)
|
867
867
|
new.data = data.dup
|
868
868
|
new
|
869
869
|
end
|
870
|
-
|
870
|
+
|
871
871
|
def self.autocreate(hash)
|
872
872
|
new_kit = new(hash)
|
873
873
|
new_kit.label = "#{new_kit.title} (#{new_kit.units})"
|
874
874
|
new_kit
|
875
875
|
end
|
876
|
-
|
876
|
+
|
877
877
|
def shape
|
878
878
|
logf :shape
|
879
879
|
if data.methods.include? :shape
|
@@ -902,7 +902,7 @@ class AxisKit < KitHash
|
|
902
902
|
# raise TypeError("Bad Rank")
|
903
903
|
# end
|
904
904
|
# end
|
905
|
-
|
905
|
+
|
906
906
|
def extend_using(other)
|
907
907
|
raise TypeError.new("Can only extend axes if data have the same ranks: #{shape.size}, #{other.shape.size}") unless shape.size == other.shape.size
|
908
908
|
raise TypeError.new("Can only extend axes if data have the same class") unless data.class == other.data.class
|
@@ -921,7 +921,7 @@ class AxisKit < KitHash
|
|
921
921
|
raise TypeError("Extending data with this rank: #{shape.size} is currently not implemented.")
|
922
922
|
end
|
923
923
|
end
|
924
|
-
|
924
|
+
|
925
925
|
end
|
926
926
|
|
927
927
|
end # class GraphKit
|
@@ -938,6 +938,6 @@ if $0 == __FILE__
|
|
938
938
|
|
939
939
|
end
|
940
940
|
|
941
|
-
|
941
|
+
|
942
942
|
# A graph kit is 'everything you need..
|
943
943
|
#A graph kit is, in fact, a very intelligent hash
|
data/lib/graphkit/csv.rb
CHANGED
@@ -2,9 +2,8 @@ require 'matrix'
|
|
2
2
|
# Methods for writing graphkits to csv (comma separated value) files
|
3
3
|
|
4
4
|
class GraphKit
|
5
|
-
def to_csv(options={})
|
5
|
+
def to_csv(options={})
|
6
6
|
check_integrity
|
7
|
-
ep 'to_csv'
|
8
7
|
stringio = options[:io] || StringIO.new
|
9
8
|
data.each do |dk|
|
10
9
|
dk.to_csv(options)
|
@@ -14,7 +13,7 @@ class GraphKit
|
|
14
13
|
end
|
15
14
|
|
16
15
|
class DataKit
|
17
|
-
class TensorArray
|
16
|
+
class TensorArray
|
18
17
|
def initialize(arr)
|
19
18
|
@arr=arr
|
20
19
|
end
|
@@ -26,21 +25,25 @@ class GraphKit
|
|
26
25
|
end
|
27
26
|
def to_csv(options={})
|
28
27
|
io = options[:io] || StringIO.new
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
|
29
|
+
if options[:append]
|
30
|
+
if File.exists?(io)
|
31
|
+
options[:header] = nil
|
32
|
+
end
|
33
|
+
csv_file = File.open(io, 'a')
|
34
|
+
else
|
35
|
+
csv_file = File.open(io, 'w')
|
36
|
+
end
|
37
|
+
|
38
|
+
unless options[:header].nil?
|
39
|
+
header = options[:header].to_s
|
32
40
|
csv_file.write(header + "\n")
|
33
41
|
end
|
34
42
|
|
35
43
|
axs = self.axes.values_at(*AXES).compact
|
36
|
-
#ep 'axs', axs
|
37
44
|
dl = axs[-1].shape.product
|
38
45
|
dat = axs.map{|ax| ax.data}
|
39
46
|
sh = shapes
|
40
|
-
#cml_sh = sh.map do |sh1|
|
41
|
-
# cml = 1
|
42
|
-
# sh1.reverse.map{|dim| cml *= dim; cml}.reverse
|
43
|
-
#end
|
44
47
|
dat = dat.map do |d|
|
45
48
|
d.kind_of?(Array) ? TensorArray.new(d) : d
|
46
49
|
end
|
@@ -48,13 +51,12 @@ class GraphKit
|
|
48
51
|
if self.errors
|
49
52
|
raise "Errors can only be plotted for 1D or 2D data" unless ranks == [1] or ranks == [1,1]
|
50
53
|
edat = self.errors.values_at(:x, :xmin, :xmax, :y, :ymin, :ymax).compact
|
51
|
-
#ep 'edat', edat
|
52
54
|
end
|
53
55
|
|
54
56
|
io = ''
|
55
57
|
case ranks
|
56
58
|
when [1], [1,1], [1,1,1], [1,1,1,1]
|
57
|
-
dl.times do |n|
|
59
|
+
dl.times do |n|
|
58
60
|
dat.each{|d| io << d[n].to_s << ","}
|
59
61
|
io << " " << edat.map{|e| e[n].to_s}.join(", ") if self.errors
|
60
62
|
io << "\n"
|
@@ -131,7 +133,7 @@ class GraphKit
|
|
131
133
|
end
|
132
134
|
|
133
135
|
return stringio.string unless options[:io]
|
134
|
-
|
136
|
+
|
135
137
|
csv_file.write(io)
|
136
138
|
csv_file.close()
|
137
139
|
end
|
data/test/helper.rb
CHANGED
@@ -7,12 +7,8 @@ rescue Bundler::BundlerError => e
|
|
7
7
|
$stderr.puts "Run `bundle install` to install missing gems"
|
8
8
|
exit e.status_code
|
9
9
|
end
|
10
|
-
require '
|
11
|
-
require 'shoulda'
|
10
|
+
require 'minitest/autorun'
|
12
11
|
|
13
12
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
13
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
14
|
require 'graphkit'
|
16
|
-
|
17
|
-
class Test::Unit::TestCase
|
18
|
-
end
|
data/test/test_graphkit.rb
CHANGED
@@ -1,88 +1,101 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class TestGraphkit <
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
3
|
+
class TestGraphkit < Minitest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
FileUtils.makedirs('test/test_output')
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
FileUtils.rm_r('test/test_output')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_to_csv_single_dk
|
14
|
+
a = GraphKit.autocreate({x: {data: [1,2,3]},
|
15
|
+
y: {data: [4,5,6]}})
|
16
|
+
|
17
|
+
a.to_csv({io:'test/test_output/csv_single_dk.csv'})
|
18
|
+
|
19
|
+
f = File.read('test/test_output/csv_single_dk.csv')
|
20
|
+
assert_equal(f, "1,4,\n2,5,\n3,6,\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_to_csv_single_dk_with_header
|
24
|
+
a = GraphKit.autocreate({x: {data: [1,2,3]},
|
25
|
+
y: {data: [4,5,6]}})
|
26
|
+
|
27
|
+
a.to_csv({io:'test/test_output/csv_single_dk_with_header.csv',
|
28
|
+
header:'test1, test2'})
|
29
|
+
|
30
|
+
f = File.read('test/test_output/csv_single_dk_with_header.csv')
|
31
|
+
assert_equal(f, "test1, test2\n1,4,\n2,5,\n3,6,\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_to_csv_append
|
35
|
+
a = GraphKit.autocreate({x: {data: [1]},
|
36
|
+
y: {data: [2]}})
|
37
|
+
b = GraphKit.autocreate({x: {data: [3]},
|
38
|
+
y: {data: [4]}})
|
39
|
+
|
40
|
+
a.to_csv({io:'test/test_output/csv_append.csv'})
|
41
|
+
b.to_csv({io:'test/test_output/csv_append.csv', append:true})
|
42
|
+
|
43
|
+
f = File.read('test/test_output/csv_append.csv')
|
44
|
+
assert_equal(f, "1,2,\n3,4,\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_to_csv_append_with_header
|
48
|
+
a = GraphKit.autocreate({x: {data: [1]},
|
49
|
+
y: {data: [2]}})
|
50
|
+
b = GraphKit.autocreate({x: {data: [3]},
|
51
|
+
y: {data: [4]}})
|
52
|
+
|
53
|
+
a.to_csv({io:'test/test_output/csv_append_with_header.csv',
|
54
|
+
header:'test1,test2'})
|
55
|
+
b.to_csv({io:'test/test_output/csv_append_with_header.csv',
|
56
|
+
header:'test1,test2', append:true})
|
57
|
+
|
58
|
+
f = File.read('test/test_output/csv_append_with_header.csv')
|
59
|
+
assert_equal(f, "test1,test2\n1,2,\n3,4,\n")
|
60
|
+
end
|
61
|
+
def test_sparse_tensor_new
|
62
|
+
t = SparseTensor.new(3)
|
63
|
+
assert_equal(t.shape, [0,0,0])
|
64
|
+
assert_equal(t.rank, 3)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_multiplot
|
68
|
+
a = GraphKit.autocreate({x: {data: [2, 5, 11, 22],
|
69
|
+
units: 'years',
|
70
|
+
title: 'Age'},
|
71
|
+
y: {data: [1,3,5,6],
|
72
|
+
units: 'feet',
|
73
|
+
title: 'Height'}})
|
74
|
+
|
75
|
+
b = GraphKit.autocreate({x: {data: [2, 5, 11, 22],
|
76
|
+
units: 'years',
|
77
|
+
title: 'Age'},
|
78
|
+
y: {data: [1,3,5,6],
|
79
|
+
units: 'feet',
|
80
|
+
title: 'Height'},
|
81
|
+
z: {data: [2,4,8,12],
|
82
|
+
units: 'stone',
|
83
|
+
title: 'Weight'}})
|
84
|
+
b.data[0].modify({with: 'lp'})
|
85
|
+
|
86
|
+
b.close
|
87
|
+
b.gnuplot_write('test/test_output/heights.ps')
|
88
|
+
|
89
|
+
multiplot = GraphKit::MultiWindow.new
|
90
|
+
multiplot.push a
|
91
|
+
multiplot.push b
|
92
|
+
|
93
|
+
multiplot2 = GraphKit::MultiKit.new([GraphKit.quick_create([[0,3], [2,4]])])
|
94
|
+
multiplot.merge(multiplot2)
|
95
|
+
|
96
|
+
assert_equal(GraphKit::MultiKit, eval(multiplot.inspect).class )
|
97
|
+
|
98
|
+
multiplot.gnuplot_write('test/test_output/multiplot.ps', multiplot: 'layout 2,2')
|
99
|
+
end
|
87
100
|
|
88
101
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edmund Highcock
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyhacks
|
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
143
|
rubyforge_project:
|
144
|
-
rubygems_version: 2.
|
144
|
+
rubygems_version: 2.4.8
|
145
145
|
signing_key:
|
146
146
|
specification_version: 4
|
147
147
|
summary: A GraphKit is a device independent intelligent data container for generating
|