simpler 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,26 +12,33 @@ to you.
12
12
 
13
13
  == Examples
14
14
 
15
- FYI - the API is still unstable.
15
+ FYI - the API is still a little unstable.
16
16
 
17
17
  ==== Basic execution (Raw input, raw output)
18
18
 
19
19
  require 'simpler'
20
- simpler = Simpler.new
21
- simpler.run!("mean(c(1,2,3))") # -> "[1] 2\n" (a Simpler::Reply object)
20
+
21
+ r = Simpler.new
22
+ r.eval! { "mean(c(1,2,3))" } # -> "[1] 2\n" (a Simpler::Reply object)
22
23
 
23
24
  ==== Using ruby variables (calculating correlation coefficient):
24
25
 
25
- xv = [1,2,7]
26
- yv = [3,4,8]
27
- reply = simpler.with(xv,yv) {|x,y| "cor(#{x},#{y})" }.run! # -> "[1] 0.9994238\n"
26
+ x = [1,2,7]
27
+ y = [3,4,8]
28
+ reply = r.eval!(x,y) {|xr,yr| "cor(#{xr},#{yr})" } # -> "[1] 0.9994238\n"
29
+
30
+ Any object that has a #to_r method can be passed in. Currently, Array and
31
+ Simpler::DataFrame are the only objects with this method defined, but one can
32
+ go really far with only these two data types.
28
33
 
29
34
  ==== Show a plot
30
35
 
31
- "Rscript" writes plotting commands that would normally go to an X window to "Rplots.pdf". *show!* merely executes your code and opens Rplots.pdf with @pdf_viewer. It's low tech, but it works.
36
+ Simpler uses <i>Rscript</i> to run R commands. So, all plots that would
37
+ normally go to X11 are saved to "Rplots.pdf". *show!* is precisly the same as
38
+ *eval!*, but it also opens "Rplots.pdf" with <i>@pdf_viewer</i>:
32
39
 
33
- simpler.pdf_viewer = "acroread"
34
- simpler.with(xv,yv) {|x,y| "plot(#{x}, #{y})" }.show!
40
+ r.pdf_viewer = "acroread"
41
+ r.show!(x,y) {|xr,yr| "plot(#{xr}, #{yr})" }
35
42
 
36
43
  ==== Using DataFrames
37
44
 
@@ -42,9 +49,16 @@ FYI - the API is still unstable.
42
49
  }
43
50
 
44
51
  df = Simpler::DataFrame.new(hash)
45
- simpler.with(df) {|d| "plot(#{d})" }.show!
52
+ r.show!(df) {|dfr| "plot(#{dfr})" }
53
+
54
+ One can also make a data frame from an array of structs with Simpler.from_structs
55
+
56
+ ==== When Errors Arise
57
+
58
+ On an R error, raises a Simpler::RError that returns the R error message and
59
+ shows the R code submitted. (the @command queue is still cleared)
46
60
 
47
- DataFrame also supports named rows and specifying directly column names.
61
+ r.eval! { "plot(c(1,2,3),c(1,2))" }
48
62
 
49
63
  == Credit
50
64
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.0
@@ -16,61 +16,91 @@ class Simpler
16
16
  RPLOTS_FILE = "Rplots.pdf"
17
17
  PDF_VIEWER = "evince"
18
18
 
19
- # returns the variable name of the object
19
+ # @param [Object] object a ruby object,
20
+ # @return [String] the variable name of the object
20
21
  def self.varname(obj)
21
22
  "rb#{obj.object_id}"
22
23
  end
23
24
 
24
- # returns it as a symbol, currently recognizes pdf, png, svg
25
- def self.filename_to_plottype(name)
26
- name.match(/\.([^\.]+)$/)[1].downcase.to_sym
27
- end
28
-
29
25
  attr_accessor :commands
30
26
  attr_accessor :pdf_viewer
31
27
 
28
+ # @param [Array] commands a list of R commands to execute
29
+ # @param [Hash] opts options (currently :pdf_viewer which defaults to
30
+ # PDF_VIEWER)
32
31
  def initialize(commands=[], opts={:pdf_viewer => PDF_VIEWER})
33
32
  @pdf_viewer = opts[:pdf_viewer]
