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をオーバライドして自分でメソッドを定義する