プロクラシスト

今日の寄り道 明日の近道

【Day-3】知らないコトがいっぱい...!『numpy 100 exercise』を全部やってみる (上級編)


スポンサーリンク

f:id:imslotter:20171203122846p:plain

データ分析ガチ勉強アドベントカレンダー3日目。 今日も引き続き、100 numpy exercise をしていく。

github.com

今日は上級編。初級、中級でさえかなり難しかったので、不安ではあるが...とりあえずやっていく!

また、問題の和訳と難易度も掲載しているので、自分の実力を試したい人はどうぞ。 自分が書いたコードともこちらに載せた(day3のやつ)

github.com

問題

以下に、初級中級の問題を掲載する。長いので、ボタンを押すと見られるようにしている。

  • ○ : 模範解答どおり
  • △ : もっと効率的なやり方があった
  • ✕ : わからなかった
実際の問題をみる
番号 問題 難易度 結果
64 2つのベクトルa,bがあって、aベクトルに、bで与えられたインデックスに箇所に1を加える ★★★
65 aベクトルの値を用いて新しいベクトルを、bのインデックスに従って作る ★★★
66 dtype=ubyte)の(w,h,3)画像全体を定義し、uniqueな値の数計算する(色はRGB256階調) ★★★
67 4次元配列において、最後の2軸だけ一気に足す方法は? ★★★
68 1次元ベクトルaを用意する。aと同じサイズで用意されたインデックスベクトル(min:0, max:9) s に従って構成されるsubsetベクトルの平均 ★★★
69 内積の対角成分だけを取り出す ★★★
70 [1,2,3,4,5]に対し、各値の間に3つの連続したゼロが挿入された新しいベクトルを作成する方法は? ★★★
71 (5,5,3)の行列に対し、(5,5)の行列を、(5,5)の要素の箇所だけ掛け合わせる方法は? ★★★
72 行列の特定の行を入れ替える ★★★
73 三角形を記述する10個のtripletsがある。全ての三角形を作る線分の中で、uniqueなモノを書き出す。 ★★★
74 bincountされた配列Cに対して、np.bincount(A)==CとなるようなAをどう作るか ★★★
75 array上でsliding windowを使った平均算出法 ★★★
76 一次元ベクトルaに対して一行目が(a[0],a[1],a[2])となって、以下の行はインデックスを1ずつずらした値が入るような行列をつくる ★★★
77 配列のbooleanをひっくり返す、floatの記号をひっくり返す ★★★
78 2つの点p0,p1がある。ある点pにおいて、p0,p1が作る直線と点pの距離を求めよ。 ★★★
79 点群の集合P0,P1,Pがあって、Pの各要素の点からP0,P1が作る直線の距離を計算して配列にせよ。 ★★★
80 配列Zに対し、与えられたpositionを配列の中心とし、与えられた形(shape)をくり抜いて新しい配列を作る(ないところは0で埋める) ★★★
81 a=[1,2,3,4,5,...,14]とした時、R = [[1,2,3,4],[2.3.4.5].[3.4.5.6],...,[11,12,13,14]]を作る方法 ★★★
82 行列のランクを計算する ★★★
83 行列の中で最も使われている値を見つける ★★★
84 (10,10)のランダム行列から全ての連続した(3,3)行列を取り出す ★★★
85 Z[i,j]==Z[j,i]となるような、2次元配列のサブクラスを作る ★★★
86 pセットの(n,n)の配列( size=(p,n,n) )と、pセットの(n,1)のベクトル( size = (p,n,1) )を用意する。p個の配列の掛け算を一度に実行する ★★★
87 (16,16)の配列について、4×4のブロック和を求める ★★★
88 ライフゲームをnuypyで実装する ★★★
89 配列の中でn番目に大きな値を取る ★★★
90 数値ベクトルについて、直積を取る。 ★★★
91 普通の配列からrecord arrayを作る ★★★
92 ベクトルaに対し、aの要素を3乗する計算方法を3つ答えよ ★★★
93 A:(8,3)の配列、B:(2,2)の配列がある。Bの要素の順序は無視して、Bのそれぞれの行の要素を含むAの行を抜き出す ★★★
94 (10,3)の行列に対し、全ての値が異なるrowとなるmatrix ★★★
95 整数行列をバイナリ表現で表す ★★★
96 2次元配列において、uniqueなrowを抽出 ★★★
97 2つのベクトルa,bを考える。inner、outer、sum、mulの関数を求める ★★★
98 2つのベクトル(x,y)によって作られるpathにおいて、等間隔サンプリングを用いてサンプリングする ★★★
99 整数nと二次元配列Xがある。整数のみを含み、また和が4になる行を抜き出す ★★★
100 1次元配列Xの平均のbootstrapped samplingにおける95%区間を計算する ★★★

結果

★★★になると、題意がわからない問題も増えた。あと、普通に難しい、日本語が*1想像以上に解くのに時間がかかった。

また、ゴリ押しでなんとかなる問題も結構あった*2。でも美しい回答を心がけたいところである。

全問題
37 15 7 15

活躍したeinsum

上級で印象に残ったのは、einsumを使う問題が結構多かった。 einsum()というのは、アインシュタインの縮約記号をpythonで使える、便利なやつ。以前記事にもしているので、そちらが詳しい。