34
33
  @commands = commands
35
34
  end
36
35
 
37
- def r_format(object)
38
- case object
39
- when String
40
- object.inspect
41
- when Numeric
42
- object.to_s
43
- else
44
- object.to_s
45
- end
36
+ # @example basic
37
+ # # find the p value of a t-test
38
+ # r.eval! do
39
+ # %Q{
40
+ # x <- c(1,2,3)
41
+ # y <- c(4,5,6)
42
+ # ttest = t.test(x, y)
43
+ # ttest$p.value
44
+ # }
45
+ # end # -> "[1] 0.02131164\n"
46
+ #
47
+ # @example pass in variables (roughly equivalent)
48
+ # x = [1,2,3]
49
+ # y = [4,5,6]
50
+ # r.eval!(x,y) do |xr,yr|
51
+ # %Q{
52
+ # ttest = t.test(#{xr}, #{yr})
53
+ # ttest$p.value
54
+ # }
55
+ # end # -> "[1] 0.02131164\n"
56
+ #
57
+ # (see Simpler#with)
58
+ def eval!(*objects, &block)
59
+ with(*objects, &block).run!
46
60
  end
47
61
 
48
- # displays the Rplots.pdf file at the end of execution
49
- def show!(string=nil)
50
- if File.exist?(RPLOTS_FILE)
51
- original_mtime = File.mtime(RPLOTS_FILE)
52
- end
53
- reply = run!(string)
54
- system "#{@pdf_viewer} #{RPLOTS_FILE} &"
62
+ # (see Simpler#eval!) same as eval! but also opens "Rplots.pdf" with PDF_VIEWER
63
+ # @example plotting
64
+ # x = [1,2,3]
65
+ # y = [4,5,6]
66
+ # r.show!(x,y) do |xr,yr|
67
+ # "plot(#{xr}, #{yr})"
68
+ # end
69
+ #
70
+ # Simpler uses Rscript to run its code and Rscript writes X11 output to the
71
+ # PDF file "Rplots.pdf". Simpler#show! merely makes a system call to open up
72
+ # the pdf file for viewing. Obviously, for more interactive sessions one
73
+ # should just use R.
74
+ def show!(*objects, &block)
75
+ with(*objects, &block)
76
+ reply = run!
77
+ open_pdf_viewer(@pdf_viewer, RPLOTS_FILE)
55
78
  reply
56
79
  end
57
80
 
58
- def error_message(error_string, r_code)
59
- error_message = ""
60
- error_message << "\n"
61
- error_message << "*************************** Error inside R *****************************\n"
62
- error_message << error_string
63
- error_message << "------------------------- [R code submitted] ---------------------------\n"
64
- error_message << r_code
65
- error_message << "------------------------------------------------------------------------\n"
66
- error_message
81
+ # @param [*Objects] objects list of objects
82
+ # @yield [*r_varnames] yields the R name of each variable (see Simpler.varname)
83
+ # @return [Simpler] returns self for chaining
84
+ # @example chaining
85
+ # reply = r.with(dataframe) do |df|
86
+ # %Q{ ... something with #{df} ... }
87
+ # end.with(x,y,z) do |xr,yr,zr|
88
+ # %Q{ ... something with #{xy} #{yr} #{zr} ... }
89
+ # end.eval!
90
+ # # can also .show!
91
+ def with(*objects, &block)
92
+ var_names = objects.map {|v| Simpler.varname(v) }
93
+ conversion_code = objects.map {|v| v.to_r }
94
+ @commands.push(*conversion_code)
95
+ unless block.nil?
96
+ @commands << block.call(*var_names)
97
+ end
98
+ self
67
99
  end
68
100
 
69
-
70
- # pushes string onto command array (if given), executes all commands, and
71
- # clears the command array.
72
- def run!(string=nil)
73
- @commands.push(string) if string
101
+ # executes all commands and clears the command array.
102
+ # @return [Simpler::Reply] a String subclass
103
+ def run!
74
104
  reply = nil
75
105
  error = nil
76
106
  cmds_to_run = @commands.map {|v| v + "\n"}.join
@@ -87,24 +117,38 @@ class Simpler
87
117
  Simpler::Reply.new(reply)
