rust 0.7 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,155 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+
5
+ ##
6
+ # Mirror of the matrix type in R.
7
+
8
+ class Matrix < RustDatatype
9
+ def self.can_pull?(type, klass)
10
+ return klass.is_a?(Array) && klass.include?("matrix")
11
+ end
12
+
13
+ def self.pull_variable(variable, type, klass)
14
+ if Rust._pull("length(#{variable})") == 1
15
+ core = ::Matrix[[Rust._pull("#{variable}[1]")]]
16
+ else
17
+ core = Rust._pull(variable)
18
+ end
19
+ row_names = [Rust["rownames(#{variable})"]].flatten
20
+ column_names = [Rust["colnames(#{variable})"]].flatten
21
+
22
+ row_names = nil if row_names.all? { |v| v == nil }
23
+ column_names = nil if column_names.all? { |v| v == nil }
24
+
25
+ Matrix.new(core, row_names, column_names)
26
+ end
27
+
28
+ def load_in_r_as(variable_name)
29
+ matrix = ::Matrix[*@data]
30
+
31
+ Rust[variable_name] = matrix
32
+ end
33
+
34
+ ##
35
+ # Creates a new matrix with the given +data+ (Ruby Matrix). Optionally, +row_names+ and +column_names+ can
36
+ # be specified.
37
+
38
+ def initialize(data, row_names = nil, column_names = nil)
39
+ @data = data.clone
40
+
41
+ @row_names = row_names
42
+ @column_names = column_names
43
+
44
+ if @data.is_a?(::Matrix)
45
+ @data = @data.row_vectors.map { |v| v.to_a }
46
+ end
47
+
48
+ if self.flatten.size == 0
49
+ raise "Empty matrices are not allowed"
50
+ else
51
+ raise TypeError, "Expected array of array" unless @data.is_a?(Array) || @data[0].is_a?(Array)
52
+ raise TypeError, "Only numeric matrices are supported" unless self.flatten.all? { |e| e.is_a?(Numeric) }
53
+ raise "All the rows must have the same size" unless @data.map { |row| row.size }.uniq.size == 1
54
+ raise ArgumentError, "Expected row names #@row_names to match the number of rows in #{self.inspect}" if @row_names && @row_names.size != self.rows
55
+ raise ArgumentError, "Expected column names #@column_names to match the number of columns in #{self.inspect}" if @column_names && @column_names.size != self.cols
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Returns the matrix element at row +i+ and column +j+.
61
+
62
+ def [](i, j)
63
+ i, j = indices(i, j)
64
+
65
+ return @data[i][j]
66
+ end
67
+
68
+ ##
69
+ # Sets the matrix element at row +i+ and column +j+ with +value+.
70
+
71
+ def []=(i, j, value)
72
+ i, j = indices(i, j)
73
+
74
+ @data[i][j] = value
75
+ end
76
+
77
+ ##
78
+ # Returns the number of rows.
79
+
80
+ def rows
81
+ @data.size
82
+ end
83
+
84
+ ##
85
+ # Returns the number of columns.
86
+
87
+ def cols
88
+ @data[0].size
89
+ end
90
+
91
+ ##
92
+ # Returns a flattened version of the matrix (Array).
93
+
94
+ def flatten
95
+ return @data.flatten
96
+ end
97
+
98
+ def inspect
99
+ row_names = @row_names || (0...self.rows).to_a.map { |v| v.to_s }
100
+ column_names = @column_names || (0...self.cols).to_a.map { |v| v.to_s }
101
+
102
+ separator = " | "
103
+ col_widths = column_names.map do |colname|
104
+ [
105
+ colname,
106
+ (
107
+ [colname ? colname.length : 1] +
108
+ @data.map {|r| r[column_names.index(colname)]}.map { |e| e.inspect.length }
109
+ ).max
110
+ ]
111
+ end.to_h
112
+ col_widths[:rowscol] = row_names.map { |rowname| rowname.length }.max + 3
113
+
114
+ result = ""
115
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
116
+ result << (" " * col_widths[:rowscol]) + column_names.map { |colname| (" " * (col_widths[colname] - colname.length)) + colname }.join(separator) + "\n"
117
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
118
+
119
+ @data.each_with_index do |row, i|
120
+ row_name = row_names[i]
121
+ row = column_names.zip(row)
122
+
123
+ index_part = "[" + (" " * (col_widths[:rowscol] - row_name.length - 3)) + "#{row_name}] "
124
+ row_part = row.map { |colname, value| (" " * (col_widths[colname] - value.inspect.length)) + value.inspect }.join(separator)
125
+
126
+ result << index_part + row_part + "\n"
127
+ end
128
+
129
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length))
130
+
131
+ return result
132
+ end
133
+
134
+ private
135
+ def indices(i, j)
136
+ if i.is_a?(String)
137
+ ri = @row_names.index(i)
138
+ raise ArgumentError, "Can not find row #{i}" unless ri
139
+ i = ri
140
+ end
141
+
142
+ if j.is_a?(String)
143
+ rj = @column_names.index(j)
144
+ raise ArgumentError, "Can not find column #{j}" unless rj
145
+ j = rj
146
+ end
147
+
148
+ raise ArgumentError, "Expected i and j to be both integers or strings" unless i.is_a?(Integer) && j.is_a?(Integer)
149
+ raise "Wrong i" unless i.between?(0, @data.size - 1)
150
+ raise "Wrong j" unless j.between?(0, @data[0].size - 1)
151
+
152
+ return [i, j]
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+
5
+ ##
6
+ # Mirror for the S4 class in R.
7
+
8
+ class S4Class < RustDatatype
9
+ def self.can_pull?(type, klass)
10
+ return type == "S4"
11
+ end
12
+
13
+ def self.pull_variable(variable, type, klass)
14
+ slots = [Rust._pull("names(getSlots(\"#{klass}\"))")].flatten
15
+
16
+ return S4Class.new(variable, klass, slots)
17
+ end
18
+
19
+ def load_in_r_as(variable_name)
20
+ Rust._eval("#{variable_name} <- #{self.r_mirror}")
21
+ end
22
+
23
+ def r_hash
24
+ "immutable"
25
+ end
26
+
27
+ ##
28
+ # Creates a new S4 element, given its +variable_name+, class name (+klass+), and +slots+.
29
+
30
+ def initialize(variable_name, klass, slots)
31
+ @klass = klass
32
+ @slots = slots
33
+
34
+ self.r_mirror_to(variable_name)
35
+ end
36
+
37
+ ##
38
+ # Returns the slot +key+ for the class name (+klass+).
39
+
40
+ def [](key)
41
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
42
+
43
+ Rust.exclusive do
44
+ return Rust["#{self.r_mirror}@#{key}"]
45
+ end
46
+ end
47
+ alias :| :[]
48
+
49
+ ##
50
+ # Returns the slot +key+ for the class name (+klass+) with +value+.
51
+
52
+ def []=(key, value)
53
+ raise ArgumentError, "Unknown slot `#{key}` for class `#@klass`" unless @slots.include?(key)
54
+
55
+ Rust.exclusive do
56
+ return Rust["#{self.r_mirror}@#{key}"] = value
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Returns the slots.
62
+
63
+ def slots
64
+ @slots
65
+ end
66
+
67
+ ##
68
+ # Returns the class name.
69
+
70
+ def class_name
71
+ @klass
72
+ end
73
+
74
+ def inspect
75
+ return "<S4 instance of #@klass, with slots #@slots>"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,122 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+
5
+ ##
6
+ # Represents a sequence of values in R (through a call to the seq function).
7
+
8
+ class Sequence < RustDatatype
9
+ attr_reader :min
10
+ attr_reader :max
11
+
12
+ def self.can_pull?(type, klass)
13
+ return false
14
+ end
15
+
16
+ ##
17
+ # Creates a new sequence from +min+ to +max+ with a given +step+ (default = 1).
18
+
19
+ def initialize(min, max, step=1)
20
+ @min = min
21
+ @max = max
22
+ @step = step
23
+ end
24
+
25
+ ##
26
+ # Sets the step to +step+.
27
+
28
+ def step=(step)
29
+ @step = step
30
+
31
+ return self
32
+ end
33
+ alias :step :step=
34
+
35
+ def each
36
+ (@min..@max).step(@step) do |v|
37
+ yield v
38
+ end
39
+ end
40
+
41
+ def to_a
42
+ result = []
43
+ self.each do |v|
44
+ result << v
45
+ end
46
+ return result
47
+ end
48
+
49
+ def to_R
50
+ "seq(from=#@min, to=#@max, by=#@step)"
51
+ end
52
+
53
+ def load_in_r_as(variable_name)
54
+ Rust._eval("#{variable_name} <- #{self.to_R}")
55
+ end
56
+ end
57
+
58
+ class MathArray < Array
59
+ def -(other)
60
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
61
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
62
+
63
+ result = self.clone
64
+ other = [other] * self.size if other.is_a?(Numeric)
65
+ for i in 0...self.size
66
+ result[i] -= other[i]
67
+ end
68
+
69
+ return result
70
+ end
71
+
72
+ def *(other)
73
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
74
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
75
+
76
+ result = self.clone
77
+ other = [other] * self.size if other.is_a?(Numeric)
78
+ for i in 0...self.size
79
+ result[i] *= other[i]
80
+ end
81
+
82
+ return result
83
+ end
84
+
85
+ def +(other)
86
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
87
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
88
+
89
+ result = self.clone
90
+ other = [other] * self.size if other.is_a?(Numeric)
91
+ for i in 0...self.size
92
+ result[i] += other[i]
93
+ end
94
+
95
+ return result
96
+ end
97
+
98
+ def /(other) #/# <- this comment is just to recover the syntax highlighting bug in Kate
99
+ raise ArgumentError, "Expected array or numeric" if !other.is_a?(::Array) && !other.is_a?(Numeric)
100
+ raise ArgumentError, "The two arrays must have the same size" if other.is_a?(::Array) && self.size != other.size
101
+
102
+ result = self.clone
103
+ other = [other] * self.size if other.is_a?(Numeric)
104
+ for i in 0...self.size
105
+ result[i] /= other[i]
106
+ end
107
+
108
+ return result
109
+ end
110
+
111
+ def **(other)
112
+ raise ArgumentError, "Expected numeric" if !other.is_a?(Numeric)
113
+
114
+ result = self.clone
115
+ for i in 0...self.size
116
+ result[i] = result[i] ** other
117
+ end
118
+
119
+ return result
120
+ end
121
+ end
122
+ end
data/lib/rust/core.rb ADDED
@@ -0,0 +1,7 @@
1
+ require_relative 'core/rust'
2
+ require_relative 'core/csv'
3
+
4
+ self_path = File.expand_path(__FILE__)
5
+ Dir.glob(File.join(File.dirname(self_path), "core/types/*.rb")).each do |lib|
6
+ require_relative lib
7
+ end
@@ -0,0 +1,44 @@
1
+ require 'rust'
2
+
3
+ Rust.prerequisite('robustbase')
4
+
5
+ module Rust::Plots
6
+ class AdjustedBoxplot < DistributionPlot
7
+ protected
8
+ def _show()
9
+ function = Rust::Function.new("adjbox")
10
+
11
+ names = []
12
+ @series.each_with_index do |data, i|
13
+ series, options = *data
14
+ varname = "plotter.series#{i}"
15
+ Rust[varname] = series
16
+ function.arguments << Rust::Variable.new(varname)
17
+ names << (options[:name] || (i+1).to_s)
18
+ end
19
+
20
+ function.options = self._augmented_options({'names' => names})
21
+
22
+ function.call
23
+
24
+ return self
25
+ end
26
+ end
27
+ end
28
+
29
+ module Rust::RBindings
30
+ def adjbox(*args, **options)
31
+ result = Rust::Plots::AdjustedBoxplot.new
32
+ options.each do |k, v|
33
+ result[k] = v
34
+ end
35
+
36
+ result._do_not_override_options!
37
+
38
+ args.each do |s|
39
+ result.series(s)
40
+ end
41
+
42
+ result.show
43
+ end
44
+ end
@@ -0,0 +1,4 @@
1
+ self_path = File.expand_path(__FILE__)
2
+ Dir.glob(File.dirname(self_path) + "/*.rb").each do |lib|
3
+ require_relative lib unless lib == self_path
4
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../core'
2
+
3
+ module Rust
4
+
5
+ ##
6
+ # Mirror for an ANOVA model type in R. To create a new ANOVA model (aov), call the #generate method.
7
+
8
+ class ANOVAModel < RustDatatype
9
+ def self.can_pull?(type, klass)
10
+ return type == "list" && [klass].flatten.include?("aov")
11
+ end
12
+
13
+ def self.pull_variable(variable, type, klass)
14
+ model = RustDatatype.pull_variable(variable, Rust::List)
15
+
16
+ return ANOVAModel.new(model)
17
+ end
18
+
19
+ def load_in_r_as(variable_name)
20
+ @model.load_in_r_as(variable_name)
21
+ end
22
+
23
+ ##
24
+ # Generates a new ANOVA model with a given +formula+, +data+. +options+ can be specified and directly passed
25
+ # to the aov function in R.
26
+
27
+ def self.generate(formula, data, **options)
28
+ mapped = ""
29
+ if options.size > 0
30
+ mapped = options.map { |k, v| "#{k}=#{v}" }.join(", ")
31
+ mapped = ", " + mapped
32
+ end
33
+
34
+ Rust.exclusive do
35
+ Rust["aov.data"] = data
36
+ Rust._eval("aov.model.result <- aov(#{formula.to_R}, data=aov.data#{mapped})")
37
+ result = ANOVAModel.new(Rust["aov.model.result"])
38
+ result.r_mirror_to("aov.model.result")
39
+ return result
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Creates a new +model+.
45
+
46
+ def initialize(model)
47
+ @model = model
48
+ end
49
+
50
+ ##
51
+ # Returns the model.
52
+
53
+ def model
54
+ @model
55
+ end
56
+
57
+ ##
58
+ # Returns a summary of the ANOVA model through the summary function in R.
59
+
60
+ def summary
61
+ unless @summary
62
+ Rust.exclusive do
63
+ Rust._eval("aov.smr <- summary(#{self.r_mirror})")
64
+ @summary = Rust['aov.smr']
65
+ end
66
+ end
67
+
68
+ return @summary
69
+ end
70
+ end
71
+ end
72
+
73
+ module Rust::RBindings
74
+ def aov(formula, data, **options)
75
+ return ANOVAModel.generate(formula, data, **options)
76
+ end
77
+ end