[Work/Class/Python3の基礎とデータ処理/2_PythonLibraries]

NumPyと配列(array)とmatplotlib

NumPy

NumPyは数字(int, float)等の配列をlistとは別の数字専用の配列で扱えるようにしたライブラリである.listとは違いC言語で書かれているため,NumPyのデータ型(数字の配列)numpy.ndarrayとnumpyに用意されている関数群を使えば,listで処理をするよりもはるかに早い計算ができる.

NumPyは配列データ型ndarrayと,それを操作する静的クラス関数で構成されている.ndarrayのインスタンス関数はほぼない(インスタンス変数のshapeを使うぐらい)ので,C言語的な手続き型プログラミングのスタイルになる.

NumPyの配列numpy.ndarray

NumPyでは数字専用の配列numpy.ndarray(n次元配列)クラスが提供されており,そのインスタンスを用いることが前提のNumPyの関数群が用意されている.

ndarrayは一般的にコンストラクタ呼び出しで生成するのではなく,初期化用の静的クラス関数numpy.array(), numpy.empty(), numpy.zeros(), numpy.ones(), numpy.identity()などを使ってインスタンスを生成する.

NumPyの配列はC言語と同じくサイズが固定されている(サイズを動的に弄れない).したがって,インスタンスを生成してくれるる静的関数.array()を呼び出す時に要素を全て入れるか,.empty()関数で形状(n x mの行列である,など)を指定する必要がある.また.empty()でデータタイプ(int, floatなど)も生成時に指定しておくと,実行時に予期せぬエラーが起きることが少なくなる.他に単位行列を作る.identity()関数もある.

import numpy
# 要素を入れてarray関数を呼び出す
my_numpy_array = numpy.array([1, 3, 5])
print(my_numpy_array)

# float型の10要素の空配列(1次元)を生成
my_numpy_array = numpy.empty(10, dtype=float)
print(my_numpy_array) 
# emptyで生成すると,適当な値が入っていることがわかる

# float型0.0の要素で埋められた3x4の2次元配列(行列)を生成
# (3, 4)と中括弧で示すのはtupleの意味
my_numpy_array = numpy.zeros((3, 4), dtype=float)
print(my_numpy_array)

# int型の0の要素で埋められた2x6の行列を生成
my_numpy_array = numpy.zeros((2, 6), dtype=int)
print(my_numpy_array)

# int型の1の要素で埋められた3x4の行列を生成
my_numpy_array = numpy.ones((3, 4), dtype=int)
print(my_numpy_array)

# 4x4のfloatの単位行列を生成
# 単位行列は2次元配列であることが自明なので,
# tupleで(4,4)を与えずに1スカラ値変数だけでいける
my_numpy_array = numpy.identity(4, dtype=float)
print(my_numpy_array)

ndarrayのサイズを取得する

numpyの静的関数ndim()にndarrayのインスタンスを突っ込むと,n次元配列のnがわかる.

my_numpy_array = numpy.identity(4, dtype=float)
print(numpy.ndim(my_numpy_array)) # =>4x4の行列は2次元配列なので,出力は「2」

ndarrayのインスタンスが持つ.shape変数(tuple型)により,そのインスタンスの各次元のサイズを取得することができる.2次元配列の場合2要素のtupleなので,それぞれ[0][1]でアクセスする.

# 3要素の1次元配列を宣言
my_numpy_array = numpy.ones((3), dtype=int) 
print(my_numpy_array.shape) # => (3, )
print(my_numpy_array.shape()[0]) # => 3
print(my_numpy_array.shape()[1]) #1次元配列なので,これはエラーになる

# 5x4要素の2次元配列を宣言
my_numpy_array = numpy.zeros((5, 4), dtype=int)
print(my_numpy_array.shape) # => (5, 4)
print(my_numpy_array.shape[0]) # => 5
print(my_numpy_array.shape[1]) # => 4

3次元配列なら3要素のtupleになるので,[0], [1], [2]でそれぞれアクセスする.

ndarrayに要素を追加する

ndarrayの静的関数としてappend()関数が用意されていて,複数の1次元配列を新たな1次元配列として連結することができる.

前述の通りndarrayのサイズは変更不可なので,append()関数で連結しても,第1引数に入れたインスタンスそのものが拡張されているわけではなく,新しいndarrayのインスタンスを返すだけなので,別の変数に代入する必要がある.

my_numpy_array = numpy.array([1, 3, 5]) # 要素1, 3, 5を持つ1次元配列のインスタンスを作成
new_my_numpy_array = numpy.append(my_numpy_array, numpy.array([7, 9, 11]))
print(new_my_numpy_array) # 連結された新しいndarrayのインスタンス
print(my_numpy_array) # もとのインスタンスは変更されていない

arange()関数

標準listでいう所のrange関数に当たるのが,numpyの静的関数arange()である.使い方は同じ.

my_numpy_array = numpy.array(numpy.arange(10, 15, 0.1))
print(my_numpy_array)

vstackによる1次元配列の積み重ねによる2次元配列の生成

例えば要素数10の配列が二つあるとして,これをもとに2x10の2次元配列(2x10の行列)を作りたい時,