88
118
  end
89
119
 
90
- # returns self for chaining
91
- def with(*objects, &block)
92
- var_names = objects.map {|v| Simpler.varname(v) }
93
- conversion_code = objects.map {|v| v.to_r }
94
- @commands.push(*conversion_code)
95
- @commands << block.call(*var_names)
96
- self
120
+ # formats strings and numbers to fit inside R code (this is for Strings and
121
+ # numbers mainly in inclusion in options)
122
+ # String ==> "\"String\""
123
+ # Numeric ==> "Numeric"
124
+ # Other objects => "#{object.to_s}"
125
+ # @param [Object] object the ruby object
126
+ # @return [String] string suitable for R code
127
+ def self.r_format(object)
128
+ case object
129
+ when String
130
+ object.inspect
131
+ when Numeric
132
+ object.to_s
133
+ else
134
+ object.to_s
135
+ end
97
136
  end
98
137
 
99
- def show_with!(*objects, &block)
100
- with(*objects, &block).show!
101
- end
102
-
103
- def run_with!(*objects, &block)
104
- with(*objects, &block).run!
138
+ def error_message(error_string, r_code)
139
+ error_message = ""
140
+ error_message << "\n"
141
+ error_message << "*************************** Error inside R *****************************\n"
142
+ error_message << error_string
143
+ error_message << "------------------------- [R code submitted] ---------------------------\n"
144
+ error_message << r_code
145
+ error_message << "------------------------------------------------------------------------\n"
146
+ error_message
105
147
  end
106
148
 
107
- alias_method :go!, :run_with!
149
+ def open_pdf_viewer(viewer, *files)
150
+ system "#{@pdf_viewer} #{files.join(" ")} &"
151
+ end
108
152
 
109
153
  class RError < StandardError
110
154
  end
@@ -6,6 +6,23 @@ class Simpler
6
6
  attr_accessor :col_names
7
7
  attr_accessor :row_names
8
8
  attr_accessor :hash
9
+
10
+ # takes an array of structs and returns a data frame object
11
+ def self.from_structs(array)
12
+ names = array.first.members
13
+ lengthwise_arrays = names.map { Array.new(names.size) }
14
+ array.each_with_index do |struct,m|
15
+ struct.values.each_with_index do |val,n|
16
+ lengthwise_arrays[n][m] = val
17
+ end
18
+ end
19
+ hash = {}
20
+ names.zip(lengthwise_arrays) do |name, lengthwise_array|
21
+ hash[name] = lengthwise_array
22
+ end
23
+ self.new(hash)
24
+ end
25
+
9
26
  # takes a hash, where the col_name is the key and the data rows are an
10
27
  # array of values. The default ordering of the hash keys will be used,
11
28
  # unless overridden with col_names. The row_names can be used to specify
@@ -2,9 +2,14 @@
2
2
  class Simpler
3
3
  module Plot
4
4
 
5
+ # returns it as a symbol, currently recognizes pdf, png, svg
6
+ def self.filename_to_plottype(name)
7
+ name.match(/\.([^\.]+)$/)[1].downcase.to_sym
8
+ end
9
+
5
10
  def plot(file_w_extension, opts={}, &block)
6
- device = self.class.filename_to_plottype(file_w_extension)
7
- opts_as_ropts = opts.map {|k,v| "#{k}=#{r_format(v)}"}
11
+ device = Simpler::Plot.filename_to_plottype(file_w_extension)
12
+ opts_as_ropts = opts.map {|k,v| "#{k}=#{Simpler.r_format(v)}"}
8
13
  string = "#{device}(#{file_w_extension.inspect}, #{opts_as_ropts.join(', ')})\n"
9
14
  string << block.call << "\n"
10
15
  string << "dev.off()\n"
@@ -1,6 +1,7 @@
1
1
 
2
2
  class Simpler
3
3
  class Reply < String
4
+ ONE_FLOAT_RE = /] ([\w\.-]+)/o
4
5
 
5
6
  # removes the [1] from the line
6
7
  def rm_leader
@@ -11,11 +12,9 @@ class Simpler
11
12
  self.chomp.split("\n").rm_leader.split(" ")
