Pythonやnumpyの経験がない皆さんは、このチュートリアルによって、Pythonプログラミング言語と科学計算のためのPythonの両方を学んでください。
なお、Matlabの知識がある場合にはMatlabのユーザーのためのnumpyのページを参照することをおすすめします。
このチュートリアルで扱うのは以下の項目です:
Pythonは動的に型付けされたマルチパラダイムのプログラミング言語です。Pythonコードは、複雑なアイデアでもごくわずかなコードを書くだけで表現することができ、しかも非常に読みやすいので、しばしば擬似コードに似ていると言われています。例として、古典的なクイックソートアルゴリズムをPythonで実装します:
# #以降はPythonのコメントです
# クイックソートのアルゴリズムの概略:
# (1)リストの要素数が1以下ならそのまま返して終了
# (2)リストの適当な要素をpivotとする
# (3)pivotよりも小さい要素を集めleftとする
# (4)pivotに等しい要素を集めmiddleとする
# (5)pivotよりも大きい要素を集めrightとする
# (6)(再帰を用いて)leftをソートしたもの、middle、rightをソートしたものをこの順につなげて終了
# 以下はリストの内容をクイックソートする関数quicksortの定義(definition)
def quicksort(arr): # :でブロック(この場合はquicksort関数の本体)が始まる
# 同じブロック内のコードはすべて同じ数のスペースでインデントされていないといけない
if len(arr) <= 1: # (1) --- 新たな:により if 文のブロックが始まる
return arr # ここはif文のブロックなので、他のコードよりもインデントが深い
pivot = arr[len(arr) // 2] # (2)
left = [x for x in arr if x < pivot] # (3)
middle = [x for x in arr if x == pivot] # (4)
right = [x for x in arr if x > pivot] # (5)
return quicksort(left) + middle + quicksort(right) # (6)
# 関数の定義の終わりには空行を入れる
# 次はquicksort関数の実行例
print( quicksort([3,6,8,10,1,2,1]) )
現在サポートされているPythonのバージョンは2.7と3.4の2種類です。やや混乱することに、Python 3.0は今までのバージョンとは互換性のない多くの変更を言語に導入したため、2.7で書かれたコードは3.4で動作しないことがあります。このクラスでは、すべてのコードがPython 3.4(以降)を使用します。
コマンドラインであなたのPythonのバージョンを確認することができます
python --version
もっとも次のほうがちょっと簡単
python -V
ほとんどの言語と同じく、Pythonには、整数、浮動小数点数、ブール値、文字列などの多くの基本のデータ型があります。これらのデータ型は、他のプログラミング言語と同じ様に動きます。
x = 3
print( x, type(x) ) # xの値とデータ型の表示
print( x + 1 ) # Addition; 足し算
print( x - 1 ) # Subtraction; 引き算
print( x * 2 ) # Multiplication; 掛け算
print( x ** 2 ) # Exponentiation; 乗数
x += 1
print( x ) # x = x+1 と同じ
x *= 2
print( x ) # x = x*2 と同じ
x -= 3 x **= 2 x %= 7 x //= 3 x /= 3 は何と同じであろうか、類推してみよう。また実際に確かめてみよう。
# 確かめてみよう
y = 2.5
print( type(y) ) # "<type 'float'>" と出力
print( y, y + 1, y * 2, y ** 2 ) # "2.5 3.5 5.0 6.25"と出力
C言語とは異なり、Pythonには単項インクリメント(x++
)やデクリメント(x--
)演算子がないことに注意しよう。</p>
Pythonでは、長い整数と複素数を組み込みで扱うことができます。これについての詳細は ドキュメントをご覧ください。
Pythonはブール論理の通常の演算子のすべてを実装していますが、&&
、||
のような記号ではなく、and
やor
というように英語の単語を使用します
t, f = True, False
print( type(t) ) # Prints "<type 'bool'>"
論理演算をすべて使ってみましょう
print( t and f ) # AND : 論理積
print( t or f ) # OR : 論理和
print( not t ) # NOT : 否定
print( t != f ) # XOR : 排他的論理和
Pythonには文字列に対する素晴らしいサポートがあります</P>
hello = 'hello' # 文字列を示すには、一重引用符でも
world = "world" # 二重引用符でも、どちらも使える
print( hello, len(hello), type(world) )
hw = hello + ' ' + world # 文字列の結合
print( hw ) # "hello world"と出力
hw12 = '%s %s %d' % (hello, world, 12) # sprintf 流の文字列生成
print( hw12 ) # "hello world 12"と出力
Stringオブジェクトには便利なメソッドがたくさんあります。次はその例:
s = "hello"
print (s.capitalize() ) # 文字列をキャピタライズ(先頭のみ大文字); "Hello"と出力
print (s.upper() ) # 文字列を大文字に変換; "HELLO"と出力
print (s.rjust(9) ) # 文字列を右揃え(左にスペースを入れる); " hello"と出力
print (s.center(9) ) # 文字列を中央寄せ(左右にスペースを入れる); " hello "と出力
print (s.ljust(9) ) # 文字列を左揃え(右にスペースを入れる); "hello "と出力
print (s.replace('l', '(ell)') ) # すべてのlを(ell)で置き換える; "he(ell)(ell)o"と出力
print ('\t world \n'.strip() ) # 先頭と末尾の空白文字を切り落とす; "world"と出力
C言語を知っている人には意外に思うかもしれませんが、文字列内容の比較も演算子を用いて行えます:
str = 'Jane'
print(str =='Jane')
print('abc' < 'def')
print('def' >= 'abc')
すべての文字列メソッドのリストについて知りたければドキュメントをご覧ください。</p>
xs = [3, 1, 2] # リストを作る
print (xs, xs[2])
print (xs[-1] ) # 負のインデックスは後ろから数えることを意味する。だから "2" を表示する
xs[2] = 'foo' # rリストはいろいろな型の要素をもつことができる
print (xs)
xs.append('bar') # リストの最後に新しい要素を付け加える
print (xs)
print(xs+[0,'baz']) # リスト同士を結合
print(xs) # とはいっても元のxsには変更なし
x = xs.pop() # リストの最後から要素を取り除き、その要素を返す
print (x, xs)
今までと同様に、リストについての詳細は ドキュメント でみることができます。</P>
リストの要素一つ一つにアクセスするのに加えて、Pythonはサブリスト(リストの一部分)にアクセスする手段を提供しています。これはスライスとして知られてます。
nums = list(range(5)) # range で指定された範囲の整数からなるリストをlistで作る。どちらも組み込みの関数
print (nums ) # "[0, 1, 2, 3, 4]"と出力
print (nums[2:4]) # 2 番目から 4 番目までの要素(4番目の要素は含まない)からなるスライス: "[2, 3]"と出力
print (nums[2:]) # 2番目 から最後の要素からなるスライス; "[2, 3, 4]"と出力
print (nums[:2]) # 先頭から2番目までの要素(2番目の要素は含まない) からなるスライス: "[0, 1]"と出力
print (nums[:]) # 全部の要素からなるスライス: ["0, 1, 2, 3, 4]"と出力
print (nums[:-1] ) # スライスのインデックス指定にも負の数が使える: ["0, 1, 2, 3]"と出力
nums[2:4] = [8, 9] # 新しい要素で num[2:4]のスライスの部分を置換
print (nums) # "[0, 1, 8, 9, 4]"と出力
このようなリストの要素を繰り返し(ループ)で使用することができます。次のfor文では、繰り返しごとにanimaslリストの一つ一つの要素がanimalの値となり、print文が実行されます。このようにfor文の基本構造は次のとおりです:
for 変数 in リスト(コンテナ): 繰り返されるコード
animals = ['cat', 'dog', 'monkey']
for animal in animals:
print (animal)
ループの本体で要素のインデックスにアクセスしたい場合は、組み込みのenumerate
機能を使用します:</p>
animals = ['cat', 'dog', 'monkey']
for idx, animal in enumerate(animals):
print( '#%d: %s' % (idx + 1, animal) )
プログラミングではよく、データ変換したくなることがあります。その簡単な例として、次のような二乗を計算するコードを考えてみましょう。</p>
nums = [0, 1, 2, 3, 4]
squares = []
for x in nums:
squares.append(x ** 2)
print(squares)
このようなコードは、リストの内包記法を使って簡単に書くことができます:</p>
nums = [0, 1, 2, 3, 4]
squares = [x ** 2 for x in nums]
print (squares)
このようにリスト内包記法の基本形は以下です:
[ 式 for 変数 in リスト(コンテナ)]これによってリストの要素それぞれが変数の値となり、その変数の値を使って「式」が計算され、その結果すべてを集めたリストが結果となります
リスト内包表記には条件も含めることができます:
[ 式 for 変数 in リスト(コンテナ) if 条件]
nums = [0, 1, 2, 3, 4]
even_squares = [x ** 2 for x in nums if x % 2 == 0]
print (even_squares)
なお、繰り返しに「多重繰り返し」があるように、リスト内包記法にも次の例のように繰り返しの部分を多重にすることができます:
print(['%s loves %s'%(x,y) for x in ['Mary','Jane'] for y in ['Tom', 'John', 'Fred']])
辞書とは
JavaのMap
やJavaScriptのオブジェクトと同じく、
(キー、値)というペアを記録するものです。これは次のように使用できます:
d = {'cat': 'cute', 'dog': 'furry'} # いくつかのデータを持つ辞書を作る
print (d['cat']) # 辞書から項目を取り出す; "cute"と出力する
print ('cat' in d) # 辞書に指定されたキーがあるかどうか調べる; "True"と出力する
d['fish'] = 'wet' # 辞書に新たな項目を記憶させる
print (d['fish'] ) # "wet"と出力する
print (d['monkey']) # KeyError: 'monkey'は辞書dの項目にはないためエラーになる
print (d.get('monkey', 'N/A')) # default値をつけて項目にアクセス; "N/A"と出力する
print (d.get('fish', 'N/A')) # default値をつけて項目にアクセス; "wet"と出力する
del d['fish'] # 辞書dから項目を削除
print (d.get('fish', 'N/A')) # "fish" はもはやや辞書dには登録されていない: "N/A"と出力する
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal in d:
legs = d[animal]
print ('A %s has %d legs' % (animal, legs))
キーとそれに対応する値にいっぺんにアクセスしたい場合は、items
メソッドを使います:</p>
d = {'person': 2, 'cat': 4, 'spider': 8}
for (animal, legs) in d.items():
print ('A %s has %d legs' % (animal, legs))
リスト内包に似た方法で、簡単に辞書を構築することができます。その例:</p>
nums = [0, 1, 2, 3, 4] # 同じことは nums=range(5) としてもできます
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print (even_num_to_square)
辞書について、詳細は このドキュメント でみることができます。
集合とは、異なる要素の順序付けられていない集まりのことです。例として、次を考えてみましょう。
animals = {'cat', 'dog'}
print ('cat' in animals ) # 集合にある要素が含まれているかどうかチェックする; "True"と出力する
print ('fish' in animals) # "False"と出力する
animals.add('fish') # 集合に要素を付け加える
print ('fish' in animals)
print (len(animals)) # 集合の要素数を返す
animals.add('cat') # 集合にすでに要素がある場合にはなにもしない
print (len(animals) )
animals.remove('cat') # 集合から要素を取り除く
print (len(animals) )
集合の繰り返し処理は、リストの反復処理と同じ構文で行います。ただし、集合の順序は順序付けされていないため、集合の要素を参照する順番を前提にすることはできません。</p>
animals = {'cat', 'dog', 'fish'}
for idx, animal in enumerate(animals):
print ('#%d: %s' % (idx + 1, animal))
# "#1: fish", "#2: dog", "#3: cat"と出力する
リストや辞書のように、集合の内包表記を使用して集合を作ることができます。</p>
from math import sqrt
print ({int(sqrt(x)) for x in range(30)})
いつものように、集合についての詳細は このドキュメントをみてください。
d = {(x, x + 1): x for x in range(10)} # タプルをキーとする辞書を作る. range(10)はリスト[0,1,..,9]に相当する
print (d[(1, 2)])
t = (5, 6) # タプルを作る
print (type(t))
print (d[t] )
t[0] = 1 # タプルに新たな要素を付け加えたり削除したりはできません
このドキュメントに、タプルに関する詳細な情報があります。
def sign(x):
if x > 0:
return 'positive'
elif x < 0:
return 'negative'
else:
return 'zero'
# 実行例
for x in [-1, 0, 1]:
print (sign(x))
次のように、オプションのキーワード引数を取る関数を定義することがよくあります:
def hello(name, loud=False):
if loud:
print ('HELLO, %s' % name.upper())
else:
print ('Hello, %s!' % name)
hello('Bob')
hello('Fred', loud=True)
Pythonの関数について詳しくは このドキュメントを参照してください。
class Greeter:
# Constructor
def __init__(self, name):
self.name = name # インスタンス変数を作成する
# Instance method
def greet(self, loud=False):
if loud:
print ('HELLO, %s!' % self.name.upper())
else:
print ('Hello, %s' % self.name)
g = Greeter('Fred') # Greeter クラスのインスタンスの作成
g.greet() # インスタンスメソッドを呼び出す; "Hello, Fred"と出力
g.greet(loud=True) # インスタンスメソッドを呼び出す; "HELLO, FRED!"と出力
Pythonのクラスについて このドキュメントにより、 詳しく知ることができます。
numpy(「ナンパイ」と読む)とは、Pythonで科学技術計算のための中核となるライブラリのことです。これは、高性能多次元配列オブジェクト、およびこれらの配列を処理するためのツールを提供しています。既にMATLABに精通している人なら、 Matlabユーザーのための NumPyyという文書から有用な情報が得られるでしょう。</P>
Numpyを使うには、まず次のようにしてnumpyパッケージをimport(取り込む)必要があります:
import numpy as np
a = np.array([1, 2, 3]) # 階数1(1次元)の配列を作る
print (type(a), a.shape, a[0], a[1], a[2])
a[0] = 5 # 配列の要素を更新する
print (a)
b = np.array([[1,2,3],[4,5,6]]) # 階数2 (2次元)の配列を作る
print(b)
print (b.shape ) # 配列bの形状を出力
print (b[0, 0], b[0, 1], b[1, 0])
Numpyには、配列を作成するための関数がたくさんあります:
a = np.zeros((2,2)) # すべての要素が0である配列を作る
print( a )
b = np.ones((1,2)) # すべての要素が1である配列を作る
print (b)
c = np.full((2,2), 7) # 定数配列を作る
print (c)
d = np.eye(2) # 2x2 の単位行列を作る
print (d)
e = np.random.random((2,2)) # 配列を作り、乱数で要素を埋める
print (e)
配列の別な作成方法については このドキュメントを参照してください。
Numpyは、配列にインデックスを付けるいくつかの方法を提供しています。
Pythonのリストと同様に、numpyの配列をスライスすることができます。配列は多次元であるため、配列の各次元にスライスを指定する必要があります。</p>
import numpy as np
# 次のような階数2で形状(3,4)の配列を作る
# [[ 1 2 3 4]
# [ 5 6 7 8]
# [ 9 10 11 12]]
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
# スライスを用いて最初の2行、1列目と2列目の要素からなる部分配列を抜き出す
# b はそのような形状(2,2)の配列
# [[2 3]
# [6 7]]
b = a[:2, 1:3]
print (b)
配列のスライスは同じデータに対して違った見方を与えるものなので、スライスの要素を書き換えると、元の配列の要素も書き換わってしまいます。
print (a[0, 1] )
b[0, 0] = 77 # b[0, 0] は a[0, 1]と同じデータ
print ( a[0, 1] ) # だからbの要素を書き換えるとaも変わる
整数インデックスとスライスインデックスを混在させることもできます。しかし、これを行うと元の配列よりも低い階数の配列が得られます。これは、MATLABが配列スライシングを処理する方法とはまったく異なります。
# 次のような階数 2 の形状 (3, 4)の配列を作る
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
print (a)
配列の真ん中の行にあるデータにアクセスする2通リの方法を見ます。整数インデックスとスライスを混在させることで低い階数の配列ができます。一方、スライスだけを使うと、元の配列と同じ階数の配列ができます。
row_r1 = a[1, :] # 配列aの2行目に対する階数 1 のスライス
row_r2 = a[1:2, :] # 配列aの2行目に対する階数 2 のスライス
row_r3 = a[[1], :] # 配列aの2行目に対する階数 2のスライス
print (row_r1, row_r1.shape )
print (row_r2, row_r2.shape)
print (row_r3, row_r3.shape)
# 配列の列に対しても同じことが起こります
col_r1 = a[:, 1]
col_r2 = a[:, 1:2]
print (col_r1, col_r1.shape)
print()
print (col_r2, col_r2.shape)
スライスを使用してnumpyの配列にインデックスした結果は、常に元の配列の部分配列になります。これと対照的に、整数配列インデックスは、別の配列のデータを使用して任意の配列を構築することを可能にします。次に例を示します。</p>
a = np.array([[1,2], [3, 4], [5, 6]])
# 整数配列インデックスの例
# 返される配列は形状(3, )
print (a[[0, 1, 2], [0, 1, 0]] )
# 上の例は次のものと等価
print ( np.array([a[0, 0], a[1, 1], a[2, 0]]) )
# When using integer array indexing, you can reuse the same
# element from the source array:
print (a[[0, 0], [1, 1]])
# Equivalent to the previous integer array indexing example
print (np.array([a[0, 1], a[0, 1]]))
整数配列のインデックスが便利なのは、行列の各行から1つの要素を選択したり変更したりする場合です。
# 要素を選んで配列を作る
a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
print (a)
# インデックスの配列を作る
b = np.array([0, 2, 0, 1])
# bのインデックスを用いてそれぞれの行からひとつずつ要素を選ぶ
print (a[np.arange(4), b] ) # "[ 1 6 7 11]"と出力
# bのインデックスを用いてそえぞれの行からひとつずつ要素の値を変化させる
a[np.arange(4), b] += 10
print (a)
ブール配列インデックスを使用すると、配列の任意の要素を取り出すことができます。このタイプの索引付けは、ある条件を満たす配列の要素を選択するために頻繁に使用されます。次に例を示します。</p>
import numpy as np
a = np.array([[1,2], [3, 4], [5, 6]])
bool_idx = (a > 2) # 2よりも大きな要素を見つける;
# これによりaと同じ形状のブール値のNumpy配列が返る
# bool_idx のそれぞれの要素は
# 対応するaの要素が2より大きいかどうかの情報を与える
print (bool_idx)
# ブール配列インデックスbool_idxを用いて階数1の配列を作る
# bool_idxの要素がTrueである要素に対応するaの要素からなる
print (a[bool_idx])
# 以上の事を一行で完結に書くことができる:
print (a[a > 2])
話を簡潔にするため、numpy配列のインデキシングに関する詳細は省略しました。もっと知りたい場合は このドキュメントを読みましょう。
numpy配列はすべて、同じ型の要素から構成されます。Numpyは、配列を構築するために使用できる多数の数値データ型を提供しています。Numpyは配列を作成するときにデータ型を推測しようとしますが、配列を構築する関数は通常、明示的にデータ型を指定するオプションの引数も含みます。次に例を示します。
x = np.array([1, 2]) # Let numpy choose the datatype
y = np.array([1.0, 2.0]) # Let numpy choose the datatype
z = np.array([1, 2], dtype=np.int64) # Force a particular datatype
print (x.dtype, y.dtype, z.dtype)
numpyのデータ型について詳しく知りたい場合は、 このドキュメントのを読むことをお勧めします。
基本的な数学関数は配列の要素ごとに作用し、演算子オーバーロードとnumpyモジュールの関数としても利用できます。
x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)
# 要素ごとの和;両方とも配列を返す
print (x + y)
print (np.add(x, y))
# 要素ごとの差; どちらも配列を返す
print (x - y)
print (np.subtract(x, y))
# 要素ごとの積; どちらも配列を返す
print (x * y)
print (np.multiply(x, y))
# 要素ごとの商; どちらも配列を返す
# [[ 0.2 0.33333333]
# [ 0.42857143 0.5 ]]
print (x / y)
print (np.divide(x, y))
# 要素ごとの平方根; 配列を返す
# [[ 1. 1.41421356]
# [ 1.73205081 2. ]]
print (np.sqrt(x))
*
はMATLABとは異なり、行列乗算ではなく、要素単位の乗算です。我々は、dot
関数を使って、行列の積を計算したり、行列とベクトルの積を求めたり、ベクトルの内積を計算します。dot
は、numpyモジュールの関数としても、配列オブジェクトのインスタンス・メソッドとしても利用できます。
x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])
v = np.array([9,10])
w = np.array([11, 12])
# Inner product of vectors; both produce 219
print (v.dot(w))
print (np.dot(v, w))
# Matrix / vector product; both produce the rank 1 array [29 67]
print (x.dot(v))
print (np.dot(x, v))
# Matrix / matrix product; both produce the rank 2 array
# [[19 22]
# [43 50]]
print (x.dot(y))
print (np.dot(x, y))
Numpyは、配列の計算を実行するための多くの便利な関数を提供しています。sum
は最も有用な関数の一つです。
x = np.array([[1,2],[3,4]])
print (np.sum(x)) # Compute sum of all elements; prints "10"
print (np.sum(x, axis=0)) # Compute sum of each column; prints "[4 6]"
print (np.sum(x, axis=1)) # Compute sum of each row; prints "[3 7]"
numpyが提供する数学関数の完全なリストは このドキュメントにあります。
配列を使用して数学関数を計算することとは別に、配列内のデータを再形成または操作する必要があることがよくあります。このタイプの操作の最も単純な例は行列を転置することですが、
これには単に配列オブジェクトのT
属性を使えば良いのです:
print (x)
print (x.T)
v = np.array([[1,2,3]])
print ( v )
print (v.T)
# ベクトルvを行列xの各行に加算する
# その結果を行列yとして記憶する
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = np.empty_like(x) # xと同じサイズの空の行列を作る
# ループを用いてvを行列xの各行に加える
for i in range(4):
y[i, :] = x[i, :] + v
print( y )
これはこれで動く。しかし、行列 x
がかなり大きい場合、
Python でこのような繰り返しをやるととても遅くなる。ここで、ベクトル
v
を行列
x
の各行に足すというのは、
v
のコピーを垂直にたくさん積み上げた行列
vv
を作り、それから行列
x
と行列vv
を要素ごとに足すことに等しい。そこで、この操作を次のように実現することができる:
vv = np.tile(v, (4, 1)) # vを4つ積み上げる
print (vv) # 出力は: "[[1 0 1]
# [1 0 1]
# [1 0 1]
# [1 0 1]]"
y = x + vv # xとvvを要素ごとに加算する
print (y)
Numpyのブロードキャストにより、 このような計算を、実際にはv
のコピーを作らずにすませることができる。. このような場合にブロードキャストを考えよう:
import numpy as np
# ベクトルvを行列xの各行に加え
# その結果を行列yで記憶する
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = x + v # ブロードキャストを使ってvをxのそれぞれの行に足す
print (y)
y = x + v
は、 たとえx
が
(4, 3)
という形状であり、 v
が(3,)
という形状であっても、ブロードキャストのおかげで計算できます。
あたかもv
が実際に(4, 3)
という
形状をもっていて、各行がみな
v
のコピーであるかのように、和が要素ごとに計算されるのです。
2つの配列に対するブロードキャストは次の規則に従います:
もしもこの説明で意味がわからなければ、 この解説か、 この解説nを読んでみよう。
ブロードキャストができる関数は 汎化関数と呼ばれている。どんなものがそうかは、 このドキュメント を参照すること.
以下に、ブロードキャストの使用例をいくつか示す:
import numpy as np
# ベクトルの外積計算
v = np.array([1,2,3]) # v の形状 (3,)
w = np.array([4,5]) # w の形状 (2,)
# 外積を計算するには、まずvの形状を変えて、
# 形状 (3, 1)の列ベクトルにする; 次にこれをwに対してブロードキャストし、
# 形状 (3, 2)の出力を得る。これが v と wの外積である
# [[ 4 5]
# [ 8 10]
# [12 15]]
print (np.reshape(v, (3, 1)) * w)
# ベクトルを行列のそれぞれの「列」に加える
x = np.array([[1,2,3], [4,5,6]])
# x は形状 (2, 3) で v は形状 (3,) であるから、それらをブロードキャストし
# 次のような形状 (2, 3)の行列を得る
# [[2 4 6]
# [5 7 9]]
print (x + v)
# ベクトルを行列の各「行」に加える
# 行列x は形状 (2, 3) で、 w は形状 (2,)である。
# 行列 x を転置すると形状(3, 2) となり、これをwに対しブロードキャストすることで
# 形状(3, 2)の行列を得る;この結果を転置することで
# 形状 (2, 3)の行列を最終結果として得る。これは行列xの各列に
# ベクトル w を加えたものになっており、結果は:
# [[ 5 6 7]
# [ 9 10 11]]
print ( (x.T + w).T )
# 別解: wの形状を形状 (2, 1)のベクトルに変えると
# これを行列 x にブロードキャストして、同じ結果を得る
print (x + np.reshape(w, (2, 1)))
# 行列の定数倍:
# x は形状(2, 3) の行列とする. Numpy ではスカラーは形状 ()のベクトル扱いである
# これらは一緒にブロードキャストされ、形状(2, 3)の次の行列を得る:
# [[ 2 4 6]
# [ 8 10 12]]
print (x * 2)
ブロードキャストを使うと、一般にコードが簡潔でしかも処理速度が速くなります。 ですから、可能な限りこれを使うよう、努めてください。
この短い文書では、知らなければならない重要なことのホンのさわりしか紹介できていないので、 numpyの文献 を読んで、Numpyについて、よりよく知る努力をしてください。
Numpyは高性能、多次元の配列を提供し、かつその配列の基本ツールや処理機能を提供している。 SciPyはNumpyをベースに、 その配列を用いた多くの関数や、いろいろな科学技術応用を提供している。
SciPyをよりよく知るには このドキュメントを見てほしい。 ここではSciPyについて役に立つほんの一部について紹介する。
SciPyは画像を扱う基本関数を提供している。たとえば、 ディスクから画像を読み込みNumpyの配列に取り込んだり、その配列を画像としてディスクに書き込んだり、 画像のサイズを変えたりする関数がある。ここではそのような関数のいくつかについてお披露目しよう。
from scipy.misc import imread, imsave, imresize
# JPEG 画像ファイルを読み込み、numpy の配列として記憶する
img = imread('assets/cat.jpg')
print( img.dtype, img.shape ) # "uint8 (400, 248, 3)" と出力する
# 色のチャネルごとにいろいろな定数を掛けてスケーリングすることで
# 画像に色合いをつけることができる。この画像の形状は(400, 248, 3)である
# これに形状(3,) の定数ベクトル[1, 0.95, 0.9] をNumpyのブロードキャスト
# する。つまり赤色(第1チャネル)は変化させないが、緑(第2チャネル)と青
# (第3チャネル)の大きさをそれぞれ0.95倍、および0.9倍する
img_tinted = img * [1, 0.95, 0.9]
# 結果の画像を 300 x 300 ピクセルになるようサイズを変更する
img_tinted = imresize(img_tinted, (300, 300))
# この画像をディスクに書き込む
imsave('assets/cat_tinted.jpg', img_tinted)
左: 原画像、 | 右: 色付けし、サイズ変更した画像 |
scipy.io.loadmat
とscipy.io.savemat
関数を使うと、 MATLABのファイルの読み書きができます。
これについては
このドキュメントをご覧ください.
SciPyは、点(座標)の集合の間の距離を計算するための有用な関数を用意しています、
関数 scipy.spatial.distance.pdist
は、
集合中のすべての点と点のペアの間の距離を計算します
import numpy as np
from scipy.spatial.distance import pdist, squareform
# 各行が2D空間における点となっている配列を作る:
# [[0 1]
# [1 0]
# [2 0]]
x = np.array([[0, 1], [1, 0], [2, 0]])
print (x)
# xのすべての行の間のユークリッド距離を計算する
# d[i, j] は x[i, :] と x[j, :] の間の距離となる
# 結果は次:
# [[ 0. 1.41421356 2.23606798]
# [ 1.41421356 0. 1. ]
# [ 2.23606798 1. 0. ]]
d = squareform(pdist(x, 'euclidean'))
print (d)
Matplotlibは、描画のためのライブラリです。
ここでは、 matplotlib.pyplot
モジュールについて簡単に紹介します。
これは、MATLABの描画機能と類似のシステムを提供しています。
matplotlibで最も重要な関数が plot
です。これにより
2Dデータをプロットすることができます。例を示します:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# x座標に対するsin関数の値を計算しy座標の値とする
x = np.arange(0, 3 * np.pi, 0.1)
y = np.sin(x)
# matplotlibを使ってプロットする
plt.plot(x, y)
plt.show() # この plt.show() を呼ばなければ図が見えない
ちょっとだけ余計に作業することで、簡単にいくつもの線を同時に描画したり、題目をつけたり、 凡例をつけたり、軸のラベルをつけたりできます:
import numpy as np
import matplotlib.pyplot as plt
# x座標に対するsin関数の値を計算しy座標の値とする
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
# matplotlibを使ってプロットする
plt.plot(x, y_sin)
plt.plot(x, y_cos)
plt.xlabel('x axis label') # ラベルに日本語は使えないと思って良い
plt.ylabel('y axis label')
plt.title('Sine and Cosine')
plt.legend(['Sine', 'Cosine'])
plt.show() # これを最後に呼ぶこと
plot
関数について、詳しくは
この文書をご覧ください
import numpy as np
import matplotlib.pyplot as plt
# x座標に対応するsinとcos関数の値を計算する
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
# subplotの枠を設定:高さ2、幅1とする,
# そして、最初の枠をactiveにする
plt.subplot(2, 1, 1)
# 最初の図を描画する
plt.plot(x, y_sin)
plt.title('Sine')
# 2番めの枠をactiveにして、別な図を描画する
plt.subplot(2, 1, 2)
plt.plot(x, y_cos)
plt.title('Cosine')
# 図を表示する(これを最後に呼ぶこと)
plt.show()
subplot
関数について、詳しくは
この文書をご覧ください
import numpy as np
from scipy.misc import imread, imresize
import matplotlib.pyplot as plt
img = imread('assets/cat.jpg')
img_tinted = img * [1, 0.95, 0.9]
# 原画像を表示
plt.subplot(1, 2, 1)
plt.imshow(img)
# 色を変えた画像を表示
plt.subplot(1, 2, 2)
# A slight gotcha with imshow is that it might give strange results
# if presented with data that is not uint8. To work around this, we
# explicitly cast the image to uint8 before displaying it.
plt.imshow(np.uint8(img_tinted))
plt.show()