simpler 0.0.4 → 0.1.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.
@@ -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