12
13
  end
13
14
 
14
- def to_f
15
- end
16
-
17
- def to_i
18
- rm_leader.to_f
15
+ # returns a single float. Assumes the answer takes the form: "[1] <Float>\n" => Float
16
+ def to_sf
17
+ ONE_FLOAT_RE.match(self)[1].to_f
19
18
  end
20
19
 
21
20
  end
@@ -16,7 +16,7 @@ describe "Simpler" do
16
16
  end
17
17
 
18
18
  it 'can get a reply' do
19
- reply = @r.run!("typos = c(2,3,0,3,1,0,0,1)\nmean(typos)\nhist(typos)")
19
+ reply = @r.eval!{"typos = c(2,3,0,3,1,0,0,1)\nmean(typos)\nhist(typos)"}
20
20
  reply.chomp.is "[1] 1.25"
21
21
  end
22
22
 
@@ -26,7 +26,7 @@ describe "Simpler" do
26
26
  "plot(c(1,2), c(2))" => %r{'x' and 'y' lengths differ},
27
27
  }
28
28
  code_matches.each do |code, matches|
29
- message = lambda { @r.run!(code) }.should.raise(Simpler::RError).message
29
+ message = lambda { @r.eval!{code} }.should.raise(Simpler::RError).message
30
30
  message.should.match(matches)
31
31
  message.should.include code
32
32
  end
@@ -51,7 +51,7 @@ wetness 6 2 1
51
51
  relative humidity 7 9 7
52
52
  }
53
53
  df = Simpler::DataFrame.new(@hash, @row_names, @col_names)
54
- as_data_frame = Simpler.new.with(df) {|df| df }.run!
54
+ as_data_frame = Simpler.new.eval!(df) {|df| df }
55
55
  as_data_frame.is expected
56
56
  end
57
57
 
@@ -63,7 +63,7 @@ relative humidity 7 9 7
63
63
  4 7 9 7
64
64
  }
65
65
  df = Simpler::DataFrame.new(@hash, nil, @col_names)
66
- as_data_frame = Simpler.new.with(df) {|df| df }.run!
66
+ as_data_frame = Simpler.new.eval!(df) {|df| df }
67
67
  as_data_frame.is expected
68
68
  end
69
69
  end
@@ -81,13 +81,13 @@ describe 'making plots' do
81
81
  it 'has convenience wrappers for plotting to file' do
82
82
  @exts.each do |ext|
83
83
  file = @file + "." + ext
84
- @r.with(@x, @y) do |x,y|
84
+ @r.eval!(@x, @y) do |x,y|
85
85
  @r.plot(file) do
86
86
  %Q{
87
87
  plot(#{x},#{y}, main="#{ext} scatterplot example", col=rgb(0,100,0,50,maxColorValue=255), pch=16)
88
88
  }
89
89
  end
90
- end.run!
90
+ end
91
91
  File.exist?(file).is true
92
92
  IO.read(@file + ".svg").matches(/svg/) if ext == :svg
93
93
  File.unlink(file)
@@ -116,9 +116,9 @@ describe 'showing plots' do
116
116
  it 'can disply plots' do
117
117
  ok $SYSTEM_CALL.nil?
118
118
 
119
- @r.with(@x, @y) do |x,y|
119
+ @r.show!(@x, @y) do |x,y|
120
120
  "plot(#{x}, #{y})"
121
- end.show!
121
+ end
122
122
 
123
123
  # this shows that we've made a system call to visualize the data
124
124
  ok !$SYSTEM_CALL.nil?
@@ -127,7 +127,7 @@ end
127
127
 
128
128
  describe 'ancillary functions' do
129
129
  it 'can get the device from the filename' do
130
- Simpler.filename_to_plottype("bob.the.pdf").is :pdf
131
- Simpler.filename_to_plottype("larry.the.svg").is :svg
130
+ Simpler::Plot.filename_to_plottype("bob.the.pdf").is :pdf
131
+ Simpler::Plot.filename_to_plottype("larry.the.svg").is :svg
132
132
  end
133
133
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 4
9
- version: 0.0.4
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - John Prince
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-13 00:00:00 -06:00
17
+ date: 2010-08-03 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency