PyLearnST01: numpy配列 ndarray の操作

統計学や機械学習を実施するとき、数値をndarrayという型で扱うことが一般的です。ここではndarryについて説明します。

説明のため、乱数でデータを生成します。 以下は乱数を生成させ、Dat01、Dat02に格納するコードです。 詳細は別ページで詳細に解説するので、おまじないとして書いて見てください。 統計に詳しい人向けにいうと、平均ベクトルをmu、分散共分散行列をsigmaとした2変量正規分布に従う乱数生成となります。

In [1]:
import numpy as np

mu = [0.3, 0.3]
sigma = [[0.1, 0], [0, 0.1]]
Dat01 = np.random.multivariate_normal(mu, sigma, 20)

mu = [0.7, 0.7]
sigma = [[0.1, 0], [0, 0.1]]
Dat02 = np.random.multivariate_normal(mu, sigma, 20)

Dat01には何が詰まっているのか見てみます。

In [2]:
print(Dat01)
[[ 0.54520509  0.49149177]
 [ 0.86891602 -0.24096119]
 [ 0.03630444 -0.03522638]
 [ 0.1353881  -0.02259006]
 [-0.58555984  0.66953454]
 [ 0.66086542  0.33117127]
 [ 0.97794207  0.44005532]
 [ 0.56663943 -0.08202121]
 [ 0.11309975  0.22939382]
 [ 0.57211927  0.08942735]
 [ 0.74823819  0.76767616]
 [ 0.63072279  0.70953642]
 [ 0.07127697  0.69450741]
 [ 0.10079265 -0.03081257]
 [ 0.31538295  0.40064975]
 [ 0.34745718  0.59245174]
 [-0.01005346 -0.1515054 ]
 [ 0.27048109 -0.14133337]
 [ 0.0943274   1.2562408 ]
 [ 0.9809389   0.26948964]]

このように、2次元のデータが20個入っています。次に、この型をチェックしてみます。

In [3]:
type(Dat01)
Out[3]:
numpy.ndarray

numpy.ndarrayと出てきました。統計や機械学習を行うとき、基本的にこの型で行います。統計や機械学習専用の型とでも覚えておいてください。

ndarray要素へのアクセス

アクセス方法は以下の通りです。想定通りの数値が取り出されていることを確認してみてください。

第a列目:

In [4]:
a=0
Dat01[:,a]
Out[4]:
array([ 0.54520509,  0.86891602,  0.03630444,  0.1353881 , -0.58555984,
        0.66086542,  0.97794207,  0.56663943,  0.11309975,  0.57211927,
        0.74823819,  0.63072279,  0.07127697,  0.10079265,  0.31538295,
        0.34745718, -0.01005346,  0.27048109,  0.0943274 ,  0.9809389 ])

第a行目:

In [5]:
a=0
Dat01[a,:]
Out[5]:
array([0.54520509, 0.49149177])

第a行b列目:

In [6]:
a, b = 0, 1
Dat01[a,b]
Out[6]:
0.49149177032820923

第a〜b行、c列目:
リストでの範囲指定と同様に、a:bと指定すると、bの一つ前まで参照されます。

In [7]:
a, b = 0, 3
c = 1
Dat01[a:b, c] # 0:3なので、0, 1, 2行目が取り出される。
Out[7]:
array([ 0.49149177, -0.24096119, -0.03522638])

第a行、b〜c列目:
リストでの範囲指定と同様に、b:cと指定すると、cの一つ前まで参照されます。

In [8]:
a = 0
b, c = 0, 2
Dat01[a, b:c] # 0:2なので、0, 1列目が取り出される。
Out[8]:
array([0.54520509, 0.49149177])

ゼロ配列の生成と簡単な計算

すべての要素にゼロが代入されたndarrayを生成します。以下は、5行4列のゼロ行列を作成するコードです。型はndarrayになっていることも確認できます。

In [9]:
import numpy as np
z = np.zeros([5, 4])
print(z)
type(z)
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
Out[9]:
numpy.ndarray

z1より、ndarrayにスカラー値を足すと、すべての要素にたされます。
z2より、ndarrayにスカラー値のかけ算を行うと、すべての要素に掛け算が行われます。
z3より、同じサイズ同士のndarrayを足すと、同じ場所の値同士が足されます。
z4より、同じサイズ同士のndarrayをかけると、同じ場所の値同士が足されます。

In [10]:
z1 = z + 1
print(z1)

z2 = z1 * 5
print(z2)

z3 = z1 + z2
print(z3)

z4 = z2 * z3 
print(z4)
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
[[5. 5. 5. 5.]
 [5. 5. 5. 5.]
 [5. 5. 5. 5.]
 [5. 5. 5. 5.]
 [5. 5. 5. 5.]]
[[6. 6. 6. 6.]
 [6. 6. 6. 6.]
 [6. 6. 6. 6.]
 [6. 6. 6. 6.]
 [6. 6. 6. 6.]]
[[30. 30. 30. 30.]
 [30. 30. 30. 30.]
 [30. 30. 30. 30.]
 [30. 30. 30. 30.]
 [30. 30. 30. 30.]]

ndarrya同士の結合

次に、array同士の結合について考えてみます。このためにまず、行列のサイズを取得してみます。以下のように、shape関数を用いることで、サイズを獲得することができます。

In [11]:
d=np.shape(Dat01)
print(d)
print("Dat01の行サイズ:", d[0])
print("Dat01の列サイズ:", d[1])
(20, 2)
Dat01の行サイズ: 20
Dat01の列サイズ: 2

