Python入門トップページ


目次

  1. NumPy とは
  2. Python リストの場合
  3. NumPy の使用例
    1. 1次元配列(ベクトル)
    2. 2次元配列(行列)
    3. 要素の取り出し
    4. ベクトルと行列の変換
    5. NumPy のデータ型
    6. NumPy 配列の作成と操作
    7. ユニバーサル関数と集約関数
      1. ユニバーサル関数
      2. 集約関数
      3. 欠損値に対する集約関数
    8. NumPy 配列のソート
    9. NumPy の構造化配列
    10. NumPy 配列の保存と読み込み
  4. NumPy で線形代数
  5. NumPy の乱数生成

NumPy

NumPy の使用例

ユニバーサル関数

NumPy のユニバーサル関数 (ufunc) は配列の個々の要素に対する計算の繰り返しを効率的に行うことができる関数です.Python のリスト形式よりも高速に実行されます.

目次に戻る

平方根

まずは,NumPy 配列の平方根np.sqrt() で求めてみよう.

import numpy as np
x = np.array([1, 2, 3, 4, 5])
print(np.sqrt(x))
[1.         1.41421356 1.73205081 2.         2.236067

ユニバーサル関数が高速に実行できることを確認してみよう.まず,1千万までの平方根であってもすぐに実行できるはずです.

x = np.arange(10000000)
y = np.sqrt(x)
y[4084441]
2021.0

マジックコマンドを使って実行時間を計測してみよう.Juypter Notebook ではセルの先頭に %%time を入力すると,そのセルの実行時間を計測できます.その他にもある一行の時間を計測する %time や,同じ処理を何度か繰り返して時間を計測する %timeit%%timeit などがあります.

%%time
x = np.arange(10000000)
y = np.sqrt(x)
y[4084441]
CPU times: user 20.9 ms, sys: 14.1 ms, total: 34.9 ms
Wall time: 36.3 ms

同じ処理を Python のリストで記述して実行してみよう.math ライブラリの math.sqrt() を使う場合は,繰り返しの処理が必要になります.C言語などでは次のような書き方は標準的であるが,Python リストでは(NumPyと比較して)処理に時間を要するはずです.

import math
%%time
n = 10000000
y = [0] * n
for i in range(0, n):
    y[i] = math.sqrt(i)
print(y[4084441])
2021.0
CPU times: user 1.02 s, sys: 58 ms, total: 1.08 s
Wall time: 1.08 s

さらに同じ処理を Python の内包表記で記述して実行してみよう.リストの繰り返しに比べて高速に処理できますが,NumPy には遠く及びません.

%%time
n = 10000000
results = [math.sqrt(i) for i in range(0, n)] # 内包表記
print(results[4084441])
2021.0
CPU times: user 639 ms, sys: 39.8 ms, total: 679 ms
Wall time: 683 ms

目次に戻る

絶対値

絶対値np.absolute() です.

x = np.array([1, -2, 3, -4, 5])
print(np.absolute(x))
[1 2 3 4 5]

np.absolute() の別名として np.abs() も利用可能です.

print(np.abs(x))
[1 2 3 4 5]

目次に戻る

指数関数と対数関数

指数関数np.exp() があるが,これ以外にも \(2^x\) を計算する np.exp2() や,\(x^y\) を計算する np.power(x, y) などもある.

x = np.array([1, 2, 3, 4, 5])
print(np.exp(x))
print(np.exp2(x))
print(np.power(3, x))
[  2.71828183   7.3890561   20.08553692  54.59815003 148.4131591 ]
[ 2.  4.  8. 16. 32.]
[  3   9  27  81 243]

自然対数np.log(),底が2と10の対数はそれぞれnp.log2(),とnp.log10() である.

x = np.array([1, 2, 4, 10, 100])
print(np.log(x))
print(np.log2(x))
print(np.log10(x))
[0.         0.69314718 1.38629436 2.30258509 4.60517019]
[0.         1.         2.         3.32192809 6.64385619]
[0.         0.30103    0.60205999 1.         2.        ]

目次に戻る

三角関数

三角関数np.sin()np.cos()np.tan() などが利用できます.

# 0°, 30°, 45°, 60°, 90°
x = np.array([0, np.pi/3, np.pi/2, 2*np.pi/3, np.pi])
print(x)
print(np.sin(x))
print(np.cos(x))
print(np.tan(x))
[0.         1.04719755 1.57079633 2.0943951  3.14159265]
[0.00000000e+00 8.66025404e-01 1.00000000e+00 8.66025404e-01
 1.22464680e-16]
[ 1.000000e+00  5.000000e-01  6.123234e-17 -5.000000e-01 -1.000000e+00]
[ 0.00000000e+00  1.73205081e+00  1.63312394e+16 -1.73205081e+00
 -1.22464680e-16]

目次に戻る

最大公約数と最小公倍数

最大公約数 (Greatest Common Divisor)最小公倍数 (Lowest Common Multiple) 返す関数 np.gcd()np.lcm() もある.

198と252の最大公約数np.gcd(198, 252)
18
198と252の最小公倍数np.lcm(198, 252)
2772

目次に戻る

集約関数

合計

合計はデータの値 \(x_i\) をすべて足し合わせたもので,これは np.sum() で簡単に求めることができる.

x = np.array([1, 2, 3, 4, 5])
np.sum(x)
15

これは数学的には次のように書くことができる. \begin{eqnarray} \sum_{i=1}^{n}x_i = x_1 + x_2 + \cdots + x_n \end{eqnarray}

やはりここでも NumPy が高速に実行できることを試しておこう.1千万回の繰り返し処理は次のように効率的に実行できるはずです.

%%time
x = np.arange(1, 10000000)
print(np.sum(x))
49999995000000
CPU times: user 15.8 ms, sys: 8.14 ms, total: 24 ms
Wall time: 23.3 ms

同じ計算を Python のリストで記述すると次のようになりますが,実行に少々時間を要するはずで,NumPy が数十倍高速です.

%%time
s = 0
for x in range(0, 10000000):
    s += x
print(s)
49999995000000
CPU times: user 653 ms, sys: 9.73 ms, total: 663 ms
Wall time: 664 ms

行列に対して合計を求めることもできるが,その動作には注意が必要かもしれません.オプショナル引数を指定しなければ,すべての要素の合計を取得します.

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(X)
print(np.sum(X))
[[1 2 3]
 [4 5 6]
 [7 8 9]]
45

オプショナル引数で axis=0 を指定すると,縦方向の合計(列ごとの合計)を取得できます.

print(np.sum(X, axis=0))
[12 15 18]

逆に axis=1 を指定すると,横方向の合計(行ごとの合計)を取得できます.

print(np.sum(X, axis=1))
[ 6 15 24]

目次に戻る

平均

平均は np.mean()np.average() です.

x = np.array([1, 2, 3, 4, 5])
np.mean(x)
3.0
x = np.array([1, 2, 3, 4, 5])
np.average(x)
3.0

もちろん,平均の数学的な定義は次のとおりです. \begin{eqnarray} \bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_i \end{eqnarray}

合計のときと同様に,行列に対しても平均を求めることもできる.

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.mean(X)
5.0
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.average(X)
5.0

オプショナル引数で axis=0 を指定すると,縦方向の平均(列ごとの平均)を取得できます.

np.mean(X, axis=0)
array([4., 5., 6.])
np.average(X, axis=0)
array([4., 5., 6.])

オプショナル引数で axis=1 を指定すると,横方向の平均(行ごとの平均)を取得できます.

np.mean(X, axis=1)
array([2., 5., 8.])
np.average(X, axis=1)
array([2., 5., 8.])

なお, np.mean()np.average() の違いのひとつは,np.average() が重み付け平均を求められることです.

x = np.array([1, 2, 3, 4, 5])
w = np.array([0.1, 0.4, 0.3, 0.1, 0.1])
np.average(x, weights = w)
2.6999999999999997

目次に戻る

最大・最小・メディアン

要素の最大や最小を求める関数もあります.

x = np.array([2, 3, 1, 5, 4])
print(np.max(x))
print(np.min(x))
5
1

さらに,最大値や最小値のインデックスを返す関数もあります.

x = np.array([2, 3, 1, 5, 4])
print(np.argmax(x))
print(np.argmin(x))
3
2

ただし,最大値や最小値が複数あっても見つかった先頭のインデックスだけが返ってくることに注意して下さい.

x = np.array([2, 3, 1, 5, 4, 5, 1])
print(np.argmax(x))
print(np.argmin(x))
3
2

なお,データ \begin{eqnarray} x_1,~x_2,\cdots,~x_n \end{eqnarray} を小さい順に並び替えて \begin{eqnarray} x_{(1)} \leq x_{(2)} \leq \cdots \leq x_{(n)} \end{eqnarray} のように書くとき,\(x_{(i)}\)\(i\) 番目の順序統計量という.つまり,最小値は \(x_{(1)}\) であり,最大値は \(x_{(n)}\) である.さらに \(x_{(1)} \leq x_{(2)} \leq \cdots \leq x_{(n)}\) の中央をメディアン(中央値)という.データ数 \(n\) が奇数のときは次のようになる.

x = np.array([2, 3, 1, 5, 4])
np.median(x)
3.0

一方でデータ数 \(n\) が偶数のときは,中央の2つの値の平均になる.

x = np.array([2, 3, 1, 5, 4, 1])
np.median(x)
2.5

目次に戻る

分散

分散np.var() を使って求めることができる.このとき,特に引数を指定しなければ,標本分散を求めることに注意してください.つまり,データ数を \( n \) とすると,\( n \) で割った結果になります(これは Pandas の std( ) と異なります).メソッド np.var() には ddof 引数を指定することができる.ddof は Delta Degrees of Freedom であり,\( n - ddof \) で割ることを意味する.引数省略時は ddof = 0 であり,ddof = 1 を指定すると,不偏分散(\( n - 1\) で割った結果)になる.

x = np.array([1, 2, 3, 4, 5])
print("標本分散 : ", np.var(x))
print("標本分散 : ", np.var(x, ddof=0))
print("不偏分散 : ", np.var(x, ddof=1))
標本分散 :  2.0
標本分散 :  2.0
不偏分散 :  2.5

なお,分散(標本分散)の定義は \begin{eqnarray} S^2 = \frac{1}{n}\sum_{i=1}^{n}\left(x_i - \bar{x}\right)^2 \end{eqnarray} で,不偏分散の定義は \begin{eqnarray} U^2 = \frac{1}{n-1}\sum_{i=1}^{n}\left(x_i - \bar{x}\right)^2 \end{eqnarray} です.

目次に戻る

標準偏差

標準偏差np.std() で求めることができます.分散のときと同様ddof の指定には注意が必要です.

x = np.array([1, 2, 3, 4, 5])
print("標本の標準偏差 : ", np.std(x))
print("標本の標準偏差 : ", np.std(x, ddof=0))
print("母集団の標準偏差 : ", np.std(x, ddof=1))
標本の標準偏差 :  1.4142135623730951
標本の標準偏差 :  1.4142135623730951
母集団の標準偏差 :  1.5811388300841898

2次元の5組のデータを定義して標準偏差を求めるには次のようにすると良い.

X = np.array([[1,8],[2,22],[3,28],[4,35],[5,36]])
print(X)
print(np.std(X, axis=0))
[[ 1  8]
 [ 2 22]
 [ 3 28]
 [ 4 35]
 [ 5 36]]
[ 1.41421356 10.24499878]

目次に戻る

相関係数

相関係数を求めるには np.corrcoef() を用いると良い.このとき,データ行列を T メソッドや transpose() メソッドで転置して渡す必要がある.

X = np.array([[1,8],[2,22],[3,28],[4,35],[5,36]])
print(X)
print(np.corrcoef(X.T))
[[ 1  8]
 [ 2 22]
 [ 3 28]
 [ 4 35]
 [ 5 36]]
[[1.         0.95247191]
 [0.95247191 1.        ]]
print(np.corrcoef(np.transpose(X)))
[[1.         0.95247191]
 [0.95247191 1.        ]]

あるいは,データ行列を転置せずに,rowvar=False によって指定することもできる(デフォルトは rowvar=True).

print(np.corrcoef(X, rowvar=False))
[[1.         0.95247191]
 [0.95247191 1.        ]]

目次に戻る

共分散

共分散np.cov() で計算できる.次のようにすると,分散共分散行列を一気に計算できる.このとき,分散の np.var() 関数とは異なり,ddof = 1 がデフォルトであることに注意が必要です.

母集団の分散・共分散X = np.array([[1,8],[2,22],[3,28],[4,35],[5,36]])
print(np.cov(X.T))
[[  2.5   17.25]
 [ 17.25 131.2 ]]
母集団の分散・共分散print(np.cov(X.T, ddof = 1))
[[  2.5   17.25]
 [ 17.25 131.2 ]]
標本分散・共分散print(np.cov(X.T, ddof = 0))
[[  2.    13.8 ]
 [ 13.8  104.96]]

目次に戻る

欠損値に対する集約関数

欠損値(つまり観測できない値)は np.nan で表現します.なお,np.nan による初期化はここを参照してください.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
[nan nan nan  4.  5.]

通常の集約関数である np.sum()np.mean(), np.average() に欠損値を含む NumPy 配列を与えると結果は nan になります.

print(x)
print(np.sum(x))
print(np.mean(x))
print(np.average(x))
[nan nan nan  4.  5.]
nan
nan
nan

欠損値を含む NumPy 配列の合計を求めるには np.nansum() を使います.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
print(np.nansum(x))
[nan nan nan  4.  5.]
9.0

行列の場合も同様です.

X = np.array([[1, 2, 3], [4, 5, np.nan], [7, 8, np.nan]])
print(X)
print("すべての合計 : ", np.nansum(X))
print("列ごとの合計 : ", np.nansum(X, axis=0))
print("行ごとの合計 : ", np.nansum(X, axis=1))
[[ 1.  2.  3.]
 [ 4.  5. nan]
 [ 7.  8. nan]]
すべての合計 :  30.0
列ごとの合計 :  [12. 15.  3.]
行ごとの合計 :  [ 6.  9. 15.]

欠損値を含む NumPy 配列の平均を求めるには np.nanmean() を使います.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
print(np.nanmean(x))
[nan nan nan  4.  5.]
4.5
X = np.array([[1, 2, 3], [4, 5, np.nan], [7, 8, np.nan]])
print(X)
print("すべての平均 : ", np.nanmean(X))
print("列ごとの平均 : ", np.nanmean(X, axis=0))
print("行ごとの平均 : ", np.nanmean(X, axis=1))
[[ 1.  2.  3.]
 [ 4.  5. nan]
 [ 7.  8. nan]]
すべての平均 :  4.285714285714286
列ごとの平均 :  [4. 5. 3.]
行ごとの平均 :  [2.  4.5 7.5]

当然ながら欠損値を 0 で表現してしまうと異なる結果になることに注意してください.

x = np.array([0, 0, 0, 4, 5])
print(x)
print(np.nanmean(x))
[0 0 0 4 5]
1.8
X = np.array([[1, 2, 3], [4, 5, 0], [7, 8, 0]])
print(X)
print("すべての平均 : ", np.nanmean(X))
print("列ごとの平均 : ", np.nanmean(X, axis=0))
print("行ごとの平均 : ", np.nanmean(X, axis=1))
[[1 2 3]
 [4 5 0]
 [7 8 0]]
すべての平均 :  3.3333333333333335
列ごとの平均 :  [4. 5. 1.]
行ごとの平均 :  [2. 3. 5.]

欠損値であるかどうかは np.isnan() で判断できます.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
print(np.isnan(x))
[nan nan nan  4.  5.]
[ True  True  True False False]
X = np.array([[1, 2, 3], [4, 5, np.nan], [7, 8, np.nan]])
print(X)
print(np.isnan(X))
[[ 1.  2.  3.]
 [ 4.  5. nan]
 [ 7.  8. nan]]
[[False False False]
 [False False  True]
 [False False  True]]

欠損値の個数をカウントするには np.count_nonzero()np.isnan() を組み合わせればよいでしょう.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
print(np.count_nonzero(np.isnan(x)))
[nan nan nan  4.  5.]
3
X = np.array([[1, 2, 3], [4, 5, np.nan], [7, 8, np.nan]])
print(X)
print("欠損値の個数 : ", np.count_nonzero(np.isnan(X)))
print("列ごとの欠損値の個数 : ", np.count_nonzero(np.isnan(X), axis=0))
print("行ごとの欠損値の個数 : ", np.count_nonzero(np.isnan(X), axis=1))
[[ 1.  2.  3.]
 [ 4.  5. nan]
 [ 7.  8. nan]]
欠損値の個数 :  2
列ごとの欠損値の個数 :  [0 0 2]
行ごとの欠損値の個数 :  [0 1 1]

欠損値でない有効なデータ数は次のようにして求めることができます.なお,~ は否定を意味します.

x = np.array([np.nan, np.nan, np.nan, 4, 5])
print(x)
print(np.count_nonzero(~np.isnan(x)))
[nan nan nan  4.  5.]
2
X = np.array([[1, 2, 3], [4, 5, np.nan], [7, 8, np.nan]])
print(X)
print("データ数 : ", np.count_nonzero(~np.isnan(X)))
print("列ごとのデータ数 : ", np.count_nonzero(~np.isnan(X), axis=0))
print("行ごとのデータ数数 : ", np.count_nonzero(~np.isnan(X), axis=1))
[[ 1.  2.  3.]
 [ 4.  5. nan]
 [ 7.  8. nan]]
データ数 :  7
列ごとのデータ数 :  [3 3 1]
行ごとのデータ数数 :  [3 2 2]

目次に戻る