よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Python機械学習】numpyの基本的使い方

numpyの使い方メモ。
内容は機械学習でよく使うらしいメソッドのものにしぼりました。



はじめに


このブログで以前にOpenCVを使って画像から顔や目の検出をやりました。
画像が顔なのか目の写真なのか等を判別できるのは機械学習の技術が応用されているからです。
機械学習はたくさんのデータからパターンを計算で導き出してモデルを作ります。
何の目的で何のデータからどういう分析アルゴリズムの方法で問題化してそれを解くか、欲しい情報をいかに精度よく弾き出せるモデルが作れるかが機械学習技術者の問われる所のようです。その際にほぼ必須で使われると言われるライブラリがnumpy、pandas、matplotlib。

前置きが長くなった。今回はそのnumpyの使い方。



実行環境


Windows10
Anaconda
JupyterLab
Python3.7



Androidスマホ
termux
Python3.8
Jupyter Notebook



目次


numpyのインポート


numpyはPythonの標準ライブラリではないので、先にpipやcondaなどでインストールが必要です。

import numpy as np

配列の生成


機械学習では既にCSVExcelファイルで用意されたデータを使うことがほとんどなので、配列を生成することはあまりないらしいが、一応。

np.array([3,2])
array([3, 2])
np.array([[1,2,3],[4,5,6],[7,8,9]])
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

shape 形状を確認する


np.array([3,2]).shape
(2,)
np.array([[1,2,3],[4,5,6],[7,8,9]]).shape
(3, 3)
array = np.array([[2018,1,1],[2019,1,5],[2020,5,6],[2018,3,8]])

print(array)
print()
print(array.shape)
[[2018    1    1]
 [2019    1    5]
 [2020    5    6]
 [2018    3    8]]

(4, 3)
# 行のみ確認
array.shape[0]
4
# 列を確認
array.shape[1]
3

配列の形状を変形する


# 4行3列を3行4列に変換
array.reshape(3,4)
array([[2018,    1,    1, 2019],
       [   1,    5, 2020,    5],
       [   6, 2018,    3,    8]])
# 4行3列を6行2列に変換
array.reshape(6,2)
array([[2018,    1],
       [   1, 2019],
       [   1,    5],
       [2020,    5],
       [   6, 2018],
       [   3,    8]])
# 4行3列を12行の一次元配列に変換
array.ravel()
array([2018,    1,    1, 2019,    1,    5, 2020,    5,    6, 2018,    3,
          8])

※ 形状を変換する場合、要素の個数が前後で合っていなければ出来ない。errorになる。
4×3 = 6×2 = ravel() = 12個

抽出


# 元の行列を表示
print("元の配列\n",array)
print()
# 任意の行のみスライスで取り出し表示
print("2行目~最後の行まで抽出\n",array[1:])
元の配列
 [[2018    1    1]
 [2019    1    5]
 [2020    5    6]
 [2018    3    8]]

2行目~最後の行まで抽出
 [[2019    1    5]
 [2020    5    6]
 [2018    3    8]]
# 全ての行の2列目全部を抽出
array[: ,1]
array([1, 1, 5, 3])
# 3行目の2列目を抽出する場合
print(array)
print()
array[2][1]
[[2018    1    1]
 [2019    1    5]
 [2020    5    6]
 [2018    3    8]]






5

比較演算子による条件での判別


# 比較演算子によりbool型で返す
array > 5
array([[ True, False, False],
       [ True, False, False],
       [ True, False,  True],
       [ True, False,  True]])

条件を満たすものはTrue,そうでないものはFalseが返る。

# 条件を満たす値のみ抽出
array[array > 2000]
array([2018, 2019, 2020, 2018])

Trueの値のみを取り出せる。

置換


where(条件,当てはまる場合の置換する値、そうでない場合の置換の値)
2000より大きい値を0、小さければ1に置換させてみる。

# 条件を満たす値のみ抽出
print("置換前\n",array)
z = np.where(array >2000,0,1)
print("置換後\n",z)
置換前
 [[2018    1    1]
 [2019    1    5]
 [2020    5    6]
 [2018    3    8]]
置換後
 [[0 1 1]
 [0 1 1]
 [0 1 1]
 [0 1 1]]

配列の自動生成


# 0から9までの整数を(合計10個)生成
a = np.arange(10)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 1以上5未満までの整数を生成
b = np.arange(1,5)
b
array([1, 2, 3, 4])
# 列ベクトルに変換
b.reshape(4,1)
array([[1],
       [2],
       [3],
       [4]])