Dat01のサイズは20行2列であることがわかりました。Dat02はどうでしょう。

In [12]:
np.shape(Dat02)
Out[12]:
(20, 2)

Dat01とDat02は行列どちらも同じサイズです。ということは縦に並べて、40行2列サイズにすることもできそうです。このように、行成分で結合させるには、np.concatenate([A, B], 0)を使用します。

In [13]:
Dat03 = np.concatenate([Dat01, Dat02], 0)
np.shape(Dat03)
Out[13]:
(40, 2)

40行2列のarrayを作れました。長くなるのでここではprintによる確認はしませんが、勉強中の方は縦につながっていることを確認してください。次に、Dat01とDat02を縦ではなく横に並べてみます。これには、np.concatenate([A, B], 1)を使用します。

In [14]:
Dat04 = np.concatenate([Dat01, Dat02], 1)
np.shape(Dat04)
Out[14]:
(20, 4)

このように、行成分で結合させることができました。concatenate関数に対して、

  • 0を指定すると行成分の結合(縦に並べる)
  • 1を指定すると列成分の結合(横に並べる)になります。

シェイプの変更

Dat04は20行4列サイズですから、合計80個の数字で構成されています。深い分析を行いたいとき、これを80行1列サイズに変形したい、というときもあります。こういうときはreshape関数を使います。np.reshape(A, (b, c))と書くことで、array Aをb行c列に変換してくれます。print関数でDat05の中身を見るとわかりますが、Dat01の0行目の4つ→1行目の4つ→2行目の4つ...の順番で縦に並んで行きます。

In [15]:
Dat05 = np.reshape(Dat04, (80, 1))
np.shape(Dat05)
Out[15]:
(80, 1)

上の説明が正しいなら、Dat05の4行目から7行目は、Dat04の1行目の4つと等しくなるはずです。確認してみましょう。

In [16]:
print(Dat05[4:8, 0])
print(Dat04[1, :])
Dat05[4:8, 0] == Dat04[1, :]
[ 0.86891602 -0.24096119  0.61195506  0.9157316 ]
[ 0.86891602 -0.24096119  0.61195506  0.9157316 ]
Out[16]:
array([ True,  True,  True,  True])

変形されたDat05を元のサイズ、20行4列に戻して、Dat04と等しくなるかチェックしてみます。

In [17]:
Dat04_return = np.reshape(Dat05, (20, 4))
np.shape(Dat04_return)
Dat04 == Dat04_return
Out[17]:
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]])

全部Trueです。元に戻ったことを確認できました。

ndarrayへのキャスト

はじめにも書きましたが、統計分析や機械学習を行うには、ndarray型の変数である必要があります。以前解説したリストとかではNGです。リストなどをndarray型に変換する方法は以下の通りです。

In [18]:
Value_List = [[1, 2, 3], [4, 5, 6]]     # リストで変数を用意しました。
print("型チェック:", type(Value_List)) # この段階ではリスト型
Value_Np = np.array(Value_List)         # adarrayへキャスト
print("型チェック:", type(Value_Np))   # この段階ではndarray型
np.shape(Value_Np)                      # 2行3列であることを確認
型チェック: <class 'list'>
型チェック: <class 'numpy.ndarray'>
Out[18]:
(2, 3)

リスト型が、ndarray型に変換されたことを確認できました。

CSVファイルへの保存と読み込み

分析結果を保存したり、実験データをndarray型で読み込む方法について述べます。まずは保存から。上はヘッダなしでの保存、下はヘッダありでの保存となります。状況に応じて使い分けます。jupyter notebookを起動しているディレクトリで実際にデータが保存されたことを確認してみてください。(comment=''をつけないと謎のシャープが入る)

In [19]:
np.savetxt("Dat04_NoHeader.csv", Dat04, delimiter=",")
np.savetxt("Dat04_Header.csv", Dat04, delimiter=",", header="c1, c2, c3, c4", comments='')

次に、保存したデータをロードしてみます。ndarray型は文字を扱うことができないので、ヘッダ付きのcsvファイルを読み込むには、ヘッダ行を飛ばして読む必要があります。skiprows=1とは、ヘッダ分1行飛ばすという意味があります。

In [20]:
Dat04_LoadHeader = np.loadtxt("Dat04_Header.csv", delimiter=",", skiprows=1)
np.shape(Dat04_LoadHeader)
Out[20]:
(20, 4)

次に、ヘッダなしの方を読み込んでみます。今度はヘッダがないので、skiprowsは不要となります。

In [21]:
Dat04_LoadNoHeader = np.loadtxt("Dat04_NoHeader.csv", delimiter=",")
np.shape(Dat04_LoadNoHeader)
Out[21]:
(20, 4)

「np.」とはなんぞや?

上の方に、「import numpy as np」と書いてあります。これは、numpyという便利な関数などがまとめられた便利ボックスを使うという宣言のようなものです。実際にその関数を使うには、その関数を使うには、「as」の後ろにかいてある「np」をつけます。ここで紹介した、以下はすべて、numpyという便利ボックスに入っている関数です。

  • np.zeros すべての要素がゼロのarrayを生成
  • np.shape arrayのサイズ
  • np.reshape arrayのシェイプ変更
  • np.concatenate arrayの結合
  • np.array ndarray型へのキャスト
  • np.savetext ndarray型の保存
  • np.loadtext ndarray型へ変数への読み込み

とりあえずは分析に必須のものを並べました。 他にもたくさんあるので、自分で勉強してください。