4-5.スライスを学ぼう
Numpyをより便利にしてくれるのがスライスという概念です.スライスの名前の通り,配列から一部を切り出す機能なのですが,非常に多様な活用方法があります.スライスを使わずしてnumpyを使いこなしているとは言えません.スライスをマスターすればPythonをより便利に活用出来ます.
(1) スライスの基本構造
とあるN行M列の配列をAとします.このときのスライス構造は以下のように表します.
とあるN行M列の配列をAとします.このときのスライス構造は以下のように表します.
A [始点:終点,始点:終点]
最初の始点:終点はX軸に関する範囲指定を示しています.例えば,始点を0,終点を5とすれば0番目から5番目までの要素が抜き出されます.これは始点を除いた:終点という表現と同じ意味を持ちます.次元の区切りは,で行います.また,重要な表現として始点と終点を指定しない:だけの表現はその軸に関してはなにもしないということを意味します.例えば,A[5,: ]は5行目の数値全てを取り出すということを意味しています.以下はこれらの実例です.
>>> A = np.array([[1,2,3,4,5],[6,7,8,9,10]]) #2行5列 >>> A array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]]) >>> A[0:1] #2軸目以降を省略して書くことも可能 [[1, 2, 3, 4, 5]] #[[ となっているので二次元
>>> A[0:1].shape >>> (1,5) #一次元配列ではなく,1行5列という二次元で認識される点に注意
>>> A[0] #1次元目の0行目を指定している [1, 2, 3, 4, 5] #こちらは一次元で出力される
>>> A[0,0:3] #0行目の0列目から左から数えて3番目(要素番号:2)まで切り出し, [1, 2, 3]
>>> A[:,0:3] #行方向のスライスをせず,そのまま. [[1, 2, 3], [6, 7, 8]]
>>> A[:,2:3] #列(横)方向に関して左から2つ目から左から3番目をスライス [[3], [8]]
>>> A[:,:] #スライスなし=そのまま array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]])
>>> A[1,:] #1行目全体をスライスする [[ 6, 7, 8, 9, 10]]
(2) Top / Worst Nを抜き出そう
このスライスが多用されるシーンの一つがnp.sort系とスライスを組み合わせたTop / Worst Nの抜き出しです.np.sortは前節で解説したように配列の要素を小さい順に並び替えるものでした.
このスライスが多用されるシーンの一つがnp.sort系とスライスを組み合わせたTop / Worst Nの抜き出しです.np.sortは前節で解説したように配列の要素を小さい順に並び替えるものでした.
np.sort(X, axis = None…)
Xの要素を小さい順に並び替えます.
Xの要素を小さい順に並び替えます.
np.argsort(X, axis = None…)
Xの要素のを小さい順に並び替えた場合の要素番号を取得します.
Xの要素のを小さい順に並び替えた場合の要素番号を取得します.
上記の説明に従えば,Top3を抜き出すにはsortされた配列から後ろ(右)から3個をスライスすれば良いことになります.
>>> A = [1,15,13,6,9] >>> np.sort(A) [1,6,9,13,15] >>> np.sort(A)[-3:]#右から数えて3つ目が始点 [9,13,15]
>>> np.argsort(A) [0,3,4,2,1] >>> np.argsort(A)[-3:0] [4,2,1]
(3) 行だけを飛び地で抜き出す
スライスは1行目,3行目,8行目のように連続しない行や列を各個抜き出し,新たな一まとまりの配列として出力することが出来ます.
スライスは1行目,3行目,8行目のように連続しない行や列を各個抜き出し,新たな一まとまりの配列として出力することが出来ます.
>>> A = np.random.randint(0,10,(5,5)) A = [[5, 8, 0, 9, 7], #人によって違います [5, 2, 5, 9, 7], [6, 4, 7, 6, 7], [5, 6, 9, 8, 8], [2, 4, 7, 4, 6]]
>>> lst = [0,2,4] >>> A[lst,:] #列方向はそのままに指定した行全体を抜き出す [[5, 8, 0, 9, 7], [6, 4, 7, 6, 7], [2, 4, 7, 4, 6]]
より具体的な例で言えば,列方向の合計が30を超える行だけを抜き出すといった使い方です.
>>> B = np.sum(A,axis=1) #列方向の合計値を出力 >>> B [29, 28, 30, 36, 23]
>>> lst = np.where(B >= 30) #30以上の要素番号を抜き出す >>> lst (array([2, 3], dtype=int32),)
>>> A[lst[0],:] #列方向の合計が30を超える行だけを抜き出す [[6, 4, 7, 6, 7], [5, 6, 9, 8, 8]]
(4) 既存の配列に挿入する
スライスは配列の切り出しだけではありません.既存の配列の一部に別の配列の一部を挿入することも出来ます.また,指定した範囲を特定の数値に置き換える事もできます.
スライスは配列の切り出しだけではありません.既存の配列の一部に別の配列の一部を挿入することも出来ます.また,指定した範囲を特定の数値に置き換える事もできます.
>>> lst = range(5) >>> A[0,:] = lst #0行目にlstを挿入する >>> A [[0., 1., 2., 3., 4.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]
#0行目がrange(5)に置き換わっている
>>> A[:,2] = lst #2列目にlstを挿入する >>> A [[0., 1., 0., 3., 4.], [0., 0., 1., 0., 0.], [0., 0., 2., 0., 0.], [0., 0., 3., 0., 0.], [0., 0., 4., 0., 0.]]
>>> A[3:5,:] = 1 #3行目から5行目までを1に >>> A [[0., 1., 0., 3., 4.], [0., 0., 1., 0., 0.], [0., 0., 2., 0., 0.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.]]
>>> A = np.zeros((5,5)) >>> B = np.full((5,5),(5)) >>> C = np.random.randint(0,5,(5,5)) >>> [[4, 3, 1, 1, 0], #人によって違います [3, 4, 4, 0, 3], [3, 3, 4, 3, 4], [2, 4, 1, 2, 4], [4, 3, 2, 2, 1]] >>> A[0:3,:] = B[0:3,:] * C[0:3,:] >>> A [[20., 15., 5., 5., 0.], [15., 20., 20., 0., 15.], [15., 15., 20., 15., 20.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.]] #0行目から3行目までをB×Cの計算結果に置き換える
ここまでで察しの良い人はスライスにfor文が使われていないことに気づいたかも知れません.これも冒頭で述べた「for文を使ったら負け」にきっちりと対応しています.C言語などであれば,スライス後のサイズである配列を予め生成し,そこにfor文で次々に数値を当てはめていく形になりますが,スライスを使えば,より感覚的に,より見やすく,高速に演算が可能になります.皆さん,どんどんスライスしていきましょう.