Class: Rover::Vector

Inherits:
Object
  • Object
show all
Defined in:
lib/rover/vector.rb

Constant Summary collapse

TYPE_CAST_MAPPING =

if a user never specifies types, the defaults are bool, float64, int64, and object

{
  bool: Numo::Bit,
  float32: Numo::SFloat,
  float64: Numo::DFloat,
  int8: Numo::Int8,
  int16: Numo::Int16,
  int32: Numo::Int32,
  int64: Numo::Int64,
  object: Numo::RObject,
  uint8: Numo::UInt8,
  uint16: Numo::UInt16,
  uint32: Numo::UInt32,
  uint64: Numo::UInt64,
  # legacy - must come last
  float: Numo::DFloat,
  int: Numo::Int64,
  uint: Numo::UInt64
}
NOT_SET =
Object.new
INTEGER_TYPES =

private

[:int8, :int16, :int32, :int64, :uint8, :uint16, :uint32, :uint64]

Instance Method Summary collapse

Constructor Details

#initialize(data, type: nil) ⇒ Vector

Returns a new instance of Vector.

Raises:

  • (ArgumentError)


29
30
31
32
# File 'lib/rover/vector.rb', line 29

def initialize(data, type: nil)
  @data = cast_data(data, type: type)
  raise ArgumentError, "Bad size: #{@data.shape}" unless @data.ndim == 1
end

Instance Method Details

#!Object



161
162
163
164
165
166
167
# File 'lib/rover/vector.rb', line 161

def !
  if @data.is_a?(Numo::Bit)
    Vector.new(@data.eq(0))
  else
    raise "Not implemented yet"
  end
end

#-@Object



169
170
171
# File 'lib/rover/vector.rb', line 169

def -@
  self * -1
end

#[](v) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rover/vector.rb', line 91

def [](v)
  if v.is_a?(Vector)
    case v.type
    when :bool
      Vector.new(v.to_numo.mask(@data))
    when *INTEGER_TYPES
      Vector.new(@data[v.to_numo])
    else
      raise ArgumentError, "Unsupported selector"
    end
  elsif v.is_a?(Numeric)
    @data[v]
  else
    Vector.new(@data[v])
  end
end

#[]=(k, v) ⇒ Object



108
109
110
111
# File 'lib/rover/vector.rb', line 108

def []=(k, v)
  k = k.to_numo if k.is_a?(Vector)
  @data[k] = v
end

#absObject



211
212
213
# File 'lib/rover/vector.rb', line 211

def abs
  Vector.new(@data.abs)
end

#all?Boolean

Returns:

  • (Boolean)


318
319
320
# File 'lib/rover/vector.rb', line 318

def all?(...)
  to_a.all?(...)
end

#any?Boolean

Returns:

  • (Boolean)


322
323
324
# File 'lib/rover/vector.rb', line 322

def any?(...)
  to_a.any?(...)
end

#ceil(ndigits = 0) ⇒ Object



223
224
225
226
227
228
229
# File 'lib/rover/vector.rb', line 223

def ceil(ndigits = 0)
  if ndigits == 0
    Vector.new(@data.ceil)
  else
    Vector.new(@data.map { |v| v.ceil(ndigits) })
  end
end

#clamp(min, max) ⇒ Object



178
179
180
# File 'lib/rover/vector.rb', line 178

def clamp(min, max)
  dup.clamp!(min, max)
end

#clamp!(min, max) ⇒ Object



173
174
175
176
# File 'lib/rover/vector.rb', line 173

def clamp!(min, max)
  @data = @data.clip(min, max)
  self
end

#crosstab(other) ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/rover/vector.rb', line 363

def crosstab(other)
  index = uniq.sort
  index_pos = index.to_a.map.with_index.to_h
  df = DataFrame.new({"_" => index})
  other.uniq.sort.each do |k|
    df[k] = 0
  end
  to_a.zip(other.to_a) do |v1, v2|
    df[v2][index_pos[v1]] += 1
  end
  df
end

#diffObject

keep same number of rows as original to make it easy to add to original data frame



86
87
88
89
# File 'lib/rover/vector.rb', line 86

def diff
  diff = @data.cast_to(Numo::DFloat).diff
  Vector.new(diff.insert(0, Float::NAN))
end

#each(&block) ⇒ Object



273
274
275
# File 'lib/rover/vector.rb', line 273

def each(&block)
  @data.each(&block)
end

#each_with_index(&block) ⇒ Object



277
278
279
# File 'lib/rover/vector.rb', line 277

def each_with_index(&block)
  @data.each_with_index(&block)
end

#empty?Boolean

Returns:

  • (Boolean)


326
327
328
# File 'lib/rover/vector.rb', line 326

def empty?
  size == 0
