numplot 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d354b2cc2c06f15dfb5130ea2ee5c48f1f157f2
4
+ data.tar.gz: 04ddb3b2c06dc8f02b43d9920b019df571abc151
5
+ SHA512:
6
+ metadata.gz: 7aad96c6901b48822dcc03bc3ae5dd6bc2f0c90e5662c3dab090295d553096e9a08321e56b050bbdac9586f0265646a66ee885b6a5045c033bb213e1d768a444
7
+ data.tar.gz: e9acde2f4a6165047da57a44de4f223a751a89c084d284fe7508f2467fffcf81ee598d64897f812517483a95c749976b94fa0decbd22813d3f3597b81156867d
@@ -0,0 +1,83 @@
1
+
2
+ module NumPlot
3
+ # The class handling options on gnuplot command.
4
+ # @!visibility private
5
+ class Option
6
+ def initialize(name, converter, aliases)
7
+ @name = name
8
+ @converter = converter
9
+ @aliases = aliases
10
+ end
11
+
12
+ def plot_arg(h)
13
+ ([@name] + @aliases).each do |key|
14
+ if h.has_key? (key)
15
+ return " #{@name} #{@converter[h[key]]}"
16
+ end
17
+ end
18
+
19
+ ""
20
+ end
21
+
22
+ def self.[](name, *aliases)
23
+ new(name, proc{|x| x }, aliases)
24
+ end
25
+ end
26
+
27
+ # The class handling color options on gnuplot command.
28
+ # @!visibility private
29
+ class ColorOption < Option
30
+ def self.[](name, *aliases)
31
+ new(name, Conversion.method(:convert_color), aliases)
32
+ end
33
+ end
34
+
35
+ # The class handling font options on gnuplot command.
36
+ # @!visibility private
37
+ class FontOption < Option
38
+ def self.[](name, *aliases)
39
+ new(name, Conversion.method(:convert_font), aliases)
40
+ end
41
+ end
42
+
43
+ # The class handling on/off options on gnuplot command.
44
+ # @!visibility private
45
+ class BoolOption
46
+ def initialize(name, *aliases)
47
+ @name = name; @aliases = aliases
48
+ end
49
+
50
+ def plot_arg(h)
51
+ ([@name] + @aliases).each do |key|
52
+ return " #{@name}" if h.has_key?(key)
53
+ end
54
+
55
+ ""
56
+ end
57
+
58
+ class << self
59
+ alias [] new
60
+ end
61
+ end
62
+
63
+ # The class handling XXX/noXXX options on gnuplot command
64
+ # @!visibility private
65
+ class YesNoOption
66
+ def initialize(name)
67
+ @name = name
68
+ @no_name = "no#{name}".intern
69
+ end
70
+
71
+ def plot_arg(h)
72
+ return " #{@name}" if h.has_key?(@name) && h[@name]
73
+ return " #{@name}" if h.has_key?(@no_name) && !h[@no_name]
74
+ return " #{@no_name}" if h.has_key?(@no_name) && h[@no_name]
75
+ return " #{@no_name}" if h.has_key?(@name) && !h[@name]
76
+ ""
77
+ end
78
+
79
+ class << self
80
+ alias [] new
81
+ end
82
+ end
83
+ end
data/lib/numplot.rb ADDED
@@ -0,0 +1,667 @@
1
+ require 'numplot/option'
2
+
3
+ require 'set'
4
+
5
+ # Call the given block once.
6
+ # This method is used to make yard to ignore the code in a block.
7
+ def __yard_ignore_here; yield; end
8
+
9
+ # A module wrapping gnuplot {http://www.gnuplot.info/}.
10
+ module NumPlot
11
+ # The collection of methods converting ruby objects to gnuplot strings.
12
+ #
13
+ # @!visibility private
14
+ module Conversion
15
+ module_function
16
+
17
+ def convert_range(range)
18
+ case range
19
+ when String then range
20
+ when Range then "[#{range.begin}:#{range.end}]"
21
+ when Array
22
+ if range.size != 2
23
+ raise TypeError, "Cannot convert #{range.inspect} to gnuplot's range"
24
+ end
25
+ "[#{range[0]}:#{range[1]}]"
26
+ else
27
+ raise TypeError, "Cannot convert #{range.inspect} to gnuplot's range"
28
+ end
29
+ end
30
+
31
+ def convert_color(color)
32
+ case color
33
+ when RGB then color.to_gnuplot_string
34
+ when String, Integer then color
35
+ else
36
+ raise TypeError, "Cannot convert #{color.inspect} to gnuplot's color"
37
+ end
38
+ end
39
+
40
+ def convert_font(font)
41
+ case font
42
+ when String then font
43
+ when Font then font.to_gnuplot_string
44
+ else
45
+ raise TypeError, "Cannot convert #{font.inspect} to gnuplot's font"
46
+ end
47
+ end
48
+
49
+ def quote(str)
50
+ "\"#{str}\""
51
+ end
52
+ end
53
+
54
+ # The class representing colors in gnuplot.
55
+ #
56
+ # You can use three types of color specifications like the following
57
+ # examples.
58
+ #
59
+ # Example:
60
+ # RGB[255, 0, 0] # => Red
61
+ # RGB["#00ff00"] # => Green
62
+ # RGB["blue"] # => blue
63
+ #
64
+ class RGB
65
+ # Create a new instance of RGB class.
66
+ #
67
+ # You can give three styles of arguments to specify a color.
68
+ # * an RGB component as three integers like NumPlot::RGB.new(0, 255, 0)
69
+ # * a "#xxyyzz" style string like NumPlot::RGB.new("#ff007f")
70
+ # * a string of the name of a color like NumPlot::RGB.new("blue")
71
+ def initialize(*args)
72
+ if args.size == 1 && String === args[0]
73
+ @s = args[0]
74
+ elsif args.size == 3
75
+ @s = "#%02x%02x%02x" % args
76
+ else
77
+ raise TypeError, "Unknown color format: #{args.inspect}"
78
+ end
79
+ end
80
+
81
+ # Convert self to gnuplot "rgbcolor" string.
82
+ # @visibility private
83
+ def to_gnuplot_string
84
+ "rgbcolor \"#{@s}\""
85
+ end
86
+
87
+ class << self
88
+ # @method [](*args)
89
+ # Create a new RGB object. See {#initialize}.
90
+ # @return [RGB] A new RGB object
91
+ __yard_ignore_here{ alias [] new }
92
+ end
93
+ end
94
+
95
+ # The class representing a font in gnuplot.
96
+ #
97
+ # Example:
98
+ # Font["ariel"] # ariel font, default size
99
+ # Font["ariel", 12] # ariel font, the size is 12 point
100
+ class Font
101
+ # Create a new Font object.
102
+ # @param fontname[String] the name of font
103
+ # @param size[String, nil] the size of font, nil to default
104
+ # @example
105
+ # Font.new("ariel") # ariel font, default size
106
+ # Font.new("ariel", 12) # ariel font, the size is 12 point
107
+ def initialize(fontname, size=nil)
108
+ @s = size ? "#{fontname},#{size}" : fontname
109
+ end
110
+
111
+ # Convert self to gnuplot font (quoted) string.
112
+ # @visibility private
113
+ def to_gnuplot_string
114
+ "\"#{@s}\""
115
+ end
116
+
117
+ class << self
118
+ # @!method [](fontname, size=nil)
119
+ # Create a new Font object. See {#initialize}.
120
+ # @return [Font] A new Font object
121
+ __yard_ignore_here{ alias [] new }
122
+ end
123
+ end
124
+
125
+ # The class communicating a gnuplot subprocess.
126
+ #
127
+ # In many cases, you use {Plotter#plot} and you don't need to use
128
+ # this class directly.
129
+ class Process
130
+ # Create a Process object for already opened pipe.
131
+ # Normally, you had better to use {.open} to create a new object.
132
+ # @param pipe a pipe to communicate to a subprocess
133
+ # @param wait wait for the output from a subprocess
134
+ def initialize(pipe, wait)
135
+ @pipe = pipe
136
+ @wait = wait
137
+ @output = nil
138
+ end
139
+
140
+ # Output string from a gnuplot subprocess.
141
+ # @return [String, nil]
142
+ attr_reader :output
143
+
144
+ # Close the pipe and terminate the subprocess.
145
+ #
146
+ # If you specify wait=true when the object is constructed,
147
+ # @return [void]
148
+ def close
149
+ if @wait
150
+ @pipe.close_write
151
+ @output = @pipe.read
152
+ @pipe.close
153
+ else
154
+ @pipe.close
155
+ end
156
+ end
157
+
158
+ # Write +str+ to the subprocess.
159
+ # @return [void]
160
+ def write(str)
161
+ @pipe.write(str)
162
+ end
163
+
164
+ # Write +str+ and a newline to the subprocess
165
+ # @return [void]
166
+ def puts(str)
167
+ @pipe.puts(str)
168
+ end
169
+
170
+ # Open a gnuplot process.
171
+ #
172
+ # If a block is given, the block is invoked with the opened
173
+ # Process object. Otherwise, opened Process object is returned.
174
+ # If you give true as +persist+, -persist option is added to the
175
+ # command line of gnuplot.
176
+ # @param persist add -persist option to gnuplot as a command line option
177
+ # @param waiting wait for termination of stdout of gnuplot pipe
178
+ # @param name the name of the gnuplot executable file
179
+ def self.open(persist=true, waiting=true, name="gnuplot")
180
+ pr = new(IO.popen(command(name, persist), "w+"), waiting)
181
+
182
+ if block_given?
183
+ begin
184
+ yield pr
185
+ ensure
186
+ pr.close
187
+ end
188
+ return pr.output
189
+ else
190
+ return pr
191
+ end
192
+ end
193
+
194
+ # Return a command line string to invoke a subprocess.
195
+ # @!visibility private
196
+ def self.command(name, persist)
197
+ name += " -persist" if persist
198
+ name
199
+ end
200
+ end
201
+
202
+ # The plotter class.
203
+ # To plot something, you need to create a Plotter object,
204
+ # set parameters to the object, add data sets to the object,
205
+ # and call {#plot} as in the example below.
206
+ # @example
207
+ # # An array of data to plot
208
+ # data = -2.0.step(2.0, 0.05).map{|x| [x, Math.sin(x)]}
209
+ # # Create a new Plotter object
210
+ # plotter = NumPlot::Plotter.new
211
+ # # Set parameters
212
+ # plotter.set("term", "png")
213
+ # plotter.set("output", "\"sin.png\"") # Need to quote
214
+ # plotter.xrange -2.0 .. 2.0
215
+ # # Set datasets
216
+ # plotter.datasets << NumPlot::Dataset.rows(data, title: "sin(x)", with: line)
217
+ # # Plot (output to sin.png)
218
+ # plotter.plot
219
+ class Plotter
220
+ include Conversion
221
+
222
+ # Create a new Plotter object.
223
+ # You can specifiy the plot command (plot/splot)
224
+ # by plot_command parameter.
225
+ # @param plot_command[String] "plot" or "splot"
226
+ def initialize(plot_command = "plot")
227
+ @plot_command = plot_command
228
+ @commands = []
229
+ @datasets = []
230
+ end
231
+
232
+ # An array of all data sets.
233
+ # @deprecated
234
+ # @return [Array]
235
+ attr_reader :datasets
236
+
237
+ # Add a command string to the list of commands.
238
+ # The commands are executed when {#plot} is called.
239
+ # @param command[String] a command to add
240
+ # @return [self]
241
+ def add_command(command)
242
+ @commands << command
243
+ self
244
+ end
245
+ private :add_command
246
+
247
+ # Gnuplot "set" command.
248
+ # This method is generic, but
249
+ # you can use some useful methods like {#xrange} and {#xlabel} for
250
+ # "set" command.
251
+ # @param key[String]
252
+ # the first argument for gnuplot "set" command like "term", "output".
253
+ # @param values[Array<String>] the rest arguments for gnuplot "set" command
254
+ # @return [self]
255
+ def set(key, *values)
256
+ add_command("set #{key} #{values.join(' ')}")
257
+ end
258
+
259
+ # Gnuplot "unset" command.
260
+ # @param key[String]
261
+ # the first argument for gnuplot "unset" command
262
+ # @param values[Array<String>] the rest arguments for gnuplot "unset" command
263
+ # @see #set
264
+ # @return [self]
265
+ def unset(key, *values)
266
+ add_command("unset #{key} #{values.join(' ')}")
267
+ end
268
+
269
+ # Add a dataset to the plotter.
270
+ # @param dataset[Dataset] a dataset
271
+ # @return [self]
272
+ def add_dataset(dataset)
273
+ @datasets << dataset
274
+ self
275
+ end
276
+
277
+ alias << add_dataset
278
+
279
+ # @!macro [new] range
280
+ # @overload $0(range, *opts)
281
+ # Set $0 parameter.
282
+ # You can specify the range with one of three styles.
283
+ # * a string such as: "[-1.0:1.0]" or "[:2.0]"
284
+ # * a range such as: -1.0 .. 1.0 or -Float::INFINITY .. 2.0
285
+ # * an array such as: [-1.0, 1.0] or [nil, 2.0]
286
+ # You can also specify the following options as args:
287
+ # * "reverse"
288
+ # * "no reverse"
289
+ # * "writeback"
290
+ # * "no writeback"
291
+ # @param range[String,Range,Array] the range of x axis
292
+ # @param opts[Array<String>] options
293
+ # @return [self]
294
+
295
+ # @macro range
296
+ def xrange(*args); _range("xrange", *args); end
297
+ # @macro range
298
+ def yrange(*args); _range("yrange", *args); end
299
+ # @macro range
300
+ def zrange(*args); _range("zrange", *args); end
301
+ # @macro range
302
+ def x2range(*args); _range("x2range", *args); end
303
+ # @macro range
304
+ def y2range(*args); _range("y2range", *args); end
305
+
306
+ def _range(type, r, opts={})
307
+ gnuplot_options = ""
308
+ RANGE_OPTIONS.each do |opt|
309
+ gnuplot_options << opt.plot_arg(opts)
310
+ end
311
+
312
+ set(type, convert_range(r), gnuplot_options)
313
+ end
314
+ private :_range
315
+
316
+ # @!visibility private
317
+ RANGE_OPTIONS = [YesNoOption[:reverse], YesNoOption[:writeback]]
318
+
319
+ # @!macro [new] label
320
+ # @overload $0(label, opts={})
321
+ # Set $0 parameter.
322
+ # You can specify the following options as a hash:
323
+ # * offset: OFFSET
324
+ # * font: FONT
325
+ # * textcolor: COLOR (tc: is also available)
326
+ # * enhanced: true OR noenhanced: true
327
+ # * rotate: (TODO: FIX IT) OR norotate: true
328
+ # @param label[String] label string
329
+ # @param opts[{Symbol => Object}] options
330
+ # @return [self]
331
+
332
+ # @macro label
333
+ def xlabel(*args); _label("xlabel", *args); end
334
+ # @macro label
335
+ def ylabel(*args); _label("ylabel", *args); end
336
+ # @macro label
337
+ def zlabel(*args); _label("zlabel", *args); end
338
+ # @macro label
339
+ def x2label(*args); _label("x2label", *args); end
340
+ # @macro label
341
+ def y2label(*args); _label("y2label", *args); end
342
+
343
+ def _label(type, *args)
344
+ set(type, LabelParser.parse(*args))
345
+ end
346
+ private :_label
347
+
348
+ # Plot given datasets with given parameters.
349
+ # @overload plot(opts={})
350
+ # Open a gnuplot process and write commands and data to the process.
351
+ # You get the result of plotting on a window or an image file
352
+ #
353
+ # You can specify the following options:
354
+ # * :persist - Add -persist to gnuplot command.
355
+ # The default value is true.
356
+ # * :waiting - Wait a gnuplot process to be terminate.
357
+ # The default value is true. If this value is true and
358
+ # the diagram is drawed on a new window, the ruby program
359
+ # is stopped until the window is closed. On the other hand,
360
+ # if this value is false, the ruby program continue
361
+ # although the window remains opened.
362
+ # * :gnuplot_command - gnuplot command name.
363
+ #
364
+ # @param opts options
365
+ # @return [void]
366
+ #
367
+ # @overload plot(io)
368
+ # Output all commands and data to IO object.
369
+ #
370
+ # Normally, you specify {Process} object as io.
371
+ # For debug use, you can specify the StringIO object as io.
372
+ #
373
+ # @param io[#write, #puts] output target
374
+ # @return [void]
375
+ def plot(arg={})
376
+
377
+ if Hash === arg
378
+ Process.open(arg.fetch(:persist, true),
379
+ arg.fetch(:waiting, true),
380
+ arg.fetch(:gnuplot_command, "gnuplot")) do |pr|
381
+ plot_to_io(pr)
382
+ end
383
+ else
384
+ plot_to_io(arg)
385
+ end
386
+ end
387
+
388
+ # Output all commands and data to IO object.
389
+ def plot_to_io(pr)
390
+ @commands.each{|cmd| pr.puts(cmd) }
391
+ pr.write(@plot_command)
392
+ pr.write(" ")
393
+ pr.write(@datasets.map{|e| e.to_plot_arg }.join(", "))
394
+ pr.write("\n")
395
+ pr.write(@datasets.map{|e| e.to_plot_dataset }.compact.join("e\n"))
396
+ end
397
+ private :plot_to_io
398
+ end
399
+
400
+ # The dataset class plotted by {Plotter}.
401
+ #
402
+ # You can construct a dataset object using one of the following three methods:
403
+ # * {.columns}: construct a dataset from columns
404
+ # * {.rows}: construct a dataset from rows
405
+ # * {.histogram}: construct a histogram dataset
406
+ #
407
+ # You need to choose from {.columns} and {.rows} according to
408
+ # the arrangement of your data.
409
+ #
410
+ # The dataset object is registered to {Plotter} object using
411
+ # {Plotter#add_dataset}.
412
+ #
413
+ # == Parameters for a dataset
414
+ # When you call {.columns}, {.rows}, or {.histogram},
415
+ # you can give some parameters to determine the style of
416
+ # plotting.
417
+ # The parameters are as follows. If you know the details
418
+ # of these parameters, please read the gnuplot documents.
419
+ # * title: "TITLE" or notitle: true
420
+ # * with: "points", "dots", etc.
421
+ # * axis:
422
+ # * linestyle: a style number (ls: is also available)
423
+ # * linetype: a line type number
424
+ # * linewidth: a line width multiplier (default is 1.0),
425
+ # (lw: is also available)
426
+ # * linecolor: a color number or an {RGB} object.
427
+ # (lc: is also available)
428
+ # * pointtype: a point type number (pt: is also available)
429
+ # * pointsize: a point size multiplier (default is 1.0),
430
+ # (ls: is also available)
431
+ # * fill: a fill style (fs: is also available)
432
+ # * nohidden3d: true if you need to activate this option
433
+ # * nocontours: true if you need to activate this option
434
+ # * nosurface: true if you need to activate this option
435
+ class Dataset
436
+ def initialize(data, opts={})
437
+ @data = data
438
+ @opts = opts
439
+ end
440
+
441
+ # Construct a dataset object form columns.
442
+ #
443
+ # If your data are xs = [x0, x1, x2, ... ] and ys = [y0, y1, y2, ...],
444
+ # you should use this method as:
445
+ # Dataset.columns([xs, ys])
446
+ #
447
+ # If your data is transposed, you need to use {.columns} instead of
448
+ # this method.
449
+ #
450
+ # You can give some parameters as a hash
451
+ # to set a plotting style. Please see
452
+ # {Dataset}.
453
+ # @param data[Enumerable<Enumerable<Numeric>>]
454
+ # @param opts[{Symbol => object}] options
455
+ # @return [Dataset]
456
+ def self.columns(data, opts={})
457
+ new(data.map(&:to_a).transpose, opts)
458
+ end
459
+
460
+ # Construct a dataset object form rows.
461
+ #
462
+ # If your data is [[x0, y0], [x1, y1], ...],
463
+ # you should use this method as:
464
+ # Dataset.rows(data)
465
+ #
466
+ # If your data is transposed, you need to use {.rows} instead of
467
+ # this method.
468
+ #
469
+ # You can give some parameters as a hash
470
+ # to set a plotting style. Please see
471
+ # {Dataset} for details.
472
+ # @param data[Enumerable<Enumerable<Numeric>>]
473
+ # @param opts[{Symbol => object}] options
474
+ # @return [Dataset]
475
+ def self.rows(data, opts={})
476
+ new(data.map(&:to_a), opts)
477
+ end
478
+
479
+
480
+ # Construct a histogram dataset object.
481
+ #
482
+ # You need to give some parameters to construct a histogram.
483
+ # * dim: the dimension of your data, now only 1-dim data is supported
484
+ # (default is 1)
485
+ # * binsize: the bin size (required, Numeric)
486
+ # * base: the coordinate of a left side of a bin (default is 0.0).
487
+ # If binsize = 1.0 and base = 0.0, the bins are
488
+ # ..., [-1.0, 0.0), [0.0, 1.0), ... .
489
+ # If binsize = 1.0 and base = 0.5, the bins are
490
+ # ..., [-1.5, -0.5), [-0.5, 0.5), [0.5, 1.5), ... .
491
+ # * histogram_style: You can choose one of the following normalization
492
+ # scheme:
493
+ # * :count not normalized
494
+ # * :ratio normailized to the number of data points
495
+ # * :density normalized to (the number of data points)*(binsize).
496
+ # You choose this option if you want to show the probability density
497
+ # function from the data.
498
+ # * cumulative: construct a cumulative histogram if true.
499
+ # If the cumulative option is true, the meaning of
500
+ # histogram_style option is changed.
501
+ # * :count not normalized
502
+ # * :ratio, :density normalized by the number of data, this means that
503
+ # the maximum value of a cumulative histogram is normalized to 1.
504
+ #
505
+ # You can also give some other parameters to determine
506
+ # a plotting style. See {Dataset} for details.
507
+ #
508
+ # @example
509
+ # require 'randomext' # install randomext gem
510
+ # # 1000 random samples from the standard normal distribution.
511
+ # rng = Random.new
512
+ # data = Array.new(1000){ rng.standard_normal }
513
+ # datasets = Dataset.histo(data, binsize: 0.1, histogram_style: :density,
514
+ # title: "Random samples from the normal distribution")
515
+ #
516
+ # @param data[Enumerable<Numeric>] a sequence of numbers
517
+ # @param opts[{Symbol => Object}] options
518
+ # @return [Dataset]
519
+ def self.histogram(data, opts={})
520
+ case opts.fetch(:dim, 1)
521
+ when 1
522
+ histo1d(data, opts)
523
+ when 2
524
+ raise NotImplementedError, "Histogram for 2D data is not implemented yet"
525
+ else
526
+ raise ArgumentError, "You can use only 1D or 2D histogram data"
527
+ end
528
+ end
529
+
530
+ # @!visibility private
531
+ def self.histo1d(data, opts)
532
+ binsize = opts.fetch(:binsize).to_f
533
+ basepoint = opts.fetch(:base, 0).to_f
534
+ histo_style = opts.fetch(:histogram_style, :count)
535
+ cumulative = opts.fetch(:cumulative, false)
536
+
537
+ bins = Hash.new(0)
538
+ data.each {|point| bins[bin1d(point, binsize, basepoint)] += 1 }
539
+
540
+ if cumulative
541
+ plotdata = cumulative_1d(bins, histo_style, data.size, binsize)
542
+ else
543
+ plotdata = histogram_plotdata_1d(bins, histo_style, data.size, binsize)
544
+ end
545
+
546
+ opts[:with] ||= "boxes"
547
+ Dataset.rows(plotdata, opts)
548
+ end
549
+
550
+ # @!visibility private
551
+ def self.bin1d(point, binsize, basepoint)
552
+ (point + binsize/2).div(binsize) * binsize
553
+ end
554
+
555
+ # @!visibility private
556
+ def self.histogram_plotdata_1d(bins, style, numdata, binsize)
557
+ r = case style
558
+ when :count then 1.0
559
+ when :ratio then numdata.to_f
560
+ when :density then numdata*binsize
561
+ end
562
+ bins.map{|k, v| [k, v/r, binsize]}
563
+ end
564
+
565
+ # @!visibility private
566
+ def self.cumulative_1d(bins, style, numdata, binsize)
567
+ cumulative_data = []
568
+ bins.sort_by{|k, v| k }.inject(0.0){|sum, (k, v)|
569
+ cumulative_data << [k, sum+v]
570
+ sum + v
571
+ }
572
+ r = case style
573
+ when :count then 1.0
574
+ when :ratio, :density then numdata
575
+ end
576
+ cumulative_data.map{|k, v| [k, v/r, binsize] }
577
+ end
578
+
579
+ private_class_method :bin1d, :histo1d, :histogram_plotdata_1d, :cumulative_1d
580
+
581
+ class << self; alias histo histogram; end
582
+
583
+ # Return a stirng for gnuplot's "plot" or "splot" like "'-' with dots"
584
+ # @return [String]
585
+ # @!visibility private
586
+ def to_plot_arg
587
+ ret = "'-'"
588
+ OPTIONS.each do |opt|
589
+ ret << opt.plot_arg(@opts)
590
+ end
591
+
592
+ ret
593
+ end
594
+
595
+ # Return a string for gnuplot's data like "1 2 3\n4 4 6\ne"
596
+ # @!visibility private
597
+ def to_plot_dataset
598
+ @data.map{|dataline| dataline.join(" ")}.join("\n") + "\ne"
599
+ end
600
+
601
+ # @!visibility private
602
+ class Title
603
+ def plot_arg(h)
604
+ if h[:notitle]
605
+ " notitle"
606
+ elsif h.has_key?(:title)
607
+ " title \'#{h[:title]}\'"
608
+ else
609
+ ""
610
+ end
611
+ end
612
+ end
613
+
614
+ # @!visibility private
615
+ OPTIONS = [Title.new,
616
+ Option[:axis],
617
+ Option[:with],
618
+ Option[:linestyle, :ls],
619
+ Option[:linetype, :lt],
620
+ Option[:linewidth, :lw],
621
+ ColorOption[:linecolor, :lc],
622
+ Option[:pointtype, :pt],
623
+ Option[:pointsize, :ps],
624
+ Option[:fill, :fs],
625
+ BoolOption[:nohidden3d],
626
+ BoolOption[:nocontours],
627
+ BoolOption[:nosurface],
628
+ Option[:palette],
629
+ ]
630
+
631
+
632
+ end
633
+
634
+ # @!visibility private
635
+ class LabelParser
636
+ def self.parse(*args)
637
+ if Hash === args.last
638
+ opts = args.pop
639
+ else
640
+ opts = {}
641
+ end
642
+
643
+ label = args.shift
644
+
645
+ raise ArgumentError, "Unknown label argument #{args.inspect}" if !args.empty?
646
+ raise ArgumentError, "No label text" if label.nil?
647
+
648
+ gnuplot_string = Conversion.quote(label)
649
+
650
+ OPTIONS.each do |opt|
651
+ gnuplot_string << opt.plot_arg(opts)
652
+ end
653
+
654
+ gnuplot_string
655
+ end
656
+
657
+ OPTIONS = [Option[:offset],
658
+ FontOption[:font],
659
+ ColorOption[:textcolor, :tc],
660
+ YesNoOption[:enhanced],
661
+ Option[:rotate],
662
+ BoolOption[:norotate],
663
+ ]
664
+ end
665
+
666
+
667
+ end
data/sample/sample1.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'numplot'
2
+
3
+ # Draw a random sample from the normal distribution (sigma = 0.3)
4
+ # with Box-Muller's method.
5
+ def rand_normal
6
+ x = rand
7
+ y = rand
8
+ Math.sqrt(-2*Math.log(x))*Math.cos(2*Math::PI*y)*0.3
9
+ end
10
+
11
+ plotter = NumPlot::Plotter.new
12
+
13
+ x_sin_pi_x = (-1.0 .. 1.0).step(0.05).map{|x| [x, Math.sin(Math::PI*x)] }
14
+ plotter <<
15
+ NumPlot::Dataset.rows(x_sin_pi_x, with: "points", title: "sin(x)")
16
+
17
+ xs = (0.0 .. 1.0).step(0.05)
18
+ squares = xs.map{|x| x**2 }
19
+ plotter <<
20
+ NumPlot::Dataset.columns([xs, squares], with: "lines", title: "x^2")
21
+
22
+ normal_random_dist = Array.new(10000){ rand_normal }
23
+ plotter <<
24
+ NumPlot::Dataset.histo(normal_random_dist,
25
+ binsize: 0.1, histogram_style: :density,
26
+ title: "a normal distribution")
27
+
28
+ plotter.xrange(-1.0 .. 1.0)
29
+ plotter.xlabel("x", font: NumPlot::Font["ariel", 12])
30
+ plotter.ylabel("y", font: NumPlot::Font["ariel"])
31
+
32
+ plotter.set("key", "below")
33
+
34
+ plotter.plot
data/test/run_test.rb ADDED
@@ -0,0 +1,5 @@
1
+ testdir = File.dirname(__FILE__)
2
+ Dir.glob("#{testdir}/**/test_*.rb") do |dir|
3
+ load dir
4
+ end
5
+
@@ -0,0 +1,14 @@
1
+ require 'numplot'
2
+
3
+ require 'test-unit'
4
+
5
+ class TestRGB < Test::Unit::TestCase
6
+ include NumPlot
7
+
8
+ def test_s_parse
9
+ assert_equal("\"foo\"", LabelParser.parse("foo"))
10
+ assert_equal("\"foo\" offset 32 font \"bar,14\"",
11
+ LabelParser.parse("foo", offset: 32, font: Font["bar",14]))
12
+
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'numplot'
2
+
3
+ require 'test-unit'
4
+
5
+ class TestYesNoOption < Test::Unit::TestCase
6
+ include NumPlot
7
+
8
+ def test_plot_arg
9
+ assert_equal("", YesNoOption[:foo].plot_arg({}))
10
+ assert_equal("", YesNoOption[:foo].plot_arg({bar:true}))
11
+ assert_equal(" foo",
12
+ YesNoOption[:foo].plot_arg({bar:true, foo:true}))
13
+ assert_equal(" nofoo",
14
+ YesNoOption[:foo].plot_arg({bar:true, foo:false}))
15
+ assert_equal(" nofoo",
16
+ YesNoOption[:foo].plot_arg({bar:true, nofoo:true}))
17
+ assert_equal(" foo",
18
+ YesNoOption[:foo].plot_arg({bar:true, nofoo:false}))
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'numplot'
2
+
3
+ require 'test-unit'
4
+ require 'stringio'
5
+
6
+ class TestRGB < Test::Unit::TestCase
7
+ include NumPlot
8
+
9
+ def test_xrange
10
+ strio = StringIO.new
11
+ Plotter.new.xrange(1 .. 2, noreverse: false, nowriteback: true).plot(strio)
12
+ assert_equal("set xrange [1:2] reverse nowriteback\nplot \n", strio.string)
13
+ end
14
+ end
data/test/test_rgb.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'numplot'
2
+
3
+ require 'test-unit'
4
+
5
+ class TestRGB < Test::Unit::TestCase
6
+ include NumPlot
7
+
8
+ def test_to_gnuplot_string
9
+ assert_equal("rgbcolor \"black\"", RGB["black"].to_gnuplot_string)
10
+ assert_equal("rgbcolor \"#0f12af\"", RGB["#0f12af"].to_gnuplot_string)
11
+ assert_equal("rgbcolor \"#0f12af\"", RGB[0xf, 0x12, 0xaf].to_gnuplot_string)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: numplot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Ippei Obayashi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: test-unit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: |
28
+ This library is an wrapper for gnuplot.
29
+ email: ohai@kmc.gr.jp
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/numplot.rb
35
+ - lib/numplot/option.rb
36
+ - sample/sample1.rb
37
+ - test/run_test.rb
38
+ - test/test_labelparser.rb
39
+ - test/test_options.rb
40
+ - test/test_plotter.rb
41
+ - test/test_rgb.rb
42
+ homepage: http://www.kmc.gr.jp
43
+ licenses: []
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.0.0
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: An wrapper for gnuplot
65
+ test_files: []
66
+ has_rdoc: