プロクラシスト

今日の寄り道 明日の近道

【Day-2】numpyの勉強に『100 numpy exercise』を全部やってみる(初級・中級編)


スポンサーリンク

f:id:imslotter:20171202225708p:plain

データ分析ガチ勉強アドベントカレンダー2日目。 数式を扱うことが多くなるので、numpyの復習をしたいと思う。使ったのは100 numpy exercise

github.com

numpyを用いるさまざまな問題が用意されていて、大変勉強になる。 今回は自分の実力を試すために、自分で解きつつ、よく使ったもの/新しく学んだことを列挙していく。

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

github.com

2日目は、初級・中級を掲載する。上級は明日挑戦して掲載予定。

  • ★☆☆ : 初級
  • ★★☆ : 中級
  • ★★★ : 上級

問題

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

  • ○ : 模範解答どおり
  • △ : もっと効率的なやり方があった
  • ✕ : わからなかった
実際の問題をみる
番号 問題 難易度 結果
1 numpyを`np`という名前でimport ★☆☆
2 numpyのバージョンとconfigを表示 ★☆☆
3 サイズ10のnullベクトルを作成 ★☆☆
4 作った配列のメモリサイズを確認 ★☆☆
5 コマンドラインからnumpyのドキュメントを見る ★☆☆
6 サイズが10で5番目の値だけ1の配列を作る ★☆☆
7 値が10から49まで順に並んだ配列を作る ★☆☆
8 配列の値をひっくり返す ★☆☆
9 それぞれ0~8まで入った3×3の行列を作る ★☆☆
10 [1,2,0,0,4,0]から0じゃない箇所を見つける ★☆☆
11 3×3の単位行列をつくる ★☆☆
12 3×3×3の乱数行列を作る ★☆☆
13 10×10の乱数行列を作って、最大値と最小値を見つける ★☆☆
14 サイズ30の乱数ベクトルを作って平均値を出す ★☆☆
15 境界が1で、中身が0の2次元配列をつくる ★☆☆
16 今ある配列に、0で埋めた境界を作る ★☆☆
17 np.nanとnp.inf、小数の扱い ★☆☆
18 5×5の行列を作って、対角成分の一つ下だけ1,2,3,4で埋める ★☆☆
19 8×8の行列を作り、チェッカーボードパターンを作る ★☆☆
20 (6,7,8)の配列で、インデックス(x,y,z)における100番目の要素は? ★☆☆
21 ile functionを使って8×8のチェッカーボードパターンを作る ★☆☆
22 5×5の乱数行列を正規化する ★☆☆
23 8ビット整数を使って色を記述するdtypeを作る ★☆☆
24 5×3の行列と3×2の行列を掛け合わせる(実数) ★☆☆
25 1次元配列で、3以上8以下の値は負値にする ★☆☆
26 numpy有無でのsum()の結果はわかる? ★☆☆
27 整数ベクトルのなかでどの記述が間違っている? ★☆☆
28 配列のゼロ除算 ★☆☆
29 配列の小数点以下切り上げ ★☆☆
30 2つの配列の中から同じ値を見つける ★☆☆
31 numpyのwarningを無視する ★☆☆
32 np.sqrtとnp.emath.sqrtの違い ★☆☆
33 昨日、今日、明日の日付 ★☆☆
34 2016年7月の日付配列 ★☆☆
35 要素の計算( (A+B)*(-A/2) )をコピーなしで計算 ★★☆
36 乱数行列の整数部分だけ取り出すやつ5種類 ★★☆
37 行の値が[0,1,2,3,4]となる、5×5の行列 ★★☆
38 ジェネレータを使って0から9までの配列を作る ★★☆
39 0より大、1より小のサイズ10のrangingベクトルを作る ★★☆
40 サイズ10のランダム配列を作ってsortする ★★☆
41 小さい行列をnp.sumより早く計算する方法は? ★★☆
42 2つのランダムベクトルが等しいかどうかを判定する ★★☆
43 配列をimmutable(read-only)にする ★★☆
44 直交座標の10×2の行列を極座標変換 ★★☆
45 サイズ10のベクトルを作って最大値を0に変換 ★★☆
46 x,y座標を[0,1]×[0,1]領域に変換したstructured arrayを作る ★★☆
47 2つのベクトルを用いてコーシー配列(Cij =1/(xi - yj))を作れ ★★☆
48 それぞれのnumpy scalar typeでの最小、最大値をprintする ★★☆
49 配列の全ての値を省略せずにプリントする ★★☆
50 ベクトル内の与えられたvalueの中で最も近い値を出力 ★★☆
51 位置座標とcolorのRGBが保存されたstructured arrayを作る ★★☆
52 10×2のランダム行列を作って、点同士の距離を求める ★★☆
53 float32の行列をint32の行列に変換 ★★☆
54 どうやってファイルを配列に変換する? ★★☆
55 numpyの配列のenumerateと同等の表現は? ★★☆
56 2DのGaussian-like arrayを生成する ★★☆
57 2次元配列にランダムにp個、値を埋め込む ★★☆
58 行列の各行に対し、その行の平均を引いていく ★★☆
59 n列目のcolomunをソート ★★☆
60 2D arrayにnullがあるかどうかを判定する ★★☆
61 指定の値に最も近い値を見つける ★★☆
62 イテレータを使って1×3行列と3×1行列のsumを計算 ★★☆
63 名前の属性を付与したarrayクラスをつくる ★★☆

