データ分析ガチ勉強アドベントカレンダー3日目。 今日も引き続き、100 numpy exercise をしていく。
今日は上級編。初級、中級でさえかなり難しかったので、不安ではあるが...とりあえずやっていく!
また、問題の和訳と難易度も掲載しているので、自分の実力を試したい人はどうぞ。 自分が書いたコードともこちらに載せた(day3のやつ)
問題
以下に、初級中級の問題を掲載する。長いので、ボタンを押すと見られるようにしている。
- ○ : 模範解答どおり
- △ : もっと効率的なやり方があった
- ✕ : わからなかった
番号 | 問題 | 難易度 | 結果 |
---|---|---|---|
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に残した。
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
を使うと、例えば以下の様なことが出来る
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の総ざらいは終わり。次回以降はpandas、scikit-learn、jupyterの復習・勉強をしていく。それではまた明日!