end

#first(n = NOT_SET) ⇒ Object



338
339
340
341
342
343
344
345
346
# File 'lib/rover/vector.rb', line 338

def first(n = NOT_SET)
  if n == NOT_SET
    @data[0]
  elsif n >= size
    Vector.new(@data)
  else
    Vector.new(@data[0...n])
  end
end

#floor(ndigits = 0) ⇒ Object



231
232
233
234
235
236
237
# File 'lib/rover/vector.rb', line 231

def floor(ndigits = 0)
  if ndigits == 0
    Vector.new(@data.floor)
  else
    Vector.new(@data.map { |v| v.floor(ndigits) })
  end
end

#frexpObject



263
264
265
266
# File 'lib/rover/vector.rb', line 263

def frexp
  fraction, exponent = Numo::NMath.frexp(@data)
  [Vector.new(fraction), Vector.new(exponent)]
end

#head(n = 5) ⇒ Object



376
377
378
379
# File 'lib/rover/vector.rb', line 376

def head(n = 5)
  n += size if n < 0
  first(n)
end

#hypot(y) ⇒ Object



258
259
260
261
# File 'lib/rover/vector.rb', line 258

def hypot(y)
  y = y.to_numo if y.is_a?(Rover::Vector)
  Vector.new(Numo::NMath.hypot(@data, y))
end

#in?(values) ⇒ Boolean

Returns:

  • (Boolean)


147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/rover/vector.rb', line 147

def in?(values)
  ret = Numo::Bit.new(size).fill(false)
  values.each do |v|
    comp =
      if v.is_a?(Numeric) || v.is_a?(Numo::NArray)
        @data.eq(v)
      else
        Numo::Bit.cast(@data.map { |d| d == v })
      end
    ret |= comp
  end
  Vector.new(ret)
end

#include?(value) ⇒ Boolean

Returns:

  • (Boolean)


330
331
332
# File 'lib/rover/vector.rb', line 330

def include?(value)
  to_a.include?(value)
end

#inspectObject Also known as: to_s

TODO add type and size?



401
402
403
404
405
# File 'lib/rover/vector.rb', line 401

def inspect
  elements = first(5).to_a.map(&:inspect)
  elements << "..." if size > 5
  "#<Rover::Vector [#{elements.join(", ")}]>"
end

#last(n = NOT_SET) ⇒ Object



348
349
350
351
352
353
354
355
356
# File 'lib/rover/vector.rb', line 348

def last(n = NOT_SET)
  if n == NOT_SET
    @data[-1]
  elsif n >= size
    Vector.new(@data)
  else
    Vector.new(@data[-n..-1])
  end
end

#ldexp(exponent) ⇒ Object



268
269
270
271
# File 'lib/rover/vector.rb', line 268

def ldexp(exponent)
  exponent = exponent.to_numo if exponent.is_a?(Rover::Vector)
  Vector.new(Numo::NMath.ldexp(@data, exponent))
end

#lnObject



254
255
256
# File 'lib/rover/vector.rb', line 254

def ln
  log
end

#log(base = NOT_SET) ⇒ Object



245
246
247
248
249
250
251
252
# File 'lib/rover/vector.rb', line 245

def log(base = NOT_SET)
  if base == NOT_SET
    Vector.new(Numo::NMath.log(@data))
  else
    type = self.type == :float32 ? :float32 : :float64
    Vector.new(@data.to_a.map { |v| Math.log(v, base) }, type: type)
  end
end

#map(&block) ⇒ Object



182
183
184
185
186
187
# File 'lib/rover/vector.rb', line 182

def map(&block)
  # convert to Ruby first to cast properly
  # https://github.com/ruby-numo/numo-narray/issues/181
  # numo-narray-alt has same behavior
  Vector.new(@data.to_a.map(&block))
end

#map!(&block) ⇒ Object



189
190
191
192
# File 'lib/rover/vector.rb', line 189

def map!(&block)
  @data = cast_data(@data.to_a.map(&block))
  self
end

#maxObject



281
282
283
# File 'lib/rover/vector.rb', line 281

def max
  @data.max
end

#meanObject



289
290
291
# File 'lib/rover/vector.rb', line 289

def mean
  @data.mean
end

#medianObject



293
294
295
296
297
298
# File 'lib/rover/vector.rb', line 293

def median
  # need to cast to get correct result
  # https://github.com/ruby-numo/numo-narray/issues/165
  # numo-narray-alt has same behavior
  @data.cast_to(Numo::DFloat).median
end

#minObject



285
286
287
# File 'lib/rover/vector.rb', line 285

def min
  @data.min
end

#missingObject



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rover/vector.rb', line 71

