rust 0.7 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,161 @@
1
+ require_relative '../rust'
2
+
3
+ module Rust
4
+ class RustDatatype
5
+ def self.pull_variable(variable, forced_interpreter = nil)
6
+ r_type = Rust._pull("as.character(typeof(#{variable}))")
7
+ r_class = Rust._pull("as.character(class(#{variable}))")
8
+
9
+ if forced_interpreter
10
+ raise ArgumentError, "Expected null or class as forced_interpreter" if forced_interpreter && !forced_interpreter.is_a?(Class)
11
+ raise ArgumentError, "Class #{forced_interpreter} can not handle type #{r_type}, class #{r_class}" unless forced_interpreter.can_pull?(r_type, r_class)
12
+
13
+ return forced_interpreter.pull_variable(variable, r_type, r_class)
14
+ end
15
+
16
+ candidates = []
17
+ ObjectSpace.each_object(Class) do |type|
18
+ if type < RustDatatype
19
+ if type.can_pull?(r_type, r_class)
20
+ candidates << type
21
+ end
22
+ end
23
+ end
24
+
25
+ if candidates.size > 0
26
+ type = candidates.max_by { |c| c.pull_priority }
27
+
28
+ puts "Using #{type} to pull #{variable}" if Rust.debug?
29
+ return type.pull_variable(variable, r_type, r_class)
30
+ else
31
+ if Rust._pull("length(#{variable})") == 0
32
+ return []
33
+ else
34
+ return Rust._pull(variable)
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.pull_priority
40
+ 0
41
+ end
42
+
43
+ def load_in_r_as(variable_name)
44
+ raise "Loading #{self.class} in R was not implemented"
45
+ end
46
+
47
+ def r_mirror_to(other_variable)
48
+ varname = self.mirrored_R_variable_name
49
+
50
+ Rust._eval("#{varname} = #{other_variable}")
51
+ Rust["#{varname}.hash"] = self.r_hash
52
+
53
+ return varname
54
+ end
55
+
56
+ def r_mirror
57
+ varname = self.mirrored_R_variable_name
58
+
59
+ if !Rust._pull("exists(\"#{varname}\")") || Rust._pull("#{varname}.hash") != self.r_hash
60
+ puts "Loading #{varname}" if Rust.debug?
61
+ Rust[varname] = self
62
+ Rust["#{varname}.hash"] = self.r_hash
63
+ else
64
+ puts "Using cached value for #{varname}" if Rust.debug?
65
+ end
66
+
67
+ return varname
68
+ end
69
+
70
+ def r_hash
71
+ self.hash.to_s
72
+ end
73
+
74
+ private
75
+ def mirrored_R_variable_name
76
+ return "rust.mirrored.#{self.object_id}"
77
+ end
78
+ end
79
+
80
+ class Null < RustDatatype
81
+ def self.can_pull?(type, klass)
82
+ return type == "NULL" && klass == "NULL"
83
+ end
84
+
85
+ def self.pull_variable(variable, type, klass)
86
+ return nil
87
+ end
88
+ end
89
+ end
90
+
91
+ class TrueClass
92
+ def to_R
93
+ "TRUE"
94
+ end
95
+ end
96
+
97
+ class FalseClass
98
+ def to_R
99
+ "FALSE"
100
+ end
101
+ end
102
+
103
+ class Object
104
+ def to_R
105
+ raise TypeError, "Unsupported type for #{self.class}"
106
+ end
107
+ end
108
+
109
+ class NilClass
110
+ def to_R
111
+ return "NULL"
112
+ end
113
+
114
+ def load_in_r_as(variable)
115
+ Rust._eval("#{variable} <- NULL")
116
+ end
117
+ end
118
+
119
+ class Numeric
120
+ def to_R
121
+ self.inspect
122
+ end
123
+ end
124
+
125
+ class Float
126
+ def to_R
127
+ return self.nan? ? "NA" : super
128
+ end
129
+ end
130
+
131
+ class Symbol
132
+ def to_R
133
+ return self.to_s.inspect
134
+ end
135
+ end
136
+
137
+ class Array
138
+ def to_R
139
+ return "c(#{self.map { |e| e.to_R }.join(",")})"
140
+ end
141
+
142
+ def distribution
143
+ result = {}
144
+ self.each do |value|
145
+ result[value] = result[value].to_i + 1
146
+ end
147
+ return result
148
+ end
149
+ end
150
+
151
+ class String
152
+ def to_R
153
+ return self.inspect
154
+ end
155
+ end
156
+
157
+ class Range
158
+ def to_R
159
+ [range.min, range.max].to_R
160
+ end
161
+ end
@@ -0,0 +1,131 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Factor < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass == "factor"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ levels = Rust["levels(#{variable})"]
11
+ values = Rust["as.integer(#{variable})"]
12
+
13
+ return Factor.new(values, levels)
14
+ end
15
+
16
+ def load_in_r_as(variable_name)
17
+ Rust['tmp.levels'] = @levels.map { |v| v.to_s }
18
+ Rust['tmp.values'] = @values
19
+
20
+ Rust._eval("#{variable_name} <- factor(tmp.values, labels=tmp.levels)")
21
+ end
22
+
23
+ def initialize(values, levels)
24
+ @levels = levels.map { |v| v.to_sym }
25
+ @values = values
26
+ end
27
+
28
+ def levels
29
+ @levels
30
+ end
31
+
32
+ def ==(other)
33
+ return false unless other.is_a?(Factor)
34
+
35
+ return @levels == other.levels && self.to_a == other.to_a
36
+ end
37
+
38
+ def [](i)
39
+ FactorValue.new(@values[i], @levels[@values[i] - 1])
40
+ end
41
+
42
+ def []=(i, value)
43
+ raise "The given value is outside the factor bounds" if value.is_a?(Integer) && (value < 1 || value > @levels.size)
44
+
45
+ if value.is_a?(FactorValue)
46
+ raise "Incompatible factor value, different levels used" unless @levels.include?(value.level) || @levels.index(value.level) + 1 == @value.value
47
+ value = value.value
48
+ end
49
+
50
+ if value.is_a?(String) || value.is_a?(Symbol)
51
+ value = value.to_sym
52
+ raise "Unsupported value #{value}; expected #{@levels.join(", ")}" unless @levels.include?(value)
53
+
54
+ value = @levels.index(value) + 1
55
+ end
56
+
57
+ @values[i] = value
58
+ end
59
+
60
+ def to_a
61
+ @values.map { |v| FactorValue.new(v, @levels[v - 1]) }
62
+ end
63
+
64
+ def method_missing(method, *args, &block)
65
+ raise NoMethodError, "Undefined method #{method} for Factor" if method.to_s.end_with?("!") || method.end_with?("=")
66
+
67
+ self.to_a.method(method).call(*args, &block)
68
+ end
69
+
70
+ def to_s
71
+ self.to_a.to_s
72
+ end
73
+
74
+ def inspect
75
+ self.to_a.inspect
76
+ end
77
+ end
78
+
79
+ class FactorValue
80
+ def initialize(value, level)
81
+ @value = value
82
+ @level = level
83
+ end
84
+
85
+ def value
86
+ @value
87
+ end
88
+
89
+ def level
90
+ @level
91
+ end
92
+
93
+ def to_i
94
+ @value
95
+ end
96
+
97
+ def to_sym
98
+ @level
99
+ end
100
+
101
+ def to_R
102
+ self.to_i
103
+ end
104
+
105
+ def inspect
106
+ @level.inspect
107
+ end
108
+
109
+ def ==(other)
110
+ if other.is_a?(FactorValue)
111
+ @value == other.value && @level == other.level
112
+ elsif other.is_a?(Integer)
113
+ @value == other
114
+ elsif other.is_a?(Symbol)
115
+ @level == other
116
+ end
117
+ end
118
+
119
+ def hash
120
+ @value.hash + @level.hash
121
+ end
122
+
123
+ def eql?(other)
124
+ return self == other
125
+ end
126
+
127
+ def method_missing(method, *args, &block)
128
+ @level.method(method).call(*args, &block)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,166 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Formula < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass == "formula" || (klass.is_a?(Array) && klass.include?("formula"))
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ formula_elements = Rust._pull("as.character(#{variable})")
11
+
12
+ assert("The number of elements of a formula must be 2 or 3: #{formula_elements} given") { formula_elements.size > 1 && formula_elements.size < 4 }
13
+ if formula_elements.size == 2
14
+ return Formula.new(nil, formula_elements[1])
15
+ elsif formula_elements.size == 3
16
+ return Formula.new(formula_elements[2], formula_elements[1])
17
+ end
18
+ end
19
+
20
+ def load_in_r_as(variable_name)
21
+ Rust._eval("#{variable_name} <- #{self.left_part} ~ #{self.right_part}")
22
+ end
23
+
24
+ attr_reader :left_part
25
+ attr_reader :right_part
26
+
27
+ def initialize(left_part, right_part)
28
+ raise ArgumentError, "Expected string" if left_part && !left_part.is_a?(String)
29
+ raise ArgumentError, "Expected string" if !right_part.is_a?(String)
30
+
31
+ @left_part = left_part || ""
32
+ @right_part = right_part
33
+ end
34
+
35
+ def ==(oth)
36
+ return false unless oth.is_a?(Formula)
37
+
38
+ return @left_part == oth.left_part && @right_part == oth.right_part
39
+ end
40
+
41
+ def to_R
42
+ return "#@left_part ~ #@right_part"
43
+ end
44
+
45
+ def inspect
46
+ return self.to_R.strip
47
+ end
48
+ end
49
+
50
+ class Call < RustDatatype
51
+ def self.can_pull?(type, klass)
52
+ return klass == "call"
53
+ end
54
+
55
+ def self.pull_variable(variable, type, klass)
56
+ return Call.new(Rust["deparse(#{variable})"])
57
+ end
58
+
59
+ def load_in_r_as(variable_name)
60
+ Rust["call.str"] = @value
61
+ Rust._eval("#{variable_name} <- str2lang(call.str)")
62
+ end
63
+
64
+ def initialize(value)
65
+ @value = value
66
+ end
67
+
68
+ def value
69
+ @value
70
+ end
71
+
72
+ def inspect
73
+ @value
74
+ end
75
+ end
76
+
77
+ class Environment < RustDatatype
78
+ def self.can_pull?(type, klass)
79
+ return type == "environment" && klass == "environment"
80
+ end
81
+
82
+ def self.pull_variable(variable, type, klass)
83
+ warn "Exchanging R environments is not supported!"
84
+ return Environment.new
85
+ end
86
+
87
+ def self.load_in_r_as(variable)
88
+ warn "Exchanging R environments is not supported!"
89
+ Rust._eval("#{variable} <- environment()")
90
+ end
91
+ end
92
+
93
+ class Function
94
+ attr_reader :name
95
+ attr_reader :arguments
96
+ attr_reader :options
97
+
98
+ def initialize(name)
99
+ @function = name
100
+ @arguments = Arguments.new
101
+ @options = Options.new
102
+ end
103
+
104
+ def options=(options)
105
+ raise TypeError, "Expected Options" unless options.is_a?(Options)
106
+
107
+ @options = options
108
+ end
109
+
110
+ def arguments=(arguments)
111
+ raise TypeError, "Expected Arguments" unless options.is_a?(Arguments)
112
+
113
+ @arguments = arguments
114
+ end
115
+
116
+ def to_R
117
+ params = [@arguments.to_R, @options.to_R].select { |v| v != "" }.join(",")
118
+ return "#@function(#{params})"
119
+ end
120
+
121
+ def call
122
+ Rust._eval(self.to_R)
123
+ end
124
+ end
125
+
126
+ class SimpleFormula
127
+ def initialize(dependent, independent)
128
+ @dependent = dependent
129
+ @independent = independent
130
+ end
131
+
132
+ def to_R
133
+ return "#@dependent ~ #@independent"
134
+ end
135
+ end
136
+
137
+ class Variable
138
+ def initialize(name)
139
+ @name = name
140
+ end
141
+
142
+ def to_R
143
+ @name
144
+ end
145
+ end
146
+
147
+ class Arguments < Array
148
+ def to_R
149
+ return self.map { |v| v.to_R }.join(", ")
150
+ end
151
+ end
152
+
153
+ class Options < Hash
154
+ def to_R
155
+ return self.map { |k, v| "#{k}=#{v.to_R}" }.join(", ")
156
+ end
157
+
158
+ def self.from_hash(hash)
159
+ options = Options.new
160
+ hash.each do |key, value|
161
+ options[key.to_s] = value
162
+ end
163
+ return options
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class List < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return type == "list"
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ return List.new(klass) if Rust._pull("length(#{variable})") == 0
11
+
12
+ names = [Rust["names(#{variable})"]].flatten
13
+ length = Rust["length(#{variable})"]
14
+
15
+ list = List.new(klass, names)
16
+ for i in 0...length
17
+ list[i] = Rust["#{variable}[[#{i + 1}]]"]
18
+ end
19
+
20
+ return list
21
+ end
22
+
23
+ def load_in_r_as(variable_name)
24
+ Rust._eval("#{variable_name} <- list()")
25
+ @data.each do |key, value|
26
+ Rust["#{variable_name}[[#{key + 1}]]"] = value
27
+ end
28
+ end
29
+
30
+ def initialize(klass, names = [])
31
+ @data = {}
32
+ @names = names
33
+ @klass = klass
34
+ end
35
+
36
+ def [](key)
37
+ key = get_key(key)
38
+
39
+ return @data[key]
40
+ end
41
+ alias :| :[]
42
+
43
+ def []=(key, value)
44
+ key = get_key(key)
45
+
46
+ return @data[key] = value
47
+ end
48
+
49
+ def names
50
+ @names
51
+ end
52
+
53
+ def inspect
54
+ result = ""
55
+ values_inspected = @data.map { |k, v| [k, v.inspect.split("\n").map { |l| " " + l }.join("\n")] }.to_h
56
+ max_length = [values_inspected.map { |k, v| v.split("\n").map { |line| line.length }.max.to_i }.max.to_i, 100].min
57
+
58
+ @data.keys.each do |i|
59
+ result << "-" * max_length + "\n"
60
+ result << (@names[i] || "[[#{i}]]") + "\n"
61
+ result << values_inspected[i] + "\n"
62
+ end
63
+ result << "-" * max_length
64
+
65
+ return result
66
+ end
67
+
68
+ private
69
+ def get_key(key)
70
+ if key.is_a?(String)
71
+ new_key = @names.index(key)
72
+ raise ArgumentError, "Wrong key: #{key}" unless new_key
73
+ key = new_key
74
+ end
75
+
76
+ raise ArgumentError, "The key should be either a string or an integer" unless key.is_a?(Integer)
77
+
78
+ return key
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,132 @@
1
+ require_relative 'datatype'
2
+
3
+ module Rust
4
+ class Matrix < RustDatatype
5
+ def self.can_pull?(type, klass)
6
+ return klass.is_a?(Array) && klass.include?("matrix")
7
+ end
8
+
9
+ def self.pull_variable(variable, type, klass)
10
+ if Rust._pull("length(#{variable})") == 1
11
+ core = ::Matrix[[Rust._pull("#{variable}[1]")]]
12
+ else
13
+ core = Rust._pull(variable)
14
+ end
15
+ row_names = [Rust["rownames(#{variable})"]].flatten
16
+ column_names = [Rust["colnames(#{variable})"]].flatten
17
+
18
+ row_names = nil if row_names.all? { |v| v == nil }
19
+ column_names = nil if column_names.all? { |v| v == nil }
20
+
21
+ Matrix.new(core, row_names, column_names)
22
+ end
23
+
24
+ def load_in_r_as(variable_name)
25
+ matrix = ::Matrix[*@data]
26
+
27
+ Rust[variable_name] = matrix
28
+ end
29
+
30
+ def initialize(data, row_names = nil, column_names = nil)
31
+ @data = data.clone
32
+
33
+ @row_names = row_names
34
+ @column_names = column_names
35
+
36
+ if @data.is_a?(::Matrix)
37
+ @data = @data.row_vectors.map { |v| v.to_a }
38
+ end
39
+
40
+ if self.flatten.size == 0
41
+ raise "Empty matrices are not allowed"
42
+ else
43
+ raise TypeError, "Expected array of array" unless @data.is_a?(Array) || @data[0].is_a?(Array)
44
+ raise TypeError, "Only numeric matrices are supported" unless self.flatten.all? { |e| e.is_a?(Numeric) }
45
+ raise "All the rows must have the same size" unless @data.map { |row| row.size }.uniq.size == 1
46
+ raise ArgumentError, "Expected row names #@row_names to match the number of rows in #{self.inspect}" if @row_names && @row_names.size != self.rows
47
+ raise ArgumentError, "Expected column names #@column_names to match the number of columns in #{self.inspect}" if @column_names && @column_names.size != self.cols
48
+ end
49
+ end
50
+
51
+ def [](i, j)
52
+ i, j = indices(i, j)
53
+
54
+ return @data[i][j]
55
+ end
56
+
57
+ def rows
58
+ @data.size
59
+ end
60
+
61
+ def cols
62
+ @data[0].size
63
+ end
64
+
65
+ def flatten
66
+ return @data.flatten
67
+ end
68
+
69
+ def []=(i, j, value)
70
+ i, j = indices(i, j)
71
+
72
+ @data[i][j] = value
73
+ end
74
+
75
+ def inspect
76
+ row_names = @row_names || (0...self.rows).to_a.map { |v| v.to_s }
77
+ column_names = @column_names || (0...self.cols).to_a.map { |v| v.to_s }
78
+
79
+ separator = " | "
80
+ col_widths = column_names.map do |colname|
81
+ [
82
+ colname,
83
+ (
84
+ [colname ? colname.length : 1] +
85
+ @data.map {|r| r[column_names.index(colname)]}.map { |e| e.inspect.length }
86
+ ).max
87
+ ]
88
+ end.to_h
89
+ col_widths[:rowscol] = row_names.map { |rowname| rowname.length }.max + 3
90
+
91
+ result = ""
92
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
93
+ result << (" " * col_widths[:rowscol]) + column_names.map { |colname| (" " * (col_widths[colname] - colname.length)) + colname }.join(separator) + "\n"
94
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length)) + "\n"
95
+
96
+ @data.each_with_index do |row, i|
97
+ row_name = row_names[i]
98
+ row = column_names.zip(row)
99
+
100
+ index_part = "[" + (" " * (col_widths[:rowscol] - row_name.length - 3)) + "#{row_name}] "
101
+ row_part = row.map { |colname, value| (" " * (col_widths[colname] - value.inspect.length)) + value.inspect }.join(separator)
102
+
103
+ result << index_part + row_part + "\n"
104
+ end
105
+
106
+ result << "-" * (col_widths.values.sum + ((col_widths.size - 1) * separator.length))
107
+
108
+ return result
109
+ end
110
+
111
+ private
112
+ def indices(i, j)
113
+ if i.is_a?(String)
114
+ ri = @row_names.index(i)
115
+ raise ArgumentError, "Can not find row #{i}" unless ri
116
+ i = ri
117
+ end
118
+
119
+ if j.is_a?(String)
120
+ rj = @column_names.index(j)
121
+ raise ArgumentError, "Can not find column #{j}" unless rj
122
+ j = rj
123
+ end
124
+
125
+ raise ArgumentError, "Expected i and j to be both integers or strings" unless i.is_a?(Integer) && j.is_a?(Integer)
126
+ raise "Wrong i" unless i.between?(0, @data.size - 1)
127
+ raise "Wrong j" unless j.between?(0, @data[0].size - 1)
128
+
129
+ return [i, j]
130
+ end
131
+ end
132
+ end