上の例の場合、要素数が4つなので(4,1)としているのですが、個数が分からない場合などに(-1,1)と指定すると調べる手間を省いて列ベクトルに変換することができる。

# 列ベクトルに変換
b.reshape(-1,1)
array([[1],
       [2],
       [3],
       [4]])
# 列ベクトルに変換して、形状の確認
b.reshape(-1,1).shape
(4, 1)

疑似乱数の生成


randint

# -10から10までの整数をランダムに1個生成
c = np.random.randint(-10,11)
c
-8
# 0から10までの整数をランダムに1個生成を10回繰り返す
for i in range(10):
    c = np.random.randint(11)
    print(c)
2
10
0
7
5
9
0
10
2
10

上のように、実行する毎に疑似乱数の値が変わる。
これは乱数生成器の計算にシステム時間を使っている為、実行する度に変わっているそうです。

seed 乱数の固定

乱数の生成を固定したい場合はseed()を使う。
()に0以上の整数を適当に入れる(何でもOK)、乱数が変化しなくなる。
下記のように書くと、何度実行しても全ての乱数が「5」で固定された。

# 0から10までの整数をランダムに1個生成を10回繰り返す

for i in range(10):
    np.random.seed(0)
    c = np.random.randint(11)   
    print(c)
5
5
5
5
5
5
5
5
5
5
# 0から10までの整数をランダムに20個生成
d = np.random.randint(0,11,20)
d
array([ 9,  4, 10,  0,  5,  9,  2, 10, 10,  5,  1,  9,  9,  1,  0,  6,  4,
        6,  5, 10])
# 範囲(最小値以上,最大値以下,個数)の浮動小数点数を指定個数
np.random.uniform(0.5,0.8,2)
array([0.66634162, 0.61096065])
# 小数第三位での四捨五入
q = np.random.uniform(0.1,0.8,2)
q_2 = q.round(2)
print(q)
print(q_2)
[0.49970587 0.75007558]
[0.5  0.75]

rand

# 0.0以上1.0未満の実数の一様分布な乱数を1個生成
e = np.random.rand()
e
0.15896958364551972
# 0.0以上1.0未満の実数を一様分布な乱数を20個生成
f = np.random.rand(20)
f
array([0.79172504, 0.52889492, 0.56804456, 0.92559664, 0.07103606,
       0.0871293 , 0.0202184 , 0.83261985, 0.77815675, 0.87001215,
       0.97861834, 0.79915856, 0.46147936, 0.78052918, 0.11827443,
       0.63992102, 0.14335329, 0.94466892, 0.52184832, 0.41466194])
# 0.0以上1.0未満の実数の一様分布な乱数を5行2列で生成して並べる
g = np.random.rand(5,2)
g
array([[0.3595079 , 0.43703195],
       [0.6976312 , 0.06022547],
       [0.66676672, 0.67063787],
       [0.21038256, 0.1289263 ],
       [0.31542835, 0.36371077]])
# 0から1までの実数をランダムに20個生成
h = np.random.rand(20)
h
array([0.68173249, 0.8806176 , 0.2969151 , 0.98861803, 0.71783171,
       0.85772122, 0.09218162, 0.08977945, 0.94182696, 0.18698314,
       0.96627109, 0.10167093, 0.07445237, 0.19986474, 0.3524313 ,
       0.61266651, 0.60138211, 0.28982749, 0.0908768 , 0.01877648])

randn

np.random.randn(20)
array([ 0.4253934 , -0.02797118,  1.47598983,  0.6467801 , -0.36433431,
       -0.67877739, -0.35362786, -0.74074747, -0.67502183, -0.13278426,
        0.61980106,  1.79116846,  0.17100044, -1.72567135,  0.16065854,
       -0.85898532, -0.20642094,  0.48842647, -0.83833097,  0.38116374])

randnはマイナスの数が含まれる。

randとrandnの違い


randは0~1までの範囲で一様な分布の疑似乱数を指定個数だけ発生させる。
一方、randnは標準正規分布(ガウス分布)の疑似乱数を指定個数だけ発生させる。平均が0で標準偏差1になるように、0に近い値がより発生しやすいような乱数を生成する。

randとrandnのヒストグラムを描画


ヒストグラムとは、データをある区間ごとに区切って、その各区間に収まるデータの個数を棒グラフで表すグラフのことだそうです。
たとえば数十人分の国語テストの点数データがあるとします。データは0~100点の値をとる。横軸に点数が入ります。それをbinsというパラメータで特定数に分割して区切り、その分割された区間の点数内に何人が含まれるかの頻度を縦軸に取る。
横軸を階級、縦軸を度数を呼ぶようです。
np.hist(データ、bins=階級の分割数)で図示します。

