PyCallをインストールしてRubyでNumpyを使う

math

A.I.や機械学習、深層学習(Deep learning)、データサイエンスといった分野では圧倒的にPythonのシェアが高いです。Numpy、Pandas、scikit-learn等のライブラリが充実している為、Pythonでは機械学習コードを書く環境が整っています。

一方、Rubyは・・・というとデータサイエンス用のライブラリは開発されているものの、データサイエンス分野で存在感はほぼ無いのが現状です。Pythonほどの情報量もないので、これらのライブラリを使いこなすのは中々難しいかもしれません。

そこで、データサイエンス分野でもRubyを使いたいという人は、PyCallと呼ばれる、Ruby-Python ブリッジライブラリを使用するのがおススメです。PyCallを使えばRubyからPythonのライブラリを呼ぶことが可能になります。すなわち、RubyでNumpyやPandas等が使用することが可能なります。Ra

今回は、PyCallインストールして、データサイエンス分野では必須と言っても良い、数値計算ライブラリNumpyをRubyから使用する方法を説明します。

スポンサーリンク

PyCallをインストール

まずは、Python のライブラリを Rubyで利用する為のライブラリPyCallをインストールします。gemでインストールします。

gem install pycall

これで、RubyからPythonライブラリを呼べるようになりました。あとは、PyCallのお作法に従えば、RubyでNumpyが使えるようになります。

#pycall 
require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: "np"

arr = np.asarray([1,2,3])
#=> array([1, 2, 3])

puts arr.class
#=> <class 'numpy.ndarray'>

ちなみに、Numpyだけを利用したい場合はNumpyのみのインストールも可能です。但し、この場合、Numpyを呼び出す為の記述方法が少々変わります。

gem install numpy
require "numpy"
Numpy.array([1,2,3])
#=> array([1,2,3])
スポンサーリンク

Numpy配列の操作方法

Numpy配列の作り方はPythonで行うときと一緒です。sizeやshape等の属性(attributes)へのアクセス、Numpy配列を操作する関数(argmaxやreshape等)や、Numpyの数学関数(sumやmedian等)も問題なく使えます。

#pycall 
require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: "np"

arr = np.asarray([1,2,3])
#=> array([1, 2, 3])

arry = np.array([1,2,3])
#=> array([1, 2, 3])

puts arr.class
#=> <class 'numpy.ndarray'>

puts arr.shape
#=>(3,)

puts arr * 2
#=> [2, 4, 6]

puts arr.argmax()
#=> 2

puts arr.size
#=> 3
 
puts np.sum(arr)
#=> 6
スポンサーリンク

RubyのメソッドでNumpy配列を操作

Rubyユーザならば、配列を扱う時はeachやmapを等のRubyのメソッドを使用したくなります。Numpy配列を操作する時もeachやmapが使えると便利です。

しかし、当然ですが、Numpyにはこれらのメソッドは存在しません。Numpy配列にeachやmap等を用いようとすると、NoMethodErrorが出ます。Numpy配列で使えるメソッド一覧を見ても、確かにeach、map、for等はありません。

#pycall 
require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: "np"

a = [1,2,3]
na = np.array(a)
na.each {|i| puts i}
#=> NoMethodError: undefined method `each ' for array([1, 2, 3]):Numpy::NDArray

puts na.methods
#=> [:*, :__mul__, :argmax, :[], :<, :>, :<=, :>=, :==, :!=, :[]=, :inspect, :method_missing, :to_s, :to_i, :call, :to_f, :dup, :kind_of?, :coerce, :__pyptr__, 
# :pyimport, :pyfrom, :to_json, :remove_instance_variable, :instance_of?, :is_a?, :tap, :public_send, :public_method, :singleton_method, :instance_variable_defined?, 
# :define_singleton_method, :method, :instance_variable_set, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :object_id, :send, :display, 
# :nil?, :hash, :class, :singleton_class, :clone, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, 
# :private_methods, :public_methods, :instance_variable_get, :instance_variables, :!, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
スポンサーリンク

RubyのメソッドでNumpy配列を操作する方

上のNumpyのメソッドを見ると、配列要素操作するメソッドが見当たりません。Numpy配列を作成した後、配列の要素を操作したくなることはあるはずです

そのような場合は、下記の方法で配列の要素を操作することが出来ます。

方法1: インデックスを使ってアクセス

#pycall 
require 'pycall/import'
include PyCall::Import
pyimport 'numpy', as: "np"

a = [1,2,3]
arr = np.array(a)

#配列サイズを取得。sizeはRubyのメソッドでなく、Numpyの関数
len_na = arr.size
(0...len_na).each do |i|
  na[i] = na[i] * 2
end

puts na
#=> [16 32 48]

方法2: Numpy::NDArrayをオーバライドして自分でメソッドを定義

Rubyでコードを書くならば、 Numpy配列の要素にもRubyっぽくアクセスできた方が便利です。Rubyで配列操作する時はやはり、eachやmapを使用したくなります。

このよう場合は、Numpyクラスをオーバーライドしてしまいます。ここでは、ブロックを利用するメソッドの定義の仕方、すなわち、yieldを使います。

require 'pycall/import'
include PyCall::Import

pyimport 'matplotlib'
pyimport 'numpy', as: "np"
require "numpy"

class Numpy::NDArray
  def each
    len = self.size
    (0...len).each do |i|
        yield self[i]
    end 
    self
  end 
  
  def map!
    len = self.size
    (0...len).each do |i|
      self[i] = yield(self[i])
    end 
  end 
  
  def map
    len = self.size
    ret = []
    (0...len).each do |i|
      ret << yield(self[i])
    end 
    Numpy.array(ret)
  end 
  
  def each_with_index
     len = self.size
    (0...len).each do |i|
        yield(i, self[i])
    end 
    self
  end 
  
  def to_a
    len = self.size
    ret = []
    (0...len).each do |i|
      ret << self[i]
    end 
    ret
  end 

end 


na = np.array([1,2,3,4,5])
puts na.to_a.class
#=>Array

na1 = na.map do |i|
  i * 2
end 
puts na1
#=> [2,4,6,8,10]
puts na1.class
#=> <class 'numpy.ndarray'>

na.map! do |i|
  i * 3
end 
puts na
#=> [1,6,9,12,15]
puts na.class
#=> <class 'numpy.ndarray'>
スポンサーリンク

まとめ

RubyでNumpyを使用する為にはPyCallをインストールします。基本的な使い方はPythonと一緒です。

Numpy配列には、Rubyの配列のようなeachやmap等メソッドは定義されていないため、Rubyの配列のようにアクセスする為には、以下の方法を使用します。
①インデックスを使ってアクセスする
②Numpy::NDArrayをオーバライドして自分でメソッドを定義する

タイトルとURLをコピーしました