my_numpy_array1 = numpy.array(numpy.arange(10, 15, 0.1))
my_numpy_array2 = numpy.array(numpy.arange(5, 10, 0.1))
print(numpy.vstack((my_numpy_array1, my_numpy_array2)))

とすると,作り出せる.vstack()関数は小括弧が二重であることに注意.(tupleを意味するので)

これまでと同じく,元の配列が拡張されるわけではなく,新たな行列のインスタンスが生成されて返される.

vstack()関数で,要素数が同じ配列なら,どんどん足していく(行列の行が増えていく)ことが可能である.例えば,

my_numpy_array = numpy.array(numpy.arange(1, 10, 0.1))
my_numpy_matrix = numpy.vstack((my_numpy_array, numpy.array(numpy.arange(2, 11, 0.1))))
my_numpy_matrix = numpy.vstack((my_numpy_matrix, numpy.array(numpy.arange(3, 12, 0.1))))
print(my_numpy_matrix) # => 3x90の行列ができている.

という感じである.

配列とスカラ値,もしくは配列同士の四則演算

配列に対してスカラ値(値が一つだけ入っている)変数を四則演算すると,各要素に対してそれが適用される

my_array = numpy.array(numpy.arange(1, 10, 2))
my_array_plus_2 = my_array + 3 # => [4 6 8 10 12]
my_array_minus_2 = my_array - 2 # => [-1 1 3 5 7]

同様に,要素数が同じ配列同士を四則演算すると,要素同士の四則演算となる.(行列演算ではないことに注意)

行列の演算

上記の計算は,配列の各要素に対して適用されるが,行列として掛け算をしたい(行列の積を求めたい)場合に静的関数の.dot()関数を使う.やはり他の変数で受けとらなければならない.

my_matrix1 = numpy.ones((4, 4), dtype=float)
my_matrix2 = numpy.identity(4, dtype=float)
my_matrix3 = numpy.dot(my_matrix1, my_matrix2)

同様にdot()関数を使うことで,ベクトル(つまり1次元配列)と行列(つまり2次元配列)の積も求めることができる.(これは2次元CGや3次元CGの座標変換などでよく使われる)

my_vector = numpy.array([1, 3.2, 5.6])
my_matrix = numpy.array([[4.2, 3.6, 2.5], [3.4, 8.9, 0.4], [0.2, 1.1, 0.5]])
print(numpy.dot(my_matrix, my_vector))

他の行列演算は,numpyのさらに中に定義されているクラスlinalgの静的関数を使う.こちらも静的関数である.

my_matrix = numpy.ones(4, dtype=float)
my_transposed_matrix = numpy.linalg.transpose(my_matrix) # 転置行列
my_inversed_matrix = numpy.linalg.inv(my_matrix) # 逆行列
my_eigenvalue = numpy.linalg.eig(my_matrix) # 行列の固有値

matplotlib

簡単に言えばグラフを書くためのライブラリである.バックエンドのGUIライブラリに何を使っているかは環境によって違うが,AnacondaやMSYS2のmingw64でインストールされるPython3用のmatplotlibでは,C++用のマルチプラットフォームGUIライブラリQtをPythonから使うための「PyQt」Ver.5が用いられている.

2次元のグラフを書くための手順を簡単に言えば,「X軸とY軸のnumpy配列を作り,plot()関数で値の配列をセットし,show()関数で表示する.」だけである.

# -*- coding: utf-8 -*-
import numpy
import matplotlib.pyplot

if __name__ == '__main__':
    # x座標用の配列を作る
    # 0から9.99までの1000要素
    x_axis_array = numpy.array(numpy.arange(0, 10.0, 0.01))
    # x座標用と同じ要素数のゼロ配列を作る
    y_axis_array = numpy.zeros_like(x_axis_array)

    # numpy.ndenumerateでくくると,indexも同時に取り出せるようになる
    for index, x_value in numpy.ndenumerate(x_axis_array):
        # f(x) = -2x^3 + 8x^2 + 5
        # を作って格納する.
        y_axis_array[index] = -2 * numpy.power(x_value, 3) + 8 * numpy.power(x_value, 2) + 5

    # X軸とY軸をのデータをセットして描画する
    # 折れ線グラフ,棒グラフ,点をデータ数分描画など,描画の仕方はオプションで指定する.
    # なにも指定しないと,自動的に適した形状で描画される.
    # 色指定をする.
    matplotlib.pyplot.plot(x_axis_array, y_axis_array, color='blue')
    # 描画したウィンドウを見せる
    matplotlib.pyplot.show()

import as

ここまで書いてくると,numpyだけならともかく,numpy.linalgクラスやmatplotlib.pyplotクラス内の静的関数を書くのが(文字数的に)面倒臭くなってくる.そこで,

import numpy as np
from numpy import linalg as la # numpyの中のlinalgクラスをimport……という意味
from matplotlib import pyplot as plt

asキーワードを使ってimportすると,その名前(この場合はnpla, plt)でそのクラスにアクセスできるようになる.

一般的にnumpyは「np」という名前で,linalglaという名前で,matplotlib.pyplotpltという名前でimportされることが多いので,慣習に従うべきである.