結果

なかなか厳しい結果となった。厳しい。 初級から結構知らないものが出てきたので、ほーんと思いながらといた。

中級以降は、ダメ。自分がいかにプリミティブな操作しかできていないんだなというのを痛感した。

難易度 問題数 正解 正答率
★☆☆ 34 23 67%
★★☆ 29 12 41%

numpy配列の基本

せっかくなので、よく使うnpの関数と、新しく知ったお得機能をメモしておきたい。このあたりを知っておけば、基本的な操作はできると思われる。

配列を作る

とりあえず配列を作れないとお話にならないので、作るときに便利な関数たちを列挙

np.array([1,2,3,4,5])
#>>>array([1, 2, 3, 4, 5])

np.zeros((3,3))
#>>>array([[ 0.,  0.,  0.],
#        [ 0.,  0.,  0.],
#        [ 0.,  0.,  0.]])

np.ones((3,3))
#>>>array([[ 1.,  1.,  1.],
#       [ 1.,  1.,  1.],
#       [ 1.,  1.,  1.]])

np.random.random(5) #0から1までのランダム変数をサイズ5のベクトルで
#>>>array([ 0.33732001,  0.73696762,  0.69890914,  0.46966465,  0.91019171])

np.random.randint(0,5,10) #0から4までのランダム整数をサイズ10のベクトルで
#>>>array([4, 3, 4, 1, 1, 4, 1, 3, 2, 0])

np.random.uniform(0,1,5) #0から1までの一様実数をサイズ5のベクトルで
#>>>array([ 0.72515655,  0.09389927,  0.49393525,  0.11564103,  0.31317932])

np.random.normal(0,1,(3,3)) #平均0, 分散1の正規分布を3×3の行列で
#>>>array([[ 0.64296467, -0.14865526, -0.24650383],
#       [ 0.30479151, -1.61713417, -0.15371185],
#       [ 0.92736104,  1.58778307, -1.86301156]])

np.arange(10) #0から9まで順番に
#>>>array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

np.arange(1,10,2) #1から10を超える直前の数字まで2個飛ばしで
#>>>array([1, 3, 5, 7, 9])

np.linspace(0,10,10) #0から10までを5分割
#>>>array([  0. ,   2.5,   5. ,   7.5,  10. ])

# 5×5の配列の中にx,y座標を埋め込む
A = np.zeros((5,5), [('x',float),('y',float)])
A['x'], A['y'] = np.meshgrid(np.linspace(0,1,5),
                             np.linspace(0,1,5))
print(A)
# >>> [[( 0.  ,  0.  ) ( 0.25,  0.  ) ( 0.5 ,  0.  ) ( 0.75,  0.  ) ( 1.  ,  0.  )]
# [( 0.  ,  0.25) ( 0.25,  0.25) ( 0.5 ,  0.25) ( 0.75,  0.25) ( 1.  ,  0.25)]
# [( 0.  ,  0.5 ) ( 0.25,  0.5 ) ( 0.5 ,  0.5 ) ( 0.75,  0.5 ) ( 1.  ,  0.5 )]
# [( 0.  ,  0.75) ( 0.25,  0.75) ( 0.5 ,  0.75) ( 0.75,  0.75) ( 1.  ,  0.75)]
# [( 0.  ,  1.  ) ( 0.25,  1.  ) ( 0.5 ,  1.  ) ( 0.75,  1.  ) ( 1.  ,  1.  )]]

