1.5. 一次元配列とその操作

 pythonの標準ライブラリー(python本体)には配列を扱うlistが用意されている。普通のアプリを作成する場合にはよく使うのだが、科学技術計算に使うためには、計算の利便性と高速化の面でlistは適さない。数値計算、科学技術計算用では、NumPyに用意されているndarrayというdata typeの配列が広く使われている。このndarrayは、行列、ベクトル計算用の高速アルゴリズムに最適化されていて、NumPyやSciPyで提供されるさまざまなfuntionが利用できる。さらにはデータ解析モジュールのPandasや機械学習モジュールのscikit-learnでもndarrayが使われているのでデータサイエンスでは必須の配列である。

 まず$0$から$9$まで$10$個の数字を要素に持つベクトルを作ってみよう。

In [3]:
import numpy as np
x = np.arange(0, 10., 1)
print(x)
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]

 上の例では、0から始まって10.までの、1づつ増える配列をnp.arangeという関数(function)によって生成し、xという変数に格納する、という式になっている。この0, 10., 1のような入力に相当する変数をnp.arangeというfunction(関数)の引数(argument)という。最後の1は増分を表し、1なら省略できる。pythonの標準ライブラリーのlistは、異なるdata typeの要素を含めることができたが、ndarrayは、要素がすべて同じ型でなければならないという制約がある。$2$行目の引数をint型(整数)の10ではなく、小数点をつけたfloat型の10.と指定しているので、配列要素はすべてfloat型になっている。引数のどこか一つでも小数点が入っていれば、配列の要素はすべてfloat型になるが、一つも小数点がなければ、以下のようにint型の配列になる。

In [4]:
x = np.arange(0, 10)
print(x)
[0 1 2 3 4 5 6 7 8 9]

 小数点がついているかいないかで、int型かfloat型か厳密に区別される。配列番号(index)は0から始まるので、要素の中に10が含まれていないことに注意。

 配列の定義の仕方はさまざまある。要素を一つ一つ定義したいなら、np.arrayを使って、

In [5]:
x2 = np.array([2, 2, 3])
print(x2)
[2 2 3]

のようにすると、1次元の配列が作れる。


スライス

 スライスとは、ある配列の一部分を切り取る(スライスする)という操作である。例えばxの$3$番目の要素($1$から数える数え方で$4$番目)から$5$番目($1$から数えて$6$番目)の要素まで取り出して、x_aという配列にしたい場合、コロン(:)を使って、

In [6]:
x_a = x[3:6]
print(x_a)
[3 4 5]

のようにx[start:end]とすればよい。ここで非常に重要なことは、x[end]は取り出される配列の要素に含まれない 、ということだ。つまり、0から数える数え方で5番目まで取り出したいなら、end5ではなく、6と書かなければならない。これは初心者がよく間違えやすいミスなので注意してほしい。したがってたとえばx[3:3]は、

In [7]:
print(x[3:3])
[]

というふうに要素が空の配列になってしまう。
 配列番号を例えば2個飛ばしてスライスしたい場合はさらに後ろに:nとすると、$n$個飛ばした配列が取り出せる。

In [9]:
print(x[3:6:2])
[3 5]

 今後重要になっていくことだが、このコロン(:)は全部の要素を表すという意味もある。なので、x[:]とすれば、それはxそのもののことになる。

In [6]:
print(x[:])
[0 1 2 3 4 5 6 7 8 9]

 例えば偶数の配列番号の要素だけ取り出したいなら、

In [10]:
print(x[::2])
[0 2 4 6 8]

 とすればよい。


配列のロールと連結

 配列のロールと連結もよく使う操作なのでここで紹介しておきたい。例えば要素が$0$から$N-1$まで$N$個ある配列$x$があるとする。例えば$N=10$として

In [7]:
x = np.arange(0, 10)
print(x)
[0 1 2 3 4 5 6 7 8 9]

この配列を全部右に一つずらし、はみ出るN-1番目の要素x[N-1]を一番最初の0番目に移動したような新しい配列を作りたいとする。一番簡単なのは、np.rollを使って、

In [8]:
x_roll = np.roll(x, 1)
print(x_roll)
[9 0 1 2 3 4 5 6 7 8]

np.roll(x, 1)1は、右方向にロールさせることを意味していて、-1にすれば逆に左方向にロールできる。同じことをスライスと配列の連結を使って実現することもできる。連結にはいろいろやり方があるがここではnp.hstackというfunctionを使う。hstackhはhorizontalつまり横に連結するという意味。

In [9]:
x_roll = np.hstack([x[9], x[0:9]])
print(x_roll)
[9 0 1 2 3 4 5 6 7 8]

この場合でも、0番目から8番目までの要素を取り出すには、x[0:8]ではなく、x[0:9]であることに注意してほしい。配列のロールと連結に関しては、多次元配列のところでもう少し詳しく勉強する予定。(連結するfunctionにはnp.vstacknp.concatenateなどもある。)



練習問題

(1.5.1.) 100までの正の整数のうち3で割り切れる要素を持つ配列を作れ。


(1.5.2.) $F_n = 2^{2^n}+1$ ($n$は自然数)で表される自然数の数列をフェルマー数という。$n=0$から$4$までのフェルマー数を要素に持つ配列を作れ。



(1.5.3.) 以下の等式が$n = 100$で成り立っていることを確認せよ。(誤差については無視してよい。) \begin{equation} \sum _{k=1} ^n k^3 = \left\{ \frac{1}{2}n(n+1) \right\}^2 \end{equation}


解答例

(1.5.1.) 特に解説の必要はないだろう。

In [10]:
import numpy as np
m = np.arange(0, 100, 3)
print(m)
[ 0  3  6  9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69
 72 75 78 81 84 87 90 93 96 99]



(1.5.2.) まず$n$をnp.ndarrayの配列nとして定義し、与えられたフェルマー数の定義式を使って計算するとよい。フェルマー数の詳細については数学の本などを参照。

In [11]:
n = np.arange(0, 5)
Fn = 2**(2**n) + 1
print(Fn)
[    3     5    17   257 65537]



(1.5.3.) 右辺は数列の和(np.sum)で、左辺は単純に計算すればよい。

In [12]:
n = 100
k3 = (np.arange(1, n+1))**3
print(sum(k3))
print((1/2*n*(n+1))**2)
25502500
25502500.0


当サイトのテキスト・画像の無断転載・複製を固く禁じます。
Unauthorized copying and replication of the contents of this site, text and images are strictly prohibited.
© 2019 Go Yusa