NumPyの使い方(9) 並べ替えsortなど
NumPyのsortについて。
sort()メソッド | 内部で配列をソート。返り値はNone |
np.sort()関数 | ソート済み配列を返す。元の配列は保存。 |
# coding: utf-8 import numpy as np np.random.seed(1) a1 = np.random.randint(0, 100, 10) print(a1) # [37 12 72 9 75 5 79 64 16 1] a1.sort() print(a1) # [ 1 5 9 12 16 37 64 72 75 79] print(a1.sort()) # None np.random.seed(1) a1 = np.random.randint(0, 100, 10) print(a1) # [37 12 72 9 75 5 79 64 16 1] a2 = np.sort(a1) print(a1) # [37 12 72 9 75 5 79 64 16 1] 元配列は保存 print(a2) # [ 1 5 9 12 16 37 64 72 75 79]
axisオプション
どのaxisでソートするかのオプション
列内でのソートはaxis=0とするか、あるいは単に0
行内でのソートはaxis=1とするか、あるいは単に1
デフォルトは-1で、最後の軸(2次元ならaxis=1)でソート
axisをNoneとすると1次元配列に平坦化。
np.random.seed(1) a3 = np.random.randint(0, 100, (3, 6)) print(a3) # [[37 12 72 9 75 5] # [79 64 16 1 76 71] # [ 6 25 50 20 18 84]] # axis=0 print(np.sort(a3, 0)) # [[ 6 12 16 1 18 5] # [37 25 50 9 75 71] # [79 64 72 20 76 84]] # axis=1 print(np.sort(a3, 1)) # [[ 5 9 12 37 72 75] # [ 1 16 64 71 76 79] # [ 6 18 20 25 50 84]] # axis=-1 print(np.sort(a3, axis=-1)) # [[ 5 9 12 37 72 75] # [ 1 16 64 71 76 79] # [ 6 18 20 25 50 84]] # axisのデフォルトは-1 print(np.sort(a3)) # [[ 5 9 12 37 72 75] # [ 1 16 64 71 76 79] # [ 6 18 20 25 50 84]] # axis=None print(np.sort(a3, axis=None)) # [ 1 5 6 ..., 76 79 84]
kindオプション
ソートに利用するアルゴリズムを選ぶ。デフォルトは'quicksort'
kind | speed | worst case | stable |
'quicksort' | 1 | O(n^2) | × |
'mergesort' | 2 | O(n*log(n) | ○ |
'heapsort' | 3 | O(n*log(n) | × |
stable:等しい要素の並び順を保存
orderオプション
フィールドをもつ要素のばあい、キーとするフィールドを指定できる。
dtype = [("name", 'U10'), ("height", int), ("age", int)] values = [("香川", 165, 48), ("秋山", 175, 32), ("長田", 170, 27)] a1 = np.array(values, dtype=dtype) print(a1) # [('香川', 165, 48) ('秋山', 175, 32) ('長田', 170, 27)] print(a1.dtype) # [('name', '<U10'), ('height', '<i4'), ('age', '<i4')] print(np.sort(a1, order="age")) # [('長田', 170, 27) ('秋山', 175, 32) ('香川', 165, 48)]
逆順ソート
降順配列にするには
array[::-1] 行を逆順にする array[:, ::-1] 列を逆順にする
などのスライスを使う。
np.random.seed(1) a3 = np.random.randint(0, 100, (3, 6)) print(a3) # [[37 12 72 9 75 5] # [79 64 16 1 76 71] # [ 6 25 50 20 18 84]] # axis=0の逆順ソート print(np.sort(a3, 0)[::-1]) # [[79 64 72 20 76 84] # [37 25 50 9 75 71] # [ 6 12 16 1 18 5]] # axis=1の逆順ソート print(np.sort(a3, 1)[:, ::-1]) # [[75 72 37 12 9 5] # [79 76 71 64 16 1] # [84 50 25 20 18 6]]
argsort()
argsort()はソートした場合のindex番号の配列を取得する。
1次元配列の場合
a1 = np.array([37, 12, 72, 9, 75, 5]) # argsort()...ソートした場合のindex番号を配列にする indexer = a1.argsort() print(indexer) # [5 3 1 0 2 4] a1[5] < a1[3] < a1[1] < a1[0] ... print(a1[indexer]) # [ 5 9 12 37 72 75]
2次元配列の場合は、argsort()でキーとする行の指定ができる。
np.random.seed(1) a1 = np.random.randint(0, 100, (3, 6)) print(a1) # [[37 12 72 9 75 5] # [79 64 16 1 76 71] # [ 6 25 50 20 18 84]] #0行目をキーとしてソート print(a1[:, a1[0].argsort()]) # [[ 5 9 12 37 72 75] # [71 1 64 79 16 76] # [84 20 25 6 50 18]] #1行目をキーとしてソート print(a1[:, a1[1].argsort()]) # [[ 9 72 12 5 75 37] # [ 1 16 64 71 76 79] # [20 50 25 84 18 6]]
argsortにもaxis, kind, orderオプションがある。
lexsort()
複数のキーで間接ソートするときに使う。
lexsort()もargsort()同様にインデックスの配列を得る。
last_name = ["はつね", "さとう", "しまだ", "さとう"] first_name = ["みく", "めぐみ","なおみ", "かな"] indexer = np.lexsort((first_name, last_name)) print(indexer) # [3 1 2 0 ] name =[(last_name[i], first_name[i]) for i in indexer] print(name) # [('さとう', 'かな'), ('さとう', 'めぐみ'), ('しまだ', 'なおみ'), ('はつね', 'みく')]
searchsorted()
ソートされた配列内で二分探索をし、挿入される位置を返す。
a1 = np.array([0, 5, 10, 15, 20]) print(a1.searchsorted(7)) # 2 print(a1.searchsorted(18)) # 4 print(a1.searchsorted(5)) # 1 print(a1.searchsorted([7, 18, 5])) # [2 4 1]
sideオプション:同じ値が複数あるとき、
'left' もっとも左のインデックス。(デフォルト)
'right' もっとも右のインデックス
a1 = np.array([1, 1, 1, 2, 2, 2]) print(a1.searchsorted([1, 2])) # [0 3] print(a1.searchsorted([1, 2], side="right")) # [3 6]
np.unique()
1次元の配列から重複を取り除き、ソートした配列を返す。
python標準のsorted(set(seq))と同様の働き。
a1 = np.array([1, 3, 2, 0, 1, 2, 2, 1]) print(np.unique(a1)) # [0 1 2 3]
return_index=Trueとすると初回のインデックスを返す。
return_counts=Trueとすると、重複している回数を返す。
print(np.unique(a1, return_index=True)) # (array([0, 1, 2, 3]), array([3, 0, 2, 1], dtype=int32)) print(np.unique(a1, return_counts=True)) # (array([0, 1, 2, 3]), array([1, 3, 3, 1]))
NumPyの使い方(8) 真偽値
NumPyの真偽値について。
・TrueとFalse
python標準のTrue, Falseでは
False | 数値のゼロ、空文字列、空配列、False |
True | 他すべて |
TrueがFalse以外ということは、
負の値、NaN、infもTrueとなる。
・条件がTrueとなる要素の確認
import numpy as np a1 = np.arange(8).reshape(2, 4) print(a1) # [[0 1 2 3] # [4 5 6 7]] # 2の倍数かどうか(1) print(a1 % 2 == 0) # [[ True False True False] # [ True False True False]] # 2の倍数かどうか(2) print(np.mod(a1, 2) == 0) # [[ True False True False] # [ True False True False]] # 2の倍数の個数 print((a1 % 2 == 0).sum()) #4 a1 = np.arange(20) print(a1 > 10) # [False False False ..., True True True] # 条件がTrueとなる要素の個数 print((a1 > 10).sum()) # 9 # 条件がTrueとなる値の和 print(a1[a1 >10].sum()) # 135 a2 = np.array([1, 2, 3, 4]) a3 = np.array([1, 1, 4, 4]) print(a2 == a3) # [ True False False True]
anyとall
any 1つでもTrueだとTrueを返す。
all すべてがTrueのときのみTrueを返す。
a1 = np.array([[0, 0, 0], [1, 1, 1], [1, 1, 1]]) print(a1) # [[0 0 0] # [1 1 1] # [1 1 1]] print(a1.any()) # True print(a1.all()) # False
any(), all()で軸を指定できる。次元削減になる。
axis=0 列方向に次元削減
axis=1 行方向に次元削減
print(a1.all(axis=0)) # [False False False] print(a1.all(axis=1)) # [False True True] print(a1.any(axis=0)) # [ True True True] print(a1.any(axis=1)) # [False True True]
boolと条件
真偽値boolや条件などについて。
真偽値bool
・True, Falseの2つの値のみ。
・True,=1, False=0で変更できない。(python2では変更できた)
True = 0 # SyntaxError: can't assign to keyword
False | 数値のゼロ、空文字列、空配列、False |
True | 他すべて |
・必要になったら、True=1, False=0の数値として扱われる。
print(True) # True print(True + True + 1) # 3
・条件分岐や繰り返しに使える。
x = 5 if x > 0: print(x > 0) # True if True: print(True) # True if 1: print(x) # 5 if 2: print(x) # 5 if -1: print(x) # 5
# and, orは短絡 (short-circuit)演算子 # 左から右へ評価し、結果が確定した時点で評価を止める。 # 戻り値は最後に評価された引数 # 最後の引数3を評価するまで確定しない。 print(1 and 2 and 3) # 3 # (1 and 0 and) の時点で評価がFalse(=0)と確定するので3は評価されない。 print(1 and 0 and 3) # 0 # (0 and)の時点で評価がFalseと確定 print(0 and 1 and 3) # 0 # 最後の(3/0)は評価されない print(1 and 0 and (3/0)) # 0 print(0 or 1 or 2 or 3) # 1 print(0 or 0 or 10 or 20) # 10 string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' non_null = string1 or string2 or string3 print(non_null) # 'Trondheim'
・3項演算子のような書き方ができる。
print(1==1 and "T" or "F") # T print(1==0 and "T" or "F") # F
NumPyの使い方(7) 統計関数、最大・最小
NumPyの統計関数の使い方
ndarray配列全体、あるいは特定の軸についての統計処理をする。
sumやmeanなどの集計処理は次元削減になる。
axis=0 縦方向にreduce
axis=1 横方向にredece
sum | 和。 長さ0の配列では0を返す。 |
mean | 平均。長さ0の配列ではNaNを返す。 |
var | 分散 |
std | 標準偏差 |
min | 最小値, aminも同じ |
max | 最大値, amaxも同じ |
nanmin | NaNを無視した最小値 |
nanmax | NaNを無視した最大値 |
minimun | 2つの配列を比較して小さい要素、NaNを優先 |
maximun | 2つの配列を比較して大きい要素、NaNを優先 |
fmin | 2つの配列を比較して小さい要素、NaNを無視 |
fmax | 2つの配列を比較して小さい要素、NaNを無視 |
argmin | 最小値のインデックス |
argmax | 最大値のインデックス |
cumsum | 累積和 |
cumprod | 累積積 |
maximumやfmaxでは予めNaNを処理したほうがよさそう。
import numpy as np a1 = np.array([[1, 2], [3, 4]]) a2 = np.array([[0, 5], [2, 6]]) a3 = np.array([[0, 5], [2, np.nan]]) print(a1.max(axis=0)) # [3 4] print(a1.max(axis=1)) # [2 4] print(a1.max()) # 4 print(a1.argmax()) # 3 : 1次元配列にし、3番要素が最大値 print(a3.max()) # nan print(np.nanmax(a3)) # 5.0 print(np.maximum(a1, a2)) # [[1 5] # [3 6]] print(np.maximum(a1, a3)) # [[1 5] # [3 nan]] RuntimeWarning: invalid value encountered in maximum print(np.fmax(a1, a3)) # [[ 1. 5.] # [ 3. 4.]] RuntimeWarning: invalid value encountered in fmax
mean(), sum()
a1 = np.arange(12).reshape(3, 4) print(a1) # [[ 0 1 2 3] # [ 4 5 6 7] # [ 8 9 10 11]] # mean print(a1.mean()) # 4.0 メソッド print(np.mean(a1)) # 4.0 関数 # axisを指定すると1次元低い配列を取得 print(np.mean(a1, 0)) # [ 4. 5. 6. 7.] print(np.mean(a1, axis=0)) # [ 4. 5. 6. 7.] print(np.mean(a1, 1)) # [ 1.5 5.5 9.5] print(np.mean(a1, axis=1)) # [ 1.5 5.5 9.5] #print(np.mean(a1, 2)) IndexError: tuple index out of range print(a1.sum()) # 66 print(np.sum(a1)) # 66 print(a1.sum(0)) # [12 15 18 21] print(a1.sum(1)) # [ 6 22 38]
累積和cumsum(), 累積積cumprod()
a2 = np.ones((3, 4), dtype=int) a2*= 2 print(a2) # [[2 2 2 2] # [2 2 2 2] # [2 2 2 2]] print(np.cumsum(a2, 0)) # [[2 2 2 2] # [4 4 4 4] # [6 6 6 6]] print(np.cumsum(a2, 1)) # [[2 4 6 8] # [2 4 6 8] # [2 4 6 8]] print(np.cumprod(a2, 0)) # [[2 2 2 2] # [4 4 4 4] # [8 8 8 8]] print(np.cumprod(a2, 1)) # [[ 2 4 8 16] # [ 2 4 8 16] # [ 2 4 8 16]] print(np.cumsum(a2)) # [ 2 4 6 ..., 20 22 24]
NumPyの使い方(6) 条件制御
NumPyの条件制御について。
if~elseで条件分岐をするとき
a = 5 if a >= 0: b = 1 else: b = -1
と書けますが、これを3項演算子を使い以下のようにも書けます。
b = 1 if a >= 0 else -1
似たような動作を組み込みのリスト型で行うとき、
例えば、Trueならlist1から、そうでなければlist2から要素を選ぶとき、
list1 = [1, 2, 3, 4, 5] list2 = [11, 12, 13, 14, 15] condi = [True, False, True, False, True] list3 = [x if c else y for (c, x, y) in zip(condi, list1, list2)] print(list3) # [1, 12, 3, 14, 5]
NumPyではnp.where()を使って
np.where(条件, x, y)のように書きます。
x,yは配列または数値。数値ならブロードキャストされる。
a3 = np.where(condi, list1, list2) print(a3) # [ 1 12 3 14 5]
他の例も
a1 = np.arange(9).reshape(3, 3) print(a1) # [[0 1 2] # [3 4 5] # [6 7 8]] # 3未満の要素を0で置き換える(1) print(np.where(a1 < 3, 0, a1)) # [[0 0 0] # [3 4 5] # [6 7 8]] # 3未満の要素を0で置き換える(2) print(np.where(a1 >= 3, a1, 0)) # [[0 0 0] # [3 4 5] # [6 7 8]] # 正なら1、正でないなら-1にする print(np.where(a1 > 0, 1, -1)) # [[-1 1 1] # [ 1 1 1] # [ 1 1 1]]
np.where()の引数を条件の配列だけにすると、
Trueとなる要素のインデックスを取得。
a1 = np.arange(9).reshape(3, 3) print(a1) # [[0 1 2] # [3 4 5] # [6 7 8]] # Trueとなる要素のインデックスを取得 print(np.where(a1 > 5)) # (array([2, 2, 2], dtype=int32), array([0, 1, 2], dtype=int32)) # そのインデックスを利用して、要素を取り出せる。 print(a1[np.where(a1 > 5)]) # [6 7 8] # Trueとなる要素のインデックスを取得 print(np.where(a1 > 2)) # (array([1, 1, 1, 2, 2, 2], dtype=int32), array([0, 1, 2, 0, 1, 2], dtype=int32)) # そのインデックスを利用して、要素を取り出せる。 print(a1[np.where(a1 > 2)]) # [3 4 5 6 7 8] # 値の検索 target = [3, 4, 7] ix = np.in1d(a1.ravel(), target).reshape(a1.shape) print(ix) # [[False False False] # [ True True False] # [False True False]] # そのインデックスを表示 print(np.where(ix)) # (array([1, 1, 2], dtype=int32), array([0, 1, 1], dtype=int32))
2つの条件を同時に判断
cond1がTrueでcond2もTrue | 3 |
cond1がTrueでcond2はFalse | 2 |
cond1がFalseでcond2はTrue | 1 |
cond1がFalseでcond2もFalse | 0 |
としたい場合は、python標準だとif, elifなどを組み合わせて書けるが、
np.whereを3回ネストしても書ける
cond1 = np.array([True, True, False, False]) cond2 = np.array([True, False, True, False]) result = np.where(cond1 & cond2, 3, np.where(cond1, 2, np.where(cond2, 1, 0))) print(result) # [3 2 1 0]
True=1, False=0であることを使い次のようにも書ける。
result = cond1 * 2 + cond2 print(result) # [3 2 1 0]
NumPyの使い方(5) ベクトル演算とユニバーサル関数
ベクトル演算とユニバーサル関数について。
ベクトル演算
ndarrayと数値(スカラー)の計算は全要素対象で、forループを使わない。
ndarrayどうしの演算は対応する位置の要素どうしで計算。
[注意]ndarray * ndarrayは内積や外積にはならない。
import numpy as np a1 = np.arange(6).reshape(2, 3) print(a1) # [[0 1 2] # [3 4 5] print(a1 + 1) # [[1 2 3] # [4 5 6] print(a1 - 1) # [[-1 0 1] # [ 2 3 4] print(a1 * 2) # [[ 0 2 4] # [ 6 8 10] print(a1 / 10) # [[ 0. 0.1 0.2] # [ 0.3 0.4 0.5] print(a1 ** 2) # [[ 0 1 4] # [ 9 16 25] print(a1 +a1) # [[ 0 2 4] # [ 6 8 10]] print(a1 * a1) # [[ 0 1 4] # [ 9 16 25]]
ユニバーサル関数(ufunc, universal function)
ベクトル演算と同様に、要素ごとに計算される。
引数となる配列が1つのとき「単項ufunc」、2つのとき「2項ufunc」
例:
a1 = np.arange(6) a2 = np.arange(0, 60, 10) # 単項ユニバーサル関数の例 print(np.square(a1)) # [ 0 1 4 9 16 25] # 2項ユニバーサル関数の例 print(np.add(a1, a2)) # [ 0 11 22 33 44 55]
単項ufunc
abs | 整数、小数、複素数の絶対値 |
fabs | 整数、小数の絶対値。高速 |
sqrt | 平方根。 array**0.5と同等 |
square | 2乗。 array**2と同等 |
exp | eを底としたべき乗 |
log | 自然対数。底がe |
log10 | 常用対数。底が10 |
log2 | 底が2の対数 |
log1p | 1を加えてからの自然対数 |
sign | 要素の符号を返す。正:+1, 負:-1, ゼロ:0 |
ceil | 切り上げ |
floor | 切り捨て |
rint | dtypeそのままで、四捨五入 |
modf | 整数部分と小数部分の2つの配列を返す |
isnan | NaNかどうかの真偽値 |
isfinite | True:Nanでもinfでもない。False:NaNかinf |
isinf | infかどうか。 |
sin | 三角関数 |
cos | |
tan | |
sinh | 双曲線関数 |
cosh | |
tanh | |
arcsin | 逆三角関数 |
arcsos | |
arctan | |
arcsinh | 逆双曲線関数 |
arccosh | |
arctanh | |
logical_not | 論理否定。-arrayと同等 |
2項unfnc
add | 和 |
subtract | 差 |
multiply | 積 |
divide | 商 |
floor_divide | 商を左方向へ丸める。// |
power | 累乗 |
maximum | 最大値。NaNがあるときはNaNを選ぶ |
minimum | 最小値。NaNがあるときはNaNを選ぶ |
fmax | 最大値。NaNがあるときはNaNではない方 |
fmin | 最小値。NaNがあるときはNaNではない方 |
mod | 剰余 |
copysign | 1つめの配列の絶対値を2つめの配列の符号に。 |
greater | > |
greater_equal | >= |
less | < |
less_equal | <= |
equal | == |
not_equal | != |
logical_and | & 論理和 |
logical_or | |論理積 |
logical_xor | ^ 排他 |
・np.vectorize()を使って自作の関数をufuncにできる。
def myfunc(x, y): return x + y vfunc = np.vectorize(myfunc) print(vfunc(np.arange(5), 10)) # [10 11 12 13 14]
Project Euler
Project Euler problem #001 Multiples of 3 and 5
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
問題1:1000未満の3または5の倍数の和を求めよ。
forループを使っていくのが自然かな。
# forループとif limit = 1000 result = 0 for i in range(limit): if i%3 == 0 or i%5 ==0: result += i print(result)
ifの部分は3項演算子でも。
# forループと3項演算子 limit = 1000 result = 0 for i in range(limit): result += i if i%5 == 0 or i%3 ==0 else 0 print(result)
リスト内包表記を使うとシンプルに。
# リスト内包表記 limit = 1000 result = sum([x for x in range(limit) if x%3 == 0 or x%5 ==0 ]) print(result)
3つの等差数列に分けても。
# 等差数列のsum() limit = 1000 result = sum(range(0, limit, 3)) + sum(range(0, limit, 5)) - sum(range(0, limit, 15)) print(result)
等差数列の和の公式を使うと、コードは複雑になるけど処理時間は大幅短縮。
# 等差数列の和の公式 limit = 1000 def sum_multi(x): first = x last = (limit - 1) - (limit - 1) % x return int((first + last) * (last / x) / 2) result = sum_multi(3) + sum_multi(5) - sum_multi(15) print(result)
Project Euler problem #002 Even Fibonacci numbers
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
問題2:フィボナッチ数列で400万以下の偶数の項の和を求めよ。
def fib4(): a, b = 0, 1 result = 0 while b < 4*10**6: result += b if b % 2 ==0 else 0 a, b = b, a + b return result print(fib4()) # 4613732
フィボナッチ数列の偶数項は3項ごとに登場。
それと、フィボナッチ数列の生成をlambda式で1行にまとめてみると、
f = lambda x:int(((1 + 5**0.5) / 2)**i / 5**0.5 + 0.5) i, result = 3, 0 while f(i) < 4*10**6: result += f(i) i += 3 print(result)
Project Euler problem #003 Largest prime factor
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
問題3:600851475143の素因数のうち最大のものを求めよ。
# 素数のジェネレータ def gen2(start=2, stop=1000000): pr = max(1, start - 1) while True: while pr < stop: pr += 1 if all(pr%x != 0 for x in range(2, int(pr**0.5) + 1)): break yield pr # 素因数分解 def factorial(n): if n in [0, 1]: return [n] result = [] stop = int(n**0.5) + 1 g = gen2() pr = next(g) while pr < stop: if n % pr == 0: result.append(pr) n //= pr continue pr = next(g) if n > 1: result.append(n) return result print(max(factorial(600851475143))) # 6857
Project Euler problem #004 Largest palindrome product
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
問題:3桁の2つの数の積で作った回文のうち最大のものを求めよ。
forループを2重にして回文判定するとよさそう。
result = 0 for i in range(100, 1000): for j in range(i, 1000): if str(i * j) == str(i * j)[::-1]: result = max(result, i * j) print(result) # 906609
端が9と仮定すると、奇数×奇数なので、forループは奇数で。
result = 0 for i in range(901, 1000, 2): for j in range(i, 1000, 2): if str(i * j) == str(i * j)[::-1]: result = max(result, i * j) print(result)
積の1の位が9になるのは1×9、3×3、7×7、9×1なので、さらにループを減らせそう。
result = 0 pair9 = {1:8, 3:0, 7:0, 9:2} for i in range(901, 1000, 2): if i%10 in pair9: # 1の位が1,3, 7, 9なら incre = pair9[i%10] # 相手の1の位が決まる else: continue for j in range(i+incre, 1000, 10): # 1の位は固定なのでstep=10 if str(i * j) == str(i * j)[::-1]: result = max(result, i * j) print(result)