# イテレータを使って0から9までのサイズ10の配列
def generate():
    for x in range(10):
        yield x
a = np.fromiter(generate(),dtype=float,count=-1)
print(a)
#>>> [ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]

# チェッカーフラッグのように、4×4の行列に0,1を並べる
np.tile(np.array([[0,1],[1,0]]),(2,2))
#>>> array([[0, 1, 0, 1],
#            [1, 0, 1, 0],
#            [0, 1, 0, 1],
#            [1, 0, 1, 0]])

形を変える

reshapeを使う

a = np.arange(10)
print(a)
>>>
a.reshape(2,5)
>>>

endpointの有無

linspaceについて、指定した値を以下として扱うか未満として扱うか

np.linspace(0,10,10)
#>>> array([  0.        ,   1.11111111,   2.22222222,   3.33333333,
#           4.44444444,   5.55555556,   6.66666667,   7.77777778,
#           8.88888889,  10.        ])
np.linspace(0,10,10,endpoint=False)
#>>>array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

hstack, vstack, concatenate

listのappendみたいなもの。ベクトル同士の結合

a = np.arange(4)
b = np.arange(4)[::-1]
print(a,b)
#>>> [0 1 2 3] [3 2 1 0]
print(np.hstack((a,b)))
#>>> [0 1 2 3 3 2 1 0]
print(np.vstack((a,b)))
#>>> [[0 1 2 3]
#     [3 2 1 0]]

スカラー値を足すとどうなる?

全部に適用される

a = np.arange(4)
print(a)
#>>> [0 1 2 3]
print(a+1)
#>>> [1 2 3 4]

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

多くの問題が学びのあるものだったが、個人的に勉強になった問題を抜粋する なお、全問題と回答はgithubにあげている。 とはいえ、中級はかなり勉強になったので、全部試してみると学びが大きいかもしれない。 githubには問題の和訳と回答をあげてみた。自分なりの回答をしているものもある。

24. 5×3の行列と3×2の行列を掛け合わせる(実数)

np.dotだけじゃなくて、@なる演算子があるらしい。

A = np.arange(0,12).reshape(4,3)
B = np.arange(0,6).reshape(3,2)
print(A)
print(B)
print(A@B)

出力

[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
[[0 1]
 [2 3]
 [4 5]]
[[10 13]
 [28 40]
 [46 67]

25. 1次元配列で、3以上8以下の値は負値にする

条件文で検索をかけることができる

a = np.arange(15)
a[(a>3)&(a<=8)] *= -1
print(a)

出力

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

36. 乱数行列の整数部分だけ取り出すやつを5種類

//という演算子をはじめて知った。

Z = np.random.uniform(0,10,10)
print("36:")
print(Z - Z%1)                      # 余りを引く
print(np.floor(Z))                  # 切り捨ての関数
print(np.ceil(Z)-1)                 # 切り上げて1を引く
print(Z.astype(int).astype(float))  # 整数にしちゃう
print(np.trunc(Z))                  # 整数部だけ取り出す
print(Z//1)                         # 切り捨て演算子

41. 小さい行列をnp.sum()より早く計算する方法は?

add.reduce()を使うと良いらしい

a = np.arange(100)
print(np.add.reduce(a))

43. 配列をimmutable(read-only)にする

writeableをfalseの設定にして、書込み禁止にする。numpyはその設定ができる

a43 = np.zeros(10)
a43.flags.writeable = False
# a43[0] = 1 エラーが出る

49. 配列の全ての値を省略せずにプリントする

printoptionsを利用することで、 省略されずに配列の全要素を見ることができる。

np.set_printoptions(threshold=np.nan)
x = np.zeros((16,16))

55. numpyの配列のenumerateと同等の表現は?

通常のenumerateのような、ndenumerate, ndindexというのがある。こっちのほうが早いのかな?あんまり違いがわかっていない。

A = np.arange(9).reshape(3,3)
for index, value in np.ndenumerate(A):
    print(index, value)
for index in np.ndindex(A.shape):
    print(index, A[index])

まとめ

全然わからないものもたくさんあって、とても勉強になった。 明日は上級(★★★)にチャレンジ予定だが、0点なのではないかと危惧している。 でも、numpyはどういう場面でも使うので、基本をしっかり抑えておきたいなと思う。

それではまた明日!

PROCRASIST