この記事を読むのに必要な時間は約 47 分です。
NumPyとは、Pythonに多次元配列の演算機能を与え、またその扱いを容易にする外部ライブラリーの1つです。
それでは、早速… 参ります。
まずは、NumPyをインポートすることから始めます。
import numpy as np
これで、NumPyが使えるようになります。
目次
基本的な多次元配列の生成
それでは、まず1次元の配列を生成してみます。
配列の生成にはarray()を使います。
a = np.array([1,2,3,4,5])
print(a)
# 出力: [1 2 3 4 5]
print()の出力から、1次元の配列を作成できていることが確認できました。
次に、2次元配列を生成してみます。
b = np.array([[1,2,3,4,5],[6,7,8,9,10]])
print(b)
# 出力: [[1 2 3 4 5] [6 7 8 9 10]]
同じく、2次元の配列も作成することができました。
配列の情報を確認する
先に生成した配列が、どのようなオブジェクトであるか?、配列の要素がどんなデータ型をしているのか?について確認するためには、それぞれ「type」と「dtype」を使います。
a = np.array([1,2,3,4,5])
b = np.array([[1,2,3,4,5],[6,7,8,9,10]])
print(type(a))
print(a.dtype)
print(type(b))
print(b.dtype)
# <class 'numpy.ndarray'>
# int64
配列(a,b)の結果をそれぞれ確認してみると、どちらもNumPyのndarrayオブジェクトであり、要素のデータ型はint型(整数)であることが分かりました。
そして、配列を生成するタイミングで要素のデータ型を指定することができます。
a = np.array([1,2,3,4,5], dtype='float')
print(a)
print(a.dtype)
# [1. 2. 3. 4. 5.]
# float64
ここまでの配列は、1次元・2次元配列について見てきましたが、その配列が本当に1次元・2次元配列なのか?迷ってしまうこともあるかもしれません。
そんなときは、ndim()を使うとハッキリします。
a = np.array([1,2,3,4,5])
b = np.array([[1,2,3,4,5],[6,7,8,9,10]])
print('配列aの次元数は、',np.ndim(a))
print('配列bの次元数は、',np.ndim(b))
# 配列aの次元数は、 1
# 配列bの次元数は、 2
出力から、aの配列は「1次元」。bの配列は「2次元」であることが分かりました。
次に、配列bを例にして配列の形状について確認してみよう。
print(b.shape)
# (2, 5)
配列の形状を確認するには、shapeを使うと確認できます。結果から、配列bは2x5(行と列)の形状をもった2次元であると言えます。
要素の取得と再代入
NumPyの配列から要素を取得、再代入する方法はPythoの配列と同じように、インデックス・スライスで行えます。
2次元配列を例に確認してみます。
x = np.array([[3,4,5,6],[7,8,9,10]])
y = x[0]
print(y)
# [3 4 5 6]
x[0]に再代入
x[0] = [11,12,13,14]
print(x)
# [[11 12 13 14] [7 8 9 10]]
次は、スライスを試してみます。
z = x[1,:]
print(z)
# [7 8 9 10]
x[1,:] = [15,16,17,18]
print(x)
# [[11 12 13 14] [15 16 17 18]]
上手くいきました。
もう少し遊んでみましょう。
z = x[:,1:3]
print(z)
# [[12 13] [16 17]]
x[:,1:3] = 0
print(x)
# [[11 0 0 14] [15 0 0 18]]
z = x[:,:-1]
print(z)
# [[11 0 0] [15 0 0]]
ちなみに「:」が表しているのは、「すべて」という意味です。
さまざまな形式の配列生成
ここまでの配列生成には、array()を用いてきましたが、他にも配列を生成する手段は存在しており、arange()とreshape()を組み合わせて使うことで、多次元配列の生成は簡単になります。
c = np.arange(1,6)
d = np.arange(1,11).reshape((2,5))
print(c)
print(d)
# [1 2 3 4 5]
# [[1 2 3 4 5][6 7 8 9 10]]
arange()は、第一引数のみを指定した場合0〜?の間隔に対応した1次元配列を生成し、ここでは第二引数も指定しているので結果的に、1〜6未満(配列c)の間隔に対応した1次元配列を生成しています。
配列dでは、arange()に続いてreshape()が設定してあります。reshape()は引数に対して形状データをタプルを与えることで形状を変換させ、ここでは(2x5)になるように指定しています。
多次元配列の要素の全てが、1あるいは0で埋められた配列を生成したいときには、ones()とzeros()があります。
使い方は、どちらも同じです。
f = np.ones((4,5), dtype='int')
print(f)
# dtype='int'でデータ型を整数にしています。
# [[1 1 1 1 1 ][1 1 1 1 1 ]..]
g = np.zeros((3,3))
print(g)
# dtype指定なしの場合はones()も同じくfloat型です
# [[0. 0. 0.][0. 0. 0.][0. 0. 0.]]
0と1には、ones()、zeros()がありますが、他の数値にはありません。
ですが、同じように統一された値で埋められた配列を生成する方法は他にもあり、full()を使うと同じようなことができます。
h = np.full((3,6), 9)
print(h)
# [[9 9 9 9 9 9][9 9 9 9 9 9][9 9 9 9 9 9]]
eye()を使うと、単位行列を簡単に生成することができます。
単位行列とは、対角に「1」が走る「0と1」からなる正方行列(NxN)のことです。
i = np.eye(6)
print(i)
# [[1. 0. 0. 0. 0. 0.]
# [0. 1. 0. 0. 0. 0.]
# [0. 0. 1. 0. 0. 0.]
# [0. 0. 0. 1. 0. 0.]
# [0. 0. 0. 0. 1. 0.]
# [0. 0. 0. 0. 0. 1.]]
また、第二引数以降に、Mは列、kは対角線のインデックスを指定すると位置をずらせます。
j = np.eye(6,M=5,k=2)
print(j)
# [[0. 0. 1. 0. 0.]
# [0. 0. 0. 1. 0.]
# [0. 0. 0. 0. 1.]
# [0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]]
2つの数値間隔から上限を指定し、任意の要素数で均等割りされた配列の生成にはlinspace()の出番です。
k = np.linspace(3.0,4.5,num=7)
print(k)
l = np.linspace(3.0,4.5,num=7,endpoint=False)
print(l)
m = np.linspace(3.0,4.5,num=7,retstep=True)
print(m)
# [3. 3.25 3.5 3.75 4. 4.25 4.5 ]
# [3. 3.21428571 3.42857143 3.64285714 3.85714286 4.07142857 4.28571429]
# (array([3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 ]), 0.25)
まず、1つ目の配列は「3.0以上~4.5以下」の間隔から7等分した配列を生成する意味を示しています。
2つ目の配列ではendpoint=Falseと指定してありますが、これは4.5は含まないという意味になり、結果的に生成される配列の条件が、ここでは「3.0以上~4.5未満」の間隔から同じく7等分した配列を生成するとなる。
最後の3つ目では、retstep=Trueという指定が設定されています。
これを指定してあげると要素間のステップ(間隔)を確認することができます。この例では、0.25間隔で7等分されているという結果が分かります。
なお、retstepにTrueを指定した場合、帰り値は配列ではなくタプルなので注意します。
乱数による配列生成
random.random()を使うと、0.0~1.0の範囲のランダムな浮動小数点数からなる配列を作成することができます。
このとき引数にはタプルで形状データを渡します。
x = np.random.random((3,2))
print(x)
# [[0.70291769 0.33325623]
# [0.49584301 0.64285317]
# [0.62360047 0.38004473]]
ランダムな結果になるのでサンプルとは等しくないかもしれません。
ランダムだけど、結果は変わらないで欲しいこともあります。そんなときは、random.seed()を使って出力されるランダムな値を固定することができます。
np.random.seed(999)
x = np.random.random((3,2))
np.random.seed(999)
y = np.random.random((2,3))
print(x)
print(y)
# [[0.80342804 0.5275223 ]
# [0.11911147 0.63968144]
# [0.09092526 0.33222568]]
# [[0.80342804 0.5275223 0.11911147]
# [0.63968144 0.09092526 0.33222568]]
今回は、シード値として999を指定しましたが好きなもので良いでしょう。
結果を確認してみると、配列の形状は異なるが出力されているランダムな値は変化していません。
同じく、0.0~1.0の間隔からランダムな値からなる配列生成にはrandom.rand()があり、random.random()との違いは、引数に渡すときにタプルではないところです。
np.random.seed(999)
x = np.random.rand(3,2)
print(x)
# [[0.80342804 0.5275223 ]
# [0.11911147 0.63968144]
# [0.09092526 0.33222568]]
任意の間隔から、ランダムな値の配列を生成したいときはrandom.randint()があります。
x = np.random.randint(3, 10)
print(x)
# 8
3つの要素がある、任意の範囲付きの乱数配列
np.random.seed(999)
x = np.random.randint(1,[3,15,50])
print(x)
# [ 1 13 38]
このコードの意味は、[(1~3),(1~15),(1~50)]の各要素が任意の間隔で、それぞれ出力されています。
平均値が0。分散が1の正規分布に従う、ランダムな値から配列を作成するときはrandom.randn()を使います。
使い方は、random.rand()と同じく渡す引数にタプルを使う必要はありません。
np.random.seed(999)
x = np.random.randn(3,2)
print(x)
# [[ 0.12715784 1.40189088]
# [ 0.31481499 -0.85844916]
# [-0.26613444 -0.64890071]]
任意の平均値、任意の標準偏差、任意の形状、を引数にもつ正規分布乱数から配列を生成したいときは、random.normal()があります。
np.random.seed(999)
mean = 0
sigma = 0.5
x = np.random.normal(mean, sigma, 1000)
meanは平均、sigmaは標準偏差を表しています。このとき変数xには、平均が0。標準偏差が0.5の正規分布乱数からなる1000個の要素を持つ1次元配列が入っています。
平均と標準偏差が守られているか?確認してみます。
# 平均
print(abs(mean - np.mean(x)))
# 0.00938421456902442
# 標準偏差
print(abs(sigma - np.std(x)))
# 0.01644626707943775
この結果から、誤差はほとんどありません。よって、このサンプルでの平均0、標準偏差0.5は守られています。
配列の変形
ここからは、多次元配列の変形についてみていきます。先ほど少しだけ登場した、reshape()を使うと形状変形の類は事足りるように感じられますが、他にも便利な関数があります。
多次元配列を1次元配列に変形する方法として、ravel()とflatten()があり、両方とも1次元に変形するという意味では同じ機能を提供しますが、参照とコピーの類で両者は異なります。
n = np.arange(10).reshape((2,5))
print('元の配列: ',n)
o = np.ravel(n)
print('ravel使用: ',o)
p = n.flatten()
print('flatten使用: ',p)
n[0,0] = 81
print('ravel使用: ',o)
print('flatten使用: ',p)
# 元の配列: [[0 1 2 3 4][5 6 7 8 9]]
# ravel使用:[0 1 2 3 4 5 6 7 8 9]
# flatten使用:[0 1 2 3 4 5 6 7 8 9]
# 代入後
# ravel使用:[81 1 2 3 4 5 6 7 8 9]
# flatten使用:[0 1 2 3 4 5 6 7 8 9]
どちらも多次元配列を1次元配列に変形する面では同じです。しかし、「ravelは、元の配列を参照している」「flattenは、元の配列を新しい配列にコピーしている」。
ravelは元の配列を参照しているので、元の配列に変更があった場合には影響を受けます。一方で、flattenの場合は新しい配列にコピーしているので変更による影響を受けません。
ちなみに、多次元配列を1次元配列に変形する方法はreshape(-1)でも可能です。挙動はravelと同じく参照します。
q = np.arange(10, dtype='float').reshape((2,5))
print('元の配列: ',q)
q[0,0] = np.nan # Not a number 略して nan
r = q.reshape(-1)
print(r)
# 元の配列:[[0. 1. 2. 3. 4.][5. 6. 7. 8. 9.]]
# [nan 1. 2. 3. 4. 5. 6. 7. 8. 9.]
複製
元の多次元配列から新しい配列として複製を作成した場合は、copy()を使います。
s = np.array([[1,2,3],[4,5,6]])
t = s.copy()
s[0,0] = 99 # 1のところに99を代入
print(s)
print(t)
# [[99 2 3][4 5 6]]
# [[1 2 3][4 5 6]]
二つの形状が同じ多次元配列を用意して、片方の配列にもう一方の配列を上書きする感じでコピーしたい場合は、copyto()を使用します。
u = np.arange(6).reshape((2,3))
v = np.zeros((2,3),dtype='int')
np.copyto(v,u) # 配列vに配列uの中身をコピー
print(v)
# [[0 1 2][3 4 5]]
次元(軸)の追加
軸とは何か?。
例えば、1次元の配列で確認します。
w = np.array([1,2,3,4,5,6])
print(w.shape)
# (6,)
配列wを確認してみると、その形状は1次元配列で要素が6つであることが分かります。この配列を1x6(列と行)の2次元配列に変形させたい場合、どうすれば良いでしょうか?
答えは、配列wに対して新たな列を追加してあげれば良いのです。
配列に次元を追加するには、expand_dims()とnewaxisという方法があります。
x = np.expand_dims(w, axis=0)
print(x.shape)
# (1, 6)
配列wの列方向に軸を追加することができました。これで配列wは、1×6の2次元配列になりました。
今みたコードの中に、axis=0という箇所がありました。ここにポイントがあります。
axis=0は、列方向に対して軸を追加するというサインです。一方で、行方向に軸を追加したい場合は、axis=1と記述します。
y = np.expand_dims(w, axis=1)
print(y.shape)
# (6, 1)
# 配列は[[1][2][3][4][5][6]]になっている。
このように今度は行方向に軸を追加することができました。
もう一つの軸の追加方法があります。これはPythonのスライスの要領を知っているとき、こっちの方が分かりやすいかもしれません。
z = np.array([5,6,7,8,9,10])
a = z[np.newaxis, :] # 列方向
b = z[:, np.newaxis] # 行方向
print(a.shape)
print(b.shape)
# (1, 6)
# (6, 1)
結果はexpand_dims()と同じであることが分かります。
また、軸数が1の次元を削除する方法もあり、squeeze()で行えます。
x = np.array([[1,2,3]])
print(x.shape)
y = np.squeeze(x)
print(y.shape)
# (1, 3)
# (3,)
転置
転置とは、例えば「2×3」の形状を持つ配列を「3×2」のようにする処理のことを言います。
配列を転置する方法には、Tとtranspose()があります。
x = np.array([1,2,3])
y = np.expand_dims(x,axis=0)
z = np.arange(1,21).reshape((4,5))
print(y.T)
print(z)
print(z.T)
# [[1][2][3]]
# 省略
# 省略
まず、配列xは要素数が3の1次元配列です。ここから、expand_dims()を用いて列方向に軸を追加しているので、この配列yは「1×3」の形状になっていることを踏まえてください。その後、y.Tで「3×1」の形状に転置処理されています。
transpose()の方も転置処理をおこないますが、指定方法に少しクセがあります。
x = np.array([[3,6,9],[11,12,13]])
x2 = np.array([[5,6,7],[8,9,10],[11,12,13]])
y = np.transpose(x,(0,1))
print(y)
y1 = np.transpose(x, (1,0))
print(y1)
y2 = np.transpose(x2)
print(y2)
# [[3 6 9][11 12 13]]
# [[3 11][6 12][9 13]]
# [[5 8 11][6 9 12][7 10 13]]
第一引数には、変形する配列を渡します。第二引数にはタプルで形状のインデックスを渡します。
x = np.arange(1,85).reshape((3,4,7))
print('元の配列は、',x)
print()
print('配列の形状は、',x.shape)
"""
元の配列は、 [[[ 1 2 3 4 5 6 7]
[ 8 9 10 11 12 13 14]
[15 16 17 18 19 20 21]
[22 23 24 25 26 27 28]]
[[29 30 31 32 33 34 35]
[36 37 38 39 40 41 42]
[43 44 45 46 47 48 49]
[50 51 52 53 54 55 56]]
[[57 58 59 60 61 62 63]
[64 65 66 67 68 69 70]
[71 72 73 74 75 76 77]
[78 79 80 81 82 83 84]]]
配列の形状は、 (3, 4, 7)
"""
y = np.transpose(x, (1,2,0))
print('変更後の配列は、',y)
print()
print('変更後の形状は、',y.shape)
"""
変更後の配列は、 [[[ 1 29 57]
[ 2 30 58]
[ 3 31 59]
[ 4 32 60]
[ 5 33 61]
[ 6 34 62]
[ 7 35 63]]
[[ 8 36 64]
[ 9 37 65]
[10 38 66]
[11 39 67]
[12 40 68]
[13 41 69]
[14 42 70]]
[[15 43 71]
[16 44 72]
[17 45 73]
[18 46 74]
[19 47 75]
[20 48 76]
[21 49 77]]
[[22 50 78]
[23 51 79]
[24 52 80]
[25 53 81]
[26 54 82]
[27 55 83]
[28 56 84]]]
変更後の形状は、 (4, 7, 3)
"""
要素の並び替え
多次元配列の要素の並び替えには、sort()があります。
x = np.array([[32,54],[98,43],[32,19]])
y = np.sort(x)
print(y)
# [[32 54][43 98][19 32]]
配列の構造を考慮せずに要素を並び替えることもできます。その際は、axis=Noneを指定します。
y2 = np.sort(x, axis=None)
print(y2)
# [19 32 32 43 54 98]
# np.sort(x, axis=None)[::-1]で降順になる。
axis=Noneを指定して並び替えを行った場合、返り値は1次元配列になります。axisには0か1を指定して列方向。あるいは、行方向といった並びかも可能です。
要素を反転させる並び替えは、[::-1]で行えます。
x = np.arange(10)
print(x[::-1])
# [9 8 7 6 5 4 3 2 1 0]
配列の結合と分割
異なる2つの配列どうしを結合するときは、concatenate()を使います。
arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9]])
arr3 = np.array([[10,11,12]])
c_arr = np.concatenate((arr1, arr2), axis=0)
print(c_arr)
c_arr2 = np.concatenate((c_arr, arr3.T), axis=1)
print(c_arr2)
"""
[[1 2 3]
[4 5 6]
[7 8 9]]
[[ 1 2 3 10]
[ 4 5 6 11]
[ 7 8 9 12]]
"""
# c_arr2 = 3x3と3x1の結合。3x3と1x3ではダメ。
一つ目のc_arr配列の結果はaxis=0なので、配列arr1の列方向に配列arr2を結合させる意味になります。
配列を結合する際は、2つの配列の構造に注意する必要があり、2つ目の配列c_arr2をみてもらうと、配列arr3に転置が実行されaxis=1ということで行方向の結合がされていることが分かります。
concatenate()では、axisを使って結合面の場合分けをしましたが、axis=1に相当する行方向に結合させたときには、hstack()。axis=0に相当する列方向に結合させたいときには、vstack()というような場合分けされた関数もあります。
x = np.array([1,2,3])
y = np.array([4,5,6])
z = np.hstack((x,y))
print(z)
# [1 2 3 4 5 6]
z = np.vstack((x,y))
print(z)
# [[1 2 3][4 5 6]]
イメージが掴みやすい例
x = np.ones(6, dtype='int').reshape((2,3))
y = np.full((2,2),9)
print(x)
z = np.hstack((x,y))
print(z)
y2 = np.full((2,3),9)
z2 = np.vstack((x,y2))
print(z2)
"""
[[1 1 1]
[1 1 1]]
[[1 1 1 9 9]
[1 1 1 9 9]]
[[1 1 1]
[1 1 1]
[9 9 9]
[9 9 9]]
"""
次に、1つの配列を異なる配列に分割する方法について確認します。
基本的には、split()を使います。また、行方向なのか列方向なのかで、hsplit()、vsplit()があります。
x = np.arange(10)
print(x)
a,b,c = np.split(x,[4,6])
print(a)
print(b)
print(c)
"""
[0 1 2 3 4 5 6 7 8 9]
[0 1 2 3]
[4 5]
[6 7 8 9]
"""
引数には、分割する境界を「分割数」あるいは、インデックスで指定できます。
x = np.array([[1,2,3],[4,5,6]])
a, b = np.split(x,2)
print(a)
print('と')
print(b)
"""
[[1 2 3]]
と
[[4 5 6]]
"""
次のサンプルは、行方向から2つの配列に分割する例になります。
x = np.arange(1,9).reshape((2,4))
print(x)
a, b = np.split(x,2, axis=1)
print(a)
print('と')
print(b)
"""
[[1 2 3 4]
[5 6 7 8]]
[[1 2]
[5 6]]
と
[[3 4]
[7 8]]
"""
ここまでに見てきた配列の分割は、ちょうど分割できる状況を例に行ってきました。
分割した配列のどちらかが奇数個になる配列の分割では、array_split()を使います。
x = np.arange(1,14)
print(x)
# 2分割を考えたときsplitの場合は
# a, b = np.split(x,2) できない。
a, b = np.array_split(x,2)
print(a)
print('と')
print(b)
"""
[ 1 2 3 4 5 6 7 8 9 10 11 12 13]
[1 2 3 4 5 6 7]
と
[ 8 9 10 11 12 13]
"""
array_split()は、このサンプルのように良い感じに分割します。
次は、下記のサンプルで確認してみます。
x = np.arange(1,26).reshape((5,5))
print(x)
"""
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
"""
a,b = np.array_split(x,2,axis=0)
print(a)
print('と')
print(b)
"""
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]]
と
[[16 17 18 19 20]
[21 22 23 24 25]]
"""
配列の分割にも、hsplit()とvsplit()なるものがあります。
これはaxisを指定したsplit()と同じなので割愛します。
配列の演算
NumPyは、多次元配列の演算を強力にする機能としてブロードキャストとユニバーサルファンクションを提供します。
ブロードキャスト
ブロードキャストは、NumPyの多次元配列の各要素に対して直接、演算子による計算ができる機能を表します。
x = np.array([[1,2,3],[4,5,6]])
y = x**2
print(y)
# [[1 4 9][16 25 36]]
結果から、各要素が2乗されている事が分かります。
ユニバーサルファンクション
名前から、小難しそうな印象を受けるかもしれませんがブロードキャストを知っていれば直感的に理解できます。
例として、多次元配列の要素の値から絶対値を取得してみます。
x = np.array([[1,-2,-3],[4,-5,6]])
print(np.abs(x))
# [[1 2 3][4 5 6]]
例では、絶対値を取得するabs()を使用しました。他にもたくさんあります。ここでは、一部を紹介します。
配列の最小値、または軸に沿った最小値を取得したい場合にamin()があります。
x = np.arange(6).reshape((3,2))
y = np.amin(x)
print(y)
# 0
y = np.amin(x, axis=0)
print(y)
# [0 1]
y = np.amin(x, axis=1)
print(y)
# [0 2 4]
配列の最大値、または軸に沿った最大値を取得したい場合はamax()が使えます。
x = np.arange(6).reshape((2,3))
y = np.amax(x)
print(y)
# 5
y = np.amax(x, axis=0)
print(y)
# [3 4 5]
y = np.amax(x, axis=1)
print(y)
# [2 5]
中央値を取得したいときは、median()があります。
x = np.arange(8).reshape((2,4))
y = np.median(x)
print(x)
print(y)
# [[0 1 2 3][4 5 6 7]]
# 3.5
平均値を取得したい場合は、mean()を使います。
x = np.arange(10).reshape((2,5))
print(x)
y = np.mean(x)
y1 = np.mean(x, axis=0)
y2 = np.mean(x, axis=1)
print(y)
print(y1)
print(y2)
"""
[[0 1 2 3 4]
[5 6 7 8 9]]
4.5
[2.5 3.5 4.5 5.5 6.5]
[2. 7.]
"""
標準偏差を算出するときは、std()を使います。
np.random.seed(999)
x = np.random.randn(1000)
y = np.std(x, ddof=1)
print(y)
# 1.0334093680837169
分散を求めたいときは、var()があります。
np.random.seed(999)
x = np.random.randn(1000)
y = np.var(x, ddof=1)
print()
print(y)
# 1.0679349220431869
他にもたくさん種類があるので、ここでは扱い切れません。すいません。
真偽値の配列を用いたマスク処理
NumPyの多次元配列に対して、比較演算子を使うと構造を維持した上で真偽値からなる配列になります。
x = np.array([[12,4,7,9,18],[11,6,20,3,1]])
print(x <= 9)
"""
[[False True True True False]
[False True False True True]]
"""
ここでは、配列xに対して、「9以下の要素はありますか?」という質問を投げかけていると想像して頂いて、該当する結果を配列構造を維持しながら要素が真偽値となって返されていることが確認できます。
この真偽値配列を活用して処理することをマスク操作と言います。
「9以下」の要素を抽出してみます。
print(x[x <= 9])
# [4 7 9 6 3 1]
Pythonでは、Trueを1、Falseを0として扱われるので、要素の値を足してくれるsum()と組み合わせて使用すると条件に合う要素の個数を取得するのに応用ができます。
print(np.sum(x <= 9))
# 6
結果から配列xには、要素の値が「9以下」であるという条件に合致する値が「6個」あるということが分かります。
もう一つサンプルを見てみます。
配列xと配列yがあり、配列yと要素の値が被っている要素を配列xから取得する例です。
x = np.array([[23,5,32,8,11],[32,6,13,90,54]])
y = np.array([[43,5,43,8,11],[54,6,12,90,54]])
z = (x == y)
print(z)
"""
[[False True False True True]
[False True False True True]]
"""
print(x[z])
# [ 5 8 11 6 90 54]
最後に、where()を使うと条件に対して、Trueの場合とFalseの場合とで異なる処理をさせることができます。
a = np.arange(10)
np.where(a<5, a, 10*a) # (条件、Trueのとき、Falseのとき)
# [ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90]
この例の場合は、0〜9までの要素を持つ配列aに「5未満」という条件に対してTrueの場合は、配列aの値をそのまま。Falseの場合は10を掛けた値にするという処理の内容です。
同じような使い方でもう一つ
x = np.arange(9).reshape((3,3))
y = np.where((x >= 5) & (x <= 7), x**2, x-x)
print(y)
"""
[[ 0 0 0]
[ 0 0 25]
[36 49 0]]
"""
以上でNumPyの触りは完了したと言って良いでしょう。
最後まで読んでいただき有難う。