randとrandnでそれぞれ1万個ずつ乱数を生成し、取りうる値を横軸にbinsで100分割し、縦軸にその区間ごとの出現頻度をカウントします。

rand

import matplotlib.pyplot as plt
%matplotlib inline

R = np.random.rand(10000)
plt.hist(R,bins=100)
plt.savefig("hist_rand.png")
plt.show()

f:id:chayarokurokuro:20200106174919p:plain

randn

import matplotlib.pyplot as plt
%matplotlib inline

R_n = np.random.randn(10000)
plt.hist(R_n,bins=100)
plt.savefig("hist_randn.png")
plt.show()

f:id:chayarokurokuro:20200106175002p:plain

上のrandが一様分布なのに対し、下のrandnはカウス分布で真ん中の出現頻度が多くなっています。
参考
numpyとmatplotlibで簡単ヒストグラム作図 - よちよちpython

配列を結合する


縦に結合する場合 : np.vstack*1
横に結合する場合 : np.hstack*2

縦の場合は列数が、横の場合は行数が一致していなければ結合できない。

j = np.random.randint(0,6,6).reshape(3,2)
print(j)

print()

k = np.random.randint(6,11,6).reshape(3,2)
print(k)

print()

# jとkを縦に結合
l = np.vstack((j,k))
print("縦に結合\n",l)

print()

# jとkを横に結合
m = np.hstack((j,k))
print("横に結合\n",m)
[[5 1]
 [3 0]
 [5 0]]

[[ 7  8]
 [10  8]
 [ 6  9]]

縦に結合
 [[ 5  1]
 [ 3  0]
 [ 5  0]
 [ 7  8]
 [10  8]
 [ 6  9]]

横に結合
 [[ 5  1  7  8]
 [ 3  0 10  8]
 [ 5  0  6  9]]

eye 単位行列を生成


左上がりの対角線上が1、その他が0の値の正方行列を単位行列という。

# 3行3列の正方行列
np.eye(3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

linspace 指定した範囲を等分割した配列を生成


# 1から10までを10等分した配列をつくる
np.linspace(1,10,10)
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])

転置行列


行と列を入れ替えるのを転置という。
Excelでいうところの右クリックで「形式選択 → 行列を入れ替える」ボタン。
ある行列Xに対してX.Tとすると転置される。

X = np.arange(10).reshape(2,5)
print("転置前\n",X)
print()
print("転置前\n",X.T)
転置前
 [[0 1 2 3 4]
 [5 6 7 8 9]]

転置前
 [[0 5]
 [1 6]
 [2 7]
 [3 8]
 [4 9]]

四則演算


配列に対して数値の四則演算ができる。すべての要素に対して行われる。

足し算

print("元の配列\n",np.arange(1,11))
print("元に5を足した\n",np.arange(1,11) + 5)
元の配列
 [ 1  2  3  4  5  6  7  8  9 10]
元に5を足した
 [ 6  7  8  9 10 11 12 13 14 15]
print("元の配列\n",np.arange(1,11).reshape(5,2))
print("元に5を足した\n",np.arange(1,11).reshape(5,2) + 5)
元の配列
 [[ 1  2]
 [ 3  4]
 [ 5  6]
 [ 7  8]
 [ 9 10]]
元に5を足した
 [[ 6  7]
 [ 8  9]
 [10 11]
 [12 13]
 [14 15]]

引き算

print("元の配列\n",np.arange(5))
print("元から6を引いた\n",np.arange(5) - 6)
元の配列
 [0 1 2 3 4]
元から6を引いた
 [-6 -5 -4 -3 -2]

掛け算

print("元の配列\n",np.arange(1,10).reshape(3,3))
print("元に2を掛けた\n",np.arange(1,10).reshape(3,3) * 2)
元の配列
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
元に2を掛けた
 [[ 2  4  6]
 [ 8 10 12]
 [14 16 18]]

割り算

W = np.random.randint(1,10,5)
print("元の配列\n",W)
print("元を2で割った\n",W / 2)
元の配列
 [7 9 9 2 7]
元を2で割った
 [3.5 4.5 4.5 1.  3.5]

累乗

A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元を2乗\n",A ** 2)
元の配列
 [9 5 6 1 1]
元を2乗
 [81 25 36  1  1]