とにかく、**多次元配列や、複雑な配列演算に超絶威力を発揮する。

下記の問題でeinsum()が使われている

  • (69). 内積の対角成分だけを取り出す
  • (71). (5,5,3)の行列に対し、(5,5)の行列を、(5,5)の要素の箇所だけ掛け合わせる方法は?
  • (84). pセットの(n,n)の配列( size=(p,n,n) )と、pセットの(n,1)のベクトル( size = (p,n,1) )を用意する。p個の配列の掛け算を一度に実行する
  • (90). 数値ベクトルについて、直積を取る。
  • (92). 大きなベクトルaに対し、aの要素を3乗する計算方法を3つ答えよ
  • (97). 2つのベクトルa,bを考える。inner、outer、sum、mulの関数を求める

更に、実行時間が早くなるという恩恵も受けられる。92.の実行時間を確かめてみる

x = np.random.rand(5e7)
%timeit np.power(x,3)
%timeit x*x*x
%timeit np.einsum('i,i,i->i',x,x,x)

計算結果

2.42 s ± 79.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
762 ms ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
364 ms ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

数倍早い。

物理学徒以外の人は余り馴染みがないと思うので、初めは苦労するかもだが、慣れるととても良い。

勉強になった問題(抜粋)

例のごとく、勉強になった問題を抜粋する。

全問題とちょっとした調べ物のリンクなどは、前と同様にgithubに残した。

github.com

81. a=[1,2,3,4,5,...,14]とした時、R = [[1,2,3,4],[2.3.4.5].[3.4.5.6],...,[11,12,13,14]]を作る方法

普通にゴリ押しでも行けたが、関数を知っていると一発で終わりとても良いので、メモしておく。

自分の回答

a = np.arange(1,15,dtype=np.int32)
b = np.zeros((11,4),dtype=np.int32)
for i in range(11):
    b[i] = a[i:i+4]
print(b)

模範解答

np.lib.stride_tricksを使う stride_trickの説明ページ

Z = np.arange(1,15,dtype=np.uint32)
R = np.lib.stride_tricks.as_strided(Z,(11,4),(4,4))
print(R)

共に出力は以下

[[ 1  2  3  4]
 [ 2  3  4  5]
 [ 3  4  5  6]
 [ 4  5  6  7]
 [ 5  6  7  8]
 [ 6  7  8  9]
 [ 7  8  9 10]
 [ 8  9 10 11]
 [ 9 10 11 12]
 [10 11 12 13]
 [11 12 13 14]]

82. 行列のランクを計算

線形代数パッケージnp.linalgを使う。linalgの説明ページ

linalgを使うと、例えば以下の様なことが出来る

  • 特異値分解 : np.linalg.svd(A)
  • ノルムを出す : np.linalg.norm(a)
  • 行列式を出す : np.linalg.det(A)
  • 方程式を解く : np.linalg.solve(a,b)
  • 逆行列を出す : np.linalg.inv(A)
  • 固有値を出す : np.linalg.eig(A)
Z = np.random.uniform(0,1,(10,10))
U, S, V = np.linalg.svd(Z) # Singular Value Decomposition
#np.linalg.matrix_rank(Z) も可
rank = np.sum(S > 1e-10)

83. 行列の中で最も使われている値を見つける

このbincount()というやつが結構便利。どの値が何個あるかを数えてくれる。 また、重複を避けた配列を返すものはnp.unique(a)というのが便利だったりする

A = np.random.randint(0,5,10)
print(A)
print(np.bincount(A).argmax())
#unique
print(np.unique(A))

出力

[1 1 2 0 3 2 2 1 4 0]
1
[0 1 2 3 4]

95. 整数行列をバイナリ表現で表す

2進数に直す方法。ゴリ押しでもいけるが、模範解答がエレガントだったので、採用。

&演算子がバイナリの演算子なのとreshap(-1,1)で縦ベクトルに出来るのを利用する。

I = np.array([0, 1, 2, 3, 15, 16, 32, 64, 127])
print(I)
# print(I.reshape(-1,1))
# print([3]&(2**np.arange(8))!=0)
B = ((I.reshape(-1,1) & (2**np.arange(8))) != 0).astype(int)
print(B[:,::-1])

出力

[  0   1   2   3  15  16  32  64 127]
[[0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1]
 [0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 1 1]
 [0 0 0 0 1 1 1 1]
 [0 0 0 1 0 0 0 0]
 [0 0 1 0 0 0 0 0]
 [0 1 0 0 0 0 0 0]
 [0 1 1 1 1 1 1 1]]

まとめ

2日に渡って、numpyの勉強をしてきた。 結構網羅的だったように思う。知らない関数もだいぶ多かったし、いい勉強になった。

何よりも、数時間配列を作ってはいじり作ってはいじりを繰り返したお陰で、ささっと配列を作れるよぅになった気がする。これはもう訓練なので、一度こういう時間を儲けられたのはとても良かった。

というわけでとりあえずnumpyの総ざらいは終わり。次回以降はpandasscikit-learnjupyterの復習・勉強をしていく。それではまた明日!

*1:和訳が意味不明のものもあると思います、スミマセン。実行してみて何がいいたいか確かめてくださいw

*2:88.ライフゲームとか

PROCRASIST