def missing
  bit =
    if @data.is_a?(Numo::RObject)
      Numo::Bit.cast(@data.map(&:nil?))
    elsif @data.respond_to?(:isnan)
      @data.isnan
    else
      Numo::Bit.new(size).fill(0)
    end

  Vector.new(bit)
end

#numeric?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/rover/vector.rb', line 57

def numeric?
  ![:object, :bool].include?(type)
end

#one_hot(drop: false, prefix: nil) ⇒ Object

Raises:

  • (ArgumentError)


386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/rover/vector.rb', line 386

def one_hot(drop: false, prefix: nil)
  raise ArgumentError, "All elements must be strings" unless all?(String)

  new_vectors = {}
  # maybe sort values first
  values = uniq.to_a
  values.shift if drop
  values.each do |v2|
    # TODO use types
    new_vectors["#{prefix}#{v2}"] = (self == v2).to_numo.cast_to(Numo::Int64)
  end
  DataFrame.new(new_vectors)
end

#percentile(q) ⇒ Object



300
301
302
# File 'lib/rover/vector.rb', line 300

def percentile(q)
  @data.percentile(q)
end

#reject(&block) ⇒ Object



198
199
200
# File 'lib/rover/vector.rb', line 198

def reject(&block)
  Vector.new(@data.to_a.reject(&block))
end

#round(ndigits = 0) ⇒ Object



215
216
217
218
219
220
221
# File 'lib/rover/vector.rb', line 215

def round(ndigits = 0)
  if ndigits == 0
    Vector.new(@data.round)
  else
    Vector.new(@data.map { |v| v.round(ndigits) })
  end
end

#select(&block) ⇒ Object



194
195
196
# File 'lib/rover/vector.rb', line 194

def select(&block)
  Vector.new(@data.to_a.select(&block))
end

#sizeObject Also known as: length, count



61
62
63
# File 'lib/rover/vector.rb', line 61

def size
  @data.size
end

#sortObject



207
208
209
# File 'lib/rover/vector.rb', line 207

def sort
  Vector.new(@data.respond_to?(:sort) ? @data.sort : @data.to_a.sort)
end

#stdObject

uses Bessel's correction for now since that's all Numo supports



309
310
311
# File 'lib/rover/vector.rb', line 309

def std
  @data.cast_to(Numo::DFloat).stddev
end

#sumObject



304
305
306
# File 'lib/rover/vector.rb', line 304

def sum
  @data.sum
end

#tail(n = 5) ⇒ Object



381
382
383
384
# File 'lib/rover/vector.rb', line 381

def tail(n = 5)
  n += size if n < 0
  last(n)
end

#take(n) ⇒ Object

Raises:

  • (ArgumentError)


358
359
360
361
# File 'lib/rover/vector.rb', line 358

def take(n)
  raise ArgumentError, "attempt to take negative size" if n < 0
  first(n)
end

#tallyObject

use Ruby tally for performance



203
204
205
# File 'lib/rover/vector.rb', line 203

def tally
  @data.to_a.tally
end

#to(type) ⇒ Object



38
39
40
# File 'lib/rover/vector.rb', line 38

def to(type)
  dup.to!(type)
end

#to!(type) ⇒ Object



42
43
44
45
# File 'lib/rover/vector.rb', line 42

def to!(type)
  @data = cast_data(@data, type: type)
  self
end

#to_aObject



51
52
53
54
55
# File 'lib/rover/vector.rb', line 51

def to_a
  a = @data.to_a
  a.map! { |v| !v.zero? } if @data.is_a?(Numo::Bit)
  a
end

#to_htmlObject

for IRuby



409
410
411
412
413
414
415
416
417
418
# File 'lib/rover/vector.rb', line 409

def to_html
  require "iruby"

  if size > 7
    # pass 8 rows so maxrows is applied
    IRuby::HTML.table(first(4).to_a + last(4).to_a, maxrows: 7)
  else
    IRuby::HTML.table(to_a)
  end
end

#to_numoObject



47
48
49
# File 'lib/rover/vector.rb', line 47

def to_numo
  @data
end

#typeObject



34
35
36
# File 'lib/rover/vector.rb', line 34

def type
  TYPE_CAST_MAPPING.find { |_, v| @data.is_a?(v) }[0]
end

#uniqObject



67
68
69
# File 'lib/rover/vector.rb', line 67

def uniq
  Vector.new(to_a.uniq)
end

#varObject

uses Bessel's correction for now since that's all Numo supports



314
315
316
# File 'lib/rover/vector.rb', line 314

def var
  @data.cast_to(Numo::DFloat).var
end

#zip(other, &block) ⇒ Object



334
335
336
# File 'lib/rover/vector.rb', line 334

def zip(other, &block)
  to_a.zip(other.to_a, &block)
end