# 形を変えても同じ
print("元の配列\n",A.reshape(-1,1))
print("元の平方根\n",np.sqrt(A.reshape(-1,1)))
元の配列
 [[9]
 [5]
 [6]
 [1]
 [1]]
元の平方根
 [[3.        ]
 [2.23606798]
 [2.44948974]
 [1.        ]
 [1.        ]]

指数関数

A = np.random.randint(1,10,5)
print("元の配列\n",A.reshape(-1,1))
print("eを元の数で累乗\n",np.exp(A.reshape(-1,1)))
元の配列
 [[9]
 [5]
 [4]
 [1]
 [4]]
eを元の数で累乗
 [[8.10308393e+03]
 [1.48413159e+02]
 [5.45981500e+01]
 [2.71828183e+00]
 [5.45981500e+01]]

対数関数

A = np.random.randint(1,10,5)
print("元の配列\n",A.reshape(-1,1))
print("底をeとする元の数+1の対数\n",np.log1p(A.reshape(-1,1)))
元の配列
 [[6]
 [1]
 [3]
 [4]
 [9]]
底をeとする元の数+1の対数
 [[1.94591015]
 [0.69314718]
 [1.38629436]
 [1.60943791]
 [2.30258509]]
print("元の配列\n",np.array([100,1000]))
print("元の配列の底10のlog\n",np.log10(np.array([100,1000])))
元の配列
 [ 100 1000]
元の配列の底10のlog
 [2. 3.]

統計量


合計

A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列の合計\n",np.sum(A))
元の配列
 [2 4 4 4 8]
元の配列の合計
 22
# 形を変えても同じ
print("元の配列\n",A.reshape(-1,1))
print("元の配列の合計\n",np.sum(A.reshape(-1,1)))
元の配列
 [[2]
 [4]
 [4]
 [4]
 [8]]
元の配列の合計
 22

平均


A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列の合計\n",np.sum(A))
print("元の配列の平均\n",np.mean(A))
元の配列
 [1 5 6 6 7]
元の配列の合計
 25
元の配列の平均
 5.0

分散


分散はデータのばらつき具合を示す指標。

A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列の合計\n",np.sum(A))
print("元の配列の平均\n",np.mean(A))
print("元の配列の分散\n",np.var(A))
元の配列
 [4 8 6 6 1]
元の配列の合計
 25
元の配列の平均
 5.0
元の配列の分散
 5.6

標準偏差


分散の平方根。分散の単位がデータの2乗の単位になっているのでルートを取って戻したもの。

A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列の合計\n",np.sum(A))
print("元の配列の平均\n",np.mean(A))
print("元の配列の分散\n",np.var(A))
print("元の配列の標準偏差\n",np.std(A))
元の配列
 [2 6 4 1 6]
元の配列の合計
 19
元の配列の平均
 3.8
元の配列の分散
 4.16
元の配列の標準偏差
 2.039607805437114

最大値・最小値 とそれぞれのインデックス


A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列の最大値\n",np.max(A))
print("元の配列の最小値\n",np.min(A))
print("元の配列の最大値のインデックス\n",np.argmax(A))
print("元の配列の最小値のインデックス\n",np.argmin(A))
元の配列
 [1 4 3 1 8]
元の配列の最大値
 8
元の配列の最小値
 1
元の配列の最大値のインデックス
 4
元の配列の最小値のインデックス
 0

データ型


データ型の確認

A = np.random.randint(1,10,5)
print("元の配列\n",A)
print("元の配列のデータ型\n",A.dtype)
元の配列
 [3 4 4 3 4]
元の配列のデータ型
 int32

データ型を指定して配列を生成

A = np.random.randint(1,10,5,dtype='int64')
print("元の配列\n",A)
print("元の配列のデータ型\n",A.dtype)
元の配列
 [5 2 3 2 5]
元の配列のデータ型
 int64

四捨五入


A = np.random.rand(5)
print("元の配列\n",A)
print("元の配列を小数第2位まで\n",np.round(A,2))
元の配列
 [0.80619399 0.70388858 0.10022689 0.91948261 0.7142413 ]
元の配列を小数第2位まで
 [0.81 0.7  0.1  0.92 0.71]

欠損値の処理


numpyでも出来ますが、pandasで処理する方が多いようなので飛ばします。



とりあえずこんなもんで。今回は以上です。
追記
何ヵ所か訂正しました。他にも間違ってる所あるかも。

*1:配列1、配列2、・・・

*2:配列1、配列2、・・・