【Python No.4】標準リストのappendとnumpyのappend

シェアする

Pythonで配列を扱う際に,appendを使用することがあると思います.

これは,現在ある配列に1つ要素を加えるという操作ですが,便利なので案外使われることが多いかもしれませんので,その辺りの話をしたいと思います.

先ずはいつものようにnumpyをインポートします.

import numpy as np

引数としてnを受け取り,10*n+2の値を計算して返すような関数f(n)があったとします.そのとき,引数nの値を0〜9まで変化させて,その結果を配列に格納するというプログラムを組む場合を考えます.

def f(n):
 return 10*n+2

result = []
for n in range(10):
 result.append(f(n))

空のリストをresultという変数で定義して,結果を後ろに連結させていくというプログラムは案外見かけるものです.ちなみに,上のプログラムの実行結果が入っているresultを出力すると以下のようになります

>>> result
[2, 12, 22, 32, 42, 52, 62, 72, 82, 92]

このとき,結果が入ってる配列は標準リストです.この中から,たとえば0番目,5番目,6番目だけを取り出したい時,どうすればいいでしょう?当然ですが,

>>> result[[0,5,6]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not list

これだとエラーです.この解決方法はいろいろあると思いますが,処理が終わったらnumpy配列に型変換してしまうことが1つの方法でしょうね.

def f(n):
 return 10*n+2
result = []
for n in range(10):
 result.append(f(n))
result = np.array(result)

0番目,5番目,6番目の値を取り出してみます.

>>> result[[0,5,6]]
array([ 2., 52., 62.])

出来ました.また,標準リストのappendを使うのではなく,numpyのappendを使用するのもいいでしょう.

result = []
for n in range(10):
 result = np.append(result, f(n))

numpyのappendは少し書き方が変わりますが,そこさえ気をつければ全く同じことが可能です.しかも最初に標準リストでresultを定義しても,numpyのappendを使うとnumpy配列に自動的に変換されるらしいです.

>>> result
array([ 2., 12., 22., 32., 42., 52., 62., 72., 82., 92.])

ただ,ループ内で毎回appendで結果を追加していくのはあまり好ましくないと思います.appendは動的に配列のサイズを増やしていくので,動作速度が遅くなるのではと思います(特にnumpyのappendは,標準リストのappendよりかなり遅いらしい).

それで,今回のプログラムに関して言うと,forループの回数が最初からわかっているので,あらかじめ結果を格納する配列を10次元としてnp.zerosで定義しておく.こうすると毎回appendする必要がない(f(n)の定義は省略します).

result = np.zeros(10)
for n in range(10):  
 result[n] = f(n)

ちなみに,np.zeros(10)は10次元のすべての要素の値が0の配列を作成します(つまり,array([0,0,0,0,0,0,0,0,0,0])).だけど,これはとてもC言語っぽい書き方(私は好きですがw).Pythonらしく書くと,

result = np.array([f(n) for n in range(10)])

という内包表現で書くのが一般的.なんと1行で終わってしまった!(C言語に慣れているとこういう書き方にとても驚く)

しかし,forループ内で,f(n)を配列に代入するだけでなく他にもいろいろ処理が行われている場合は,この書き方はダメですね.

ちなみに,resultの要素の値が 2 と書かれている場合と 2. と書かれている場合があると思いますが,これは型の違い.numpy配列の型をみるには,変数の後ろに .dtypeを付けるとわかります.

>>> result.dtype
dtype('float64')

floatなので実数型です.ちなみに,整数値が入っている標準リストをnumpy配列に変換した直後だと整数型になります.

>>> result = [0,2,3]
>>> result = np.array(result)
>>> result.dtype
dtype('int64')

intなので整数型ですね.ちなみに標準リストに実数値が入っている場合は,numpy配列に変換すると実数型になります.

それで.np.zeros関数は要素の値を0とする配列を作成しますが,型はすべて実数となります.

まとめ

  • 配列のappendは出来るだけ使わない方が無難(特にnumpyのappendは遅いので使用しない方がよい)
  • どうしても使う場合は,標準リストのappendを使用して処理が終わった後でnumpy配列に変換
  • ループ回数が既知であれば,値を格納するnumpy配列をループ回数分だけ最初から確保しておく