訳者による紹介
本記事は、NumPy v.1.19 公式Documentation内の "NumPy: the absolute basics for beginners" の翻訳です。このドキュメントが開発版から本リリース扱いに変わったことをきっかけに、公開しました。
近年Pythonの人気は高止まりしています。その理由の一つは、AIブームとPythonの機械学習ライブラリの充実でしょう。Pythonが機械学習など科学技術計算分野でのライブラリが豊富であることが、さらなるライブラリの拡充や、初心者に限らない支持を引き起こしているようです。
NumPyはこれら科学技術計算ライブラリの基盤となるシステムです。NumPyが採用される理由に、Pythonの速度の問題があります。Pythonは言語の特性上、一部の操作は非常に低速であることで知られています。そのため多くのライブラリは、Pythonの速度が処理のボトルネックになることを回避するために、C言語で実装されたNumPyなどを使うことで大量のデータ処理にも耐えうる速度を実現しているのです。
NumPyはとても便利かつ高速に作動する魅力がありますが、Python自身と比べるとすこしとっつきにくいかもしれません。本記事では、NumPyの基礎を、画像をまじえつつ明快に説明していています。解説の範囲は"Quickstart tutorial"の半分以上をカバーしているので、あの無味乾燥なチュートリアルを読まずとも、ある程度のことができるようになるはずです。
また、【初心者向け】図解でわかるNumPyとデータ表現にも目を通すと参考になるかもしれません。執筆者は本記事の画像の作成者です。
誤訳等の指摘をいただけると大変助かります。
以下翻訳
NumPy完全初心者へのガイドへようこそ!もしコメントや提案があれば、遠慮なく連絡してください!
Welcome to NumPy!
NumPy (Numerical Python)は科学と工学(engineering)のほとんどすべての領域で用いられているオープンソースPythonライブラリです。NumPyは数値データを扱うための世界標準であり、ScientificPythonとPydata系の中核です[Pydata ecosystems: Numpyの開発元Pydataの製品群]。NumPyユーザーには始めたてのプログラマーから、最先端の科学・工学研究と開発を行うベテラン研究者までいます。NumPy APIはPandas, SciPy, Matplotlib, scikit-learn, scikit-imageや他のほとんどのデータサイエンス用や科学用Pythonパッケージに広く用いられています。
NumPyライブラリは多次元の配列と行列データ構造を持ちます(これに関しては後の節でより詳しく説明します)。NumPyは、同一のデータ型で構成されたn次元の配列オブジェクトであるndarrayを、配列を効率的に処理するためのメソッドと併せて提供します。Numpyは配列についての多様な数学的操作を行うために用いることができます。NumPyは配列と行列の効率的な計算を保証する力強いデータ構造をPythonに付け足し、これら配列と行列で働く高度の数学的機能を持った巨大なライブラリを提供します。
Learn more about NumPy here!
NumPyをインストールする
NumPyをインストールするには、科学用Pythonディストリビューションを利用することを強く勧めます。もし
お使いのOSにNumPyをインストールするための完全な手引きが必要なら、ここですべての詳細を見つけることができます。
もしすでにPythonを使っているなら、以下のコードでNumPyをインストールできます。
conda install numpy
あるいは
pip install numpy
もしまだPythonを持っていないのであれば、Anacondaの使用を検討するのがよいでしょう。AnacondaはPythonを一番簡単に始められる方法です。このディストリビューションを使う利点は、NumPyやデータ解析に使われる主要なパッケージ、pandas、Scikit-Learnなどを個々にインストールすることをそこまで心配する必要がないことです。
You can find all of the installation details in the Installation section at SciPy.
NumPyをインポートする方法
パッケージやライブラリを使いたい時はいつでも、最初に使いたいものをアクセス可能にする必要があります。
NumPyとそれに備わるすべての機能を使いはじめるには、NumPyをインポートしなければなりせん。これは次のインポート文[statement]によって簡単にできます。
import numpy as np
(我々はNumPyをnpと省略します。それは時間を節約するためであり、またコードを標準化しておくことで、そのコードを使って働く誰もが簡単に理解でき実行できるようにするためです。)
コード例の読み方
もしコードがたくさんのチュートリアルを読むのにまだ慣れていないなら、どのように次のようなコードブロックを理解すればよいかわからないかもしれません。
>>> a = np.arange(6)
>>> a2 = a[np.newaxis, :]
>>> a2.shape
(1, 6)
もしこのやり方をよく知らなくても、この表記法はとても理解しやすいです。もし>>>があれば、入力、つまりあなたが入力するだろうコードを指しています。コードの前に>>>がないものはすべて出力、つまりコードを実行した結果です。これはPythonをコマンドラインで実行するときのスタイルですが、IPythonを使うときは、異なるスタイルが表示されるかもしれません。
Python list と NumPy arrayの違いは何ですか?
NumPyは、配列を作り数値データを操作するための高速かつ効率的な方法を非常に多く備えています。Pyhonリストは一つのリストに異なったデータ型を持つことが可能ですが、NumPy配列で配列上のすべての要素は同種でなければなりません。もし配列に他のデータ型が混ざっていれば、配列上で働くはずの数学的操作はひどく非効率になるでしょう。
NumPyはなぜ使われているのですか?
NumPy配列はPythonリストよりも高速で簡潔です。[Pythonの]配列はメモリーをあまり使わず、使用するのに便利です。それと比べてもNumPyがデータをためるためにメモリーを使う量ははるかに少なく、データタイプを識別するメカニズムも備えています。このことはコードをさらに最適化することを可能にします。
配列とはなんですか?
配列とはNumPyライブラリの主要なデータ構造のひとつです。配列は値たちのグリッドであり配列は生のデータについての情報、要素をどのように配置するかについての情報、要素をどのように理解(interpret)するかについての情報を持ちます。NumPyが持つ諸要素からなるgridは、様々な方法でインデックスすることができます。要素はすべて同種であり、配列のdtype
として表されます。
配列は正の整数のタプル、ブール値(booleans)、別の配列や整数によってインデックス可能です。配列のrank
は次元の数です。配列のshape
は各次元に沿った配列のサイズを表す整数のタプルです。
NumPy配列を初期化する一つの方法は、Pythonリストから初期化することです。二次元以上のデータにはネストしたリストを用います。
例:
>>> a = np.array([1, 2, 3, 4, 5, 6])
あるいは
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
配列の要素にアクセスするには鍵括弧を使います。配列の要素にアクセスするとき、NumPyのインデックスは0から始まることを忘れないでください。これはあなたが配列の最初の要素にアクセスしたいならば、配列”0”にアクセスすることになるだろうという意味です。
>>> print(a[0])
[1 2 3 4]
配列についてさらに詳しく
この節では 1D array
, 2D array
, ndarray
, vector
, matrix
を扱います
「ndarray」と表記される配列を時折目にしたことがあるかもしれません。これは「N次元配列」の略記です。N次元配列は簡単に言うと任意の次元数をもつ配列です。「1-D」ないし一次元配列、「2-D」ないし二次元配列、etc...、もまた目にしたことがあるかもしれません。NumPyのndarrayクラスは行列とベクトルのどちらを表すのにも使われます。ベクトルは一次元配列(行ベクトルと列ベクトルの違いはありません)であり、行列は二次元の配列を参照します。 3D(三次元)以上の配列では、テンソルという用語もよく使われます。
配列の属性とはなんですか?
配列は通常、同じ種類かつ同じサイズの項目の固定サイズのコンテナです。配列の次元数と項目は配列の形から定義されます。配列の形は各次元のサイズを規定する自然数のタプルです。
NumPyでは、次元はaxesと呼ばれます。すなわち、もし次のような配列があるなら
[[0., 0., 0.],
[1., 1., 1.]]
この配列は二つの軸を持ちます。一つ目の軸の長さは2で、二つ目の長さは3です。
他のPythonコンテナと同様に、インデックスやスライシングで配列の内容にアクセスしたり修正したりすることができます。ですが典型的なコンテナオブジェクトと違って、同じデータを異なる配列で共有できるので、ある配列で行われた変更が、別の配列で現れるかもしれません。
配列の属性は配列固有の情報を反映します。もし新たに配列を作らずに配列のプロパティを得る、あるいは設定する必要があるなら、配列の属性を通して配列にアクセスすることが多いです。
Read more about array attributes here and learn about array objects here.
簡単な配列をつくる方法
この節では np.array()
, np.zeros()
, np.ones()
, np.empty()
, np.arange()
, np.linspace()
, dtype
を扱います
NumPy配列を作るには、関数np.array()
を使います。
簡単な配列を作るのに必要なことは、リストを渡すことです。必要に応じてリストのデータ型を指定することもできます。 You can find more information about data types here.
>>> import numpy as np
>>> a = np.array([1, 2, 3])
次のように配列を視覚化することができます。
これらの視覚化は概念をわかりやすくし、NumPyの発想と仕組みの基礎的な理解を与えるためのものであることに注意してください。配列と配列操作はここで表現されたものよりはるかに複雑です。
要素の連続から作る配列以外にも、0
で埋め尽くされた配列を簡単に作成することができます。
>>> np.zeros(2)
array([0., 0.])
他にも1
で埋め尽くされた配列を作ることができます。
>>> np.ones(2)
array([1., 1.])
あるいは空の配列だってすら!empty
関数は、初期内容がランダムかつメモリの状態に依存する配列を作成します。empty
関数をzero
関数(やそれに似たもの)に優先して使う理由はスピードです。後ですべての要素を埋めるのを忘れないようにしてください!
>>> # Create an empty array with 2 elements
>>> np.empty(2)
array([ 3.14, 42. ]) # may vary
連続した要素の配列を作成できます:
>>> np.arange(4)
array([0, 1, 2, 3])
そして均等に間隔をあけた列からなる配列も作成できます。そのためには、最初の数・最後の数・ステップ数を指定します。
>>> np.arange(2, 9, 2)
array([2, 4, 6, 8])
np.linspace()
を使用して、指定した間隔で線形に間を空けた値を持つ配列を作成することができます。
>>> np.linspace(0, 10, num=5)
array([ 0. , 2.5, 5. , 7.5, 10. ])
データタイプを指定する
デフォルトのデータ型は浮動小数点(np.float64)ですが、使いたいデータ型をdtypeキーワードで明示的に指定できます。
>>> x = np.ones(2, dtype=np.int64)
>>> x
array([1, 1])
Learn more about creating arrays here
要素を追加・削除・ソートする
この節は np.sort()
, np.concatenate()
を扱います
要素をソートするとき、np.sort()
を使うと簡単です。この関数を呼ぶ際には、軸、種類、順序を指定できます。
この配列を例にとると、
>>> arr = np.array([2, 1, 5, 3, 7, 4, 6, 8])
こうして昇順に素早く並び替えることができます。
>>> np.sort(arr)
array([1, 2, 3, 4, 5, 6, 7, 8])
sortは配列にソートされたコピーをかえしますが、その外にも以下を利用できます
・argsort
: 指定された軸による間接ソート
・lexsort
: 複数のキーによる間接的な静的ソート[indirect stable sort on multiple keys]
・searchsorted
: ソートされた配列から要素を発見する
・partition
: 部分ソート
これらの配列を例にしましょう:
>>> a = np.array([1, 2, 3, 4])
>>> b = np.array([5, 6, 7, 8])
np.concatenate()でこれらの配列を連結することができます。
>>> np.concatenate((a, b))
array([1, 2, 3, 4, 5, 6, 7, 8])
また、この配列を例にとると、:
>>> x = np.array([[1, 2], [3, 4]])
>>> y = np.array([[5, 6]])
こうすることで連結できます:
>>> np.concatenate((x, y), axis=0)
array([[1, 2],
[3, 4],
[5, 6]])
配列から要素を削除するには、インデックスを使用して残したい要素を選択するのが簡単です。
連結についてさらに知りたい場合は、右を参照してください: concatenate
.
配列の形とサイズをどうやって知るか?
このセクションは ndarray.ndim
, ndarray.size
, ndarray.shape
を扱います
ndarray.ndim
は配列の軸の数、すなわち次元の数を示します。
ndarray.size
は配列の要素の総数を示します。これは配列のサイズの要素を掛けたものです。
ndarray.shape
は配列の次元ごとに格納されている要素の数を示す整数のタプルを表示します。たとえば、2行3列の2次元配列がある場合、配列の形状は(2,3)です。
たとえば、次の配列を作成するとします。
>>> array_example = np.array([[[0, 1, 2, 3],
... [4, 5, 6, 7]],
... [[0, 1, 2, 3],
... [4, 5, 6, 7]],
... [[0 ,1 ,2, 3],
... [4, 5, 6, 7]]])
配列の次元数を調べるには、次を実行します:
>>> array_example.ndim
3
配列の要素の総数を調べるには、次を実行します:
>>> array_example.size
24
そして配列の形状を調べるには、次を実行します:
>>> array_example.shape
(3, 2, 4)
配列を変形できますか?
この節では arr.reshape()
を扱います
もちろん!
arr.reshape()
を使えば、データを変更せずに配列に新しい形を与えることができます。この変形メソッドを使うときは、作りたい配列は元の配列と同じ要素数の必要があることを忘れないでください。12個の要素の配列を変形するなら、新たな配列もまた合計で12個の要素を持つことを確かめる必要があります。
この配列を用いるなら:
>>> a = np.arange(6)
>>> print(a)
[0 1 2 3 4 5]
配列を変形するためにreshape()
を使えます。たとえば、この配列を3行2列の配列に変形することができます:
>>> b = a.reshape(3, 2)
>>> print(b)
[[0 1]
[2 3]
[4 5]]
np.shape()
とともに、いくつかのパラメーターを指定することが可能です。:
>>> numpy.reshape(a, newshape=(1, 6), order='C')]
array([[0, 1, 2, 3, 4, 5]])
a
は形状を変更する配列です。
newshape
は新しい配列の形です。整数か整数のタプルを指定することができます。整数を指定した場合、その整数の長さの配列が生まれます。形状は元の形状と互換性をもつ必要があります。
order:
C
はCに似たインデックス順序で読み書きすることを表し、FはFortranに似たインデックス順序で読み書きすることを表します。Aは要素がメモリ上でFortran連続であれば、Fortranに似たインデックス順序を使い、そうでなければCに似たインデックスを使うことを意味します(これは任意のパラメーターであり、必ず指定する必要はありません)。
If you want to learn more about C and Fortran order, you can read more about the internal organization of NumPy arrays here. Essentially, C and Fortran orders have to do with how indices correspond to the order the array is stored in memory. In Fortran, when moving through the elements of a two-dimensional array as it is stored in memory, the first index is the most rapidly varying index. As the first index moves to the next row as it changes, the matrix is stored one column at a time. This is why Fortran is thought of as a Column-major language. In C on the other hand, the last index changes the most rapidly. The matrix is stored by rows, making it a Row-major language. What you do for C or Fortran depends on whether it’s more important to preserve the indexing convention or not reorder the data.
Learn more about shape manipulation here.
1D配列を2D配列に変換する方法(配列に新しいaxisを加える方法)
この節では np.newaxis
, np.expand_dims
を扱います
np.newaxis
と np.expand_dims
を使えば、既存の配列の次元を増やすことが可能です。
np.newaxis
を使用すると、一度だけ使用した場合には、配列の次元が一次元増加します。つまり、1D配列は2D配列に、2D配列は3D配列に、となります。
例えば、次の配列では、
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a.shape
(6,)
np.newaxis
を使って新たな軸を追加できます。:
>>> a2 = a[np.newaxis, :]
>>> a2.shape
(1, 6)
np.newaxis
を使うことで、行ベクトルか列ベクトルのどちからの1D配列を明示的に変形することができます。たとえば、第1次元上の軸を挿入することで、1D配列を行ベクトルに変換することができます。
>>> row_vector = a[np.newaxis, :]
>>> row_vector.shape
(1, 6)
また、列ベクトルには、第2次元上の軸を挿入できます:
>>> col_vector = a[:, np.newaxis]
>>> col_vector.shape
(6, 1)
np.expand_dims で指定した軸を挿入して、配列を拡張することも可能です。
たとえば、この配列では、:
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> a.shape
(6,)
インデックス位置1に軸を追加するにはnp.expand_dims
を使用できます。:
>>> b = np.expand_dims(a, axis=1)
>>> b.shape
(6, 1)
インデックス位置0に軸を追加するためには次のようにします。:
>>> c = np.expand_dims(a, axis=0)
>>> c.shape
(1, 6)
添え字アクセスとスライス(Indexing and slicing)
Numpy配列の添え字アクセスとスライスはPythonリストをスライスするのと同じ方法で行えます。
>>> data = np.array([1, 2, 3])
>>> data[1]
2
>>> data[0:2]
array([1, 2])
>>> data[1:]
array([2, 3])
>>> data[-2:]
array([2, 3])
さらなる分析や操作に使うために、配列の一部や配列の特定の要素を取り出す必要があるかもしれません。そのためには、配列をサブセット、スライス、そして/もしくは インデクスしなければならないでしょう。
配列から特定の条件を満たす値を抜き出したいなら、NumPyを使うと簡単です。
たとえば、次の配列を例にします。
>>> a = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
配列にある5未満の数値を簡単に表示できます.
>>> print(a[a < 5])
[1 2 3 4]
また、例えば5以上の数値を選択して、その条件を用いて配列のインデックスを作成することもできます。
>>> five_up = (a >= 5)
>>> print(a[five_up])
[ 5 6 7 8 9 10 11 12]
2で割れる要素たちを取り出すこともできます。:
>>> divisible_by_2 = a[a%2==0]
>>> print(divisible_by_2)
[ 2 4 6 8 10 12]
&
と |
演算子を使い、二つの条件を満たす要素をとりだすこともできます:
>>> c = a[(a > 2) & (a < 11)]
>>> print(c)
[ 3 4 5 6 7 8 9 10]
また、論理演算子**&** と | を使って、配列の値がある条件を満たすか否かを示すブール値を返すこともできます。これは名前や別カテゴリーの値を持つ配列のときに便利です。
>>> five_up = (a > 5) | (a == 5)
>>> print(five_up)
[[False False False False]
[ True True True True]
[ True True True True]]
np.nonzero()
を使い配列から要素の要素やインデクスを選択することも可能です。
次の配列を起点にしましょう:
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
np.nonzero()
を使って、この場合では5未満の、要素のインデクスを表示することができます:
>>> b = np.nonzero(a < 5)
>>> print(b)
(array([0, 0, 0, 0]), array([0, 1, 2, 3]))
この例では配列のタプルが返されます。返されるタプルはそれぞれの次元に一つずつです。第一の配列は条件を満たす値がある行インデクスを表し、第二の配列は条件を満たす値がある列インデクスを示します。
要素がある座標のリストを生成したい場合、この配列をzipし、座標のリストを反復処理して、表示することができます。たとえば、:
>>> list_of_coordinates= list(zip(b[0], b[1]))
>>> for coord in list_of_coordinates:
... print(coord)
(0, 0)
(0, 1)
(0, 2)
(0, 3)
np.nonzero()を使って配列中で5未満の要素を表示することもまたできます:
>>> print(a[b])
[1 2 3 4]
```shell
探している要素が配列に存在しない場合、戻り値のインデクスの配列は空になります。たとえば、以下のようになります。
```shell
>>> not_there = np.nonzero(a == 42)
>>> print(not_there)
(array([], dtype=int64), array([], dtype=int64))
既存のデータから配列をつくる方法
この節では slicing and indexing
, np.vstack()
, np.hstack()
,np.hsplit()
, .view()
, copy()
を扱います
既存の配列の部分からたやすく配列を作ることができます。
次の配列があるとしましょう。
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
配列のスライスしたい部分を指定することで、配列の部分からいつでも新しい配列を作ることができます。
>>> arr1 = a[3:8]
>>> arr1
array([4, 5, 6, 7, 8])
ここで、インデクス位置3からインデクス位置8までの範囲を指定しています。
既存の配列二つを連結するのは縦横どちらにもできます。次の二つの配列、a1
,a2
があるとします。
>>> a1 = np.array([[1, 1],
... [2, 2]])
>>> a2 = np.array([[3, 3],
... [4, 4]])
vstack
を使って、これらを縦に積み重ねることができます。
>>> np.vstack((a1, a2))
array([[1, 1],
[2, 2],
[3, 3],
[4, 4]])
そしてhstack
で横に積み重ねることができます。
>>> np.hstack((a1, a2))
array([[1, 1, 3, 3],
[2, 2, 4, 4]])
hsplit
で配列をいくつかの小さな配列に分割することができます。配列を同形配列何個に分割するかや、分割後の列の数を指定することができます。
この配列があるとしましょう:
>>> x = np.arange(1, 25).reshape(2, 12)
>>> x
array([[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]])
もしあなたがこの配列を3つの同じ形の配列へと分割したいなら、次のコードを実行しましょう。
>>> np.hsplit(x, 3)
[array([[1, 2, 3, 4],
[13, 14, 15, 16]]), array([[ 5, 6, 7, 8],
[17, 18, 19, 20]]), array([[ 9, 10, 11, 12],
[21, 22, 23, 24]])]
もし3列目と4列目の後ろで配列を分割したいなら、次のコードを実行しましょう。
>>> np.hsplit(x, (3, 4))
[array([[1, 2, 3],
[13, 14, 15]]), array([[ 4],
[16]]), array([[ 5, 6, 7, 8, 9, 10, 11, 12],
[17, 18, 19, 20, 21, 22, 23, 24]])]
Learn more about stacking and splitting arrays here.
view
メソッドを使って、元の配列と同じデータを参照する新たな配列を作ることができます(浅いコピー:shallow copy)。
ビューはNumPyの重要な概念の一つです。NumPyの関数は、添字アクセスやスライスなどの操作と同様に、可能な限りビューを返します。これはメモリの節約になり、高速です(データのコピーを作成する必要がありません)。しかし注意しなければならないことがあります––ビュー内のデータを変更すると、元の配列も変更されてしまいます。
次のような配列を作成したとしましょう。
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
ここでa
をスライスしてb1
をつくり、b1
の最初の要素を変更します。この操作はa
の対応する要素をも変更します。
>>> b1 = a[0, :]
>>> b1
array([1, 2, 3, 4])
>>> b1[0] = 99
>>> b1
array([99, 2, 3, 4])
>>> a
array([[99, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
copy
メソッドを使用すると、配列とそのデータの完全なコピーを作成します(ディープコピー)。これを配列に使用するには、次のコードを実行します。
>>> b2 = a.copy()
配列の基本的演算(operation)
この節では加算、減算、乗算、除算などを扱います
配列を作成したら、その配列で作業を始めることができます。例えば、”data”と”ones”と呼ばれる二つの配列を作成したとしましょう。
プラス記号を使って配列を足し合わせることができます。
>>> data = np.array([1, 2])
>>> ones = np.ones(2, dtype=int)
>>> data + ones
array([2, 3])
もちろん、できることは加算だけに留まりません!
>>> data - ones
array([0, 1])
>>> data * data
array([1, 4])
>>> data / data
array([1., 1.])
NumPyでは基本的操作は簡単です。もし配列の合計を知りたいなら、sum()を使いましょう。これは1D, 2Dやそれ以上の配列で動きます。
>>> a = np.array([1, 2, 3, 4])
>>> a.sum()
10
二次元配列で列や行を加算したいなら(To add the rows or the columns in a 2D array)、軸を指定します。
この配列から始める場合、:
>>> b = np.array([[1, 1], [2, 2]])
行は次のように合計できます:
>>> b.sum(axis=0)
array([3, 3])
列は次のように合計できます。:
>>> b.sum(axis=1)
array([2, 4])
Broadcasting
配列とひとつの数とで、あるいは異なるサイズの配列間で、演算したい時があります(前者はベクトルとスカラー間の演算とも呼ばれます)。たとえば、ある配列(”data”と呼びます)は何マイル離れているかの情報を持っており、あなたはそれをキロメートルに変換したいものとします。この操作を次のように行うことができます。
>>> data = np.array([1.0, 2.0])
>>> data * 1.6
array([1.6, 3.2])
NumPyは掛け算が一つ一つのセルで行われなければならないことを了解しています。このコンセプトはブロードキャストと呼ばれます。ブロードキャステトはNumPyが相異なる形の配列の演算を行うための仕組みです。配列の次元は互換性がなければなりません。例えば、両方の配列の次元が同じか、片方が1次元の場合にそうです。もしそうでなければ、ValueError
が発生します。
より便利な配列操作
この節では、maximum
, minimum
, sum
, mean
, product
, standard deviation
などを扱います
NumPyは集合関数も実行します。min
, max
, sum
に加えて、平均を得るためのmean
, 要素をかけ合わせた結果を得るためのprod
, 標準偏差を得るためのstd
などを簡単に実行できます。
>>> data.max()
2.0
>>> data.min()
1.0
>>> data.sum()
3.0
まずこの配列、“a”から始めましょう
>>> a = np.array([[0.45053314, 0.17296777, 0.34376245, 0.5510652],
... [0.54627315, 0.05093587, 0.40067661, 0.55645993],
... [0.12697628, 0.82485143, 0.26590556, 0.56917101]])
行や列に沿って集計したいというのは非常によくあることです。デフォルトでは、NumPy集計関数はすべて、配列全体の総和を返します。配列の要素の合計や最小値を知りたいときは、次のコードを用います。
>>> a.sum()
4.8595784
あるいは:
>>> a.min()
0.05093587
どの軸で集計関数をさせたいのか指定することができます。たとえば、axis=0とすることで、各列内の最小値を見つけることができます。
>>> a.min(axis=0)
array([0.12697628, 0.05093587, 0.26590556, 0.5510652 ])
上記の4つの数値は元の配列の行の数値と一致しています。4行の配列では、結果として、4つの値を得ることができます。
行列を作る
Pythonリストを渡して、NumPyでその配列を表す2D配列(または「行列」)を作ることができます。
>>> data = np.array([[1, 2], [3, 4]])
>>> data
array([[1, 2],
[3, 4]])
添え字アクセスとスライス操作は、行列を扱う際に便利です。
>>> data[0, 1]
2
>>> data[1:3]
array([[3, 4]])
>>> data[0:2, 0]
array([1, 3])
行列の操作はベクトルの操作と同じ方法でできます。
>>> data.max()
4
>>> data.min()
1
>>> data.sum()
10
行列内のすべての値を集計することも、軸パラメータを使って列か行をまたいで[across columns or rows]集計することができます。
>>> data.max(axis=0)
array([3, 4])
>>> data.max(axis=1)
array([2, 4])
行列を作成したら、同じサイズの行列が二つあれば、算術演算子を使って足し算や掛け算を行えます。
>>> data = np.array([[1, 2], [3, 4]])
>>> ones = np.array([[1, 1], [1, 1]])
>>> data + ones
array([[2, 3],
[4, 5]])
異なるサイズの行列に対してもこれらの算術演算をすることができますが、一方の行列が一行か一列しかない場合に限ります。この場合、NumPyは演算のためにブロードキャスト規則を使います。
>>> data = np.array([[1, 2], [3, 4], [5, 6]])
>>> ones_row = np.array([[1, 1]])
>>> data + ones_row
array([[2, 3],
[4, 5],
[6, 7]])
NumPyがN次元の配列を表示するとき、最後の軸は最もループし、対して最初の軸は緩やかにループすることに注意してください[次の例では最後の軸である列に関しては12回のループが、最初の軸については4回のループがある]。
たとえば、
>>> np.ones((4, 3, 2))
array([[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]]])
NumPyで配列を初期化したい場合がよくあります。NumPyはones()
やzeros()
といった関数や、乱数生成のためのrandom.Generator
クラスを提供しています。初期化に必要なのは、ただ生成してほしい要素数を渡すことだけです。
>>>np.ones(3)
array([1., 1., 1.])
>>> np.zeros(3)
array([0., 0., 0.])
# the simplest way to generate random numbers
>>> rng = np.random.default_rng(0)
>>> rng.random(3)
array([0.63696169, 0.26978671, 0.04097352])
この関数やメソッドに二次元の行列を表すタプルを与えれば、ones()
、zeros()
と random()
を使って二次元配列も生成可能です。
>>> np.ones((3, 2))
array([[1., 1.],
[1., 1.],
[1., 1.]])
>>> np.zeros((3, 2))
array([[0., 0.],
[0., 0.],
[0., 0.]])
>>> rng.random((3, 2))
array([[0.01652764, 0.81327024],
[0.91275558, 0.60663578],
[0.72949656, 0.54362499]]) # may vary
乱数を生成する
乱数生成の使用は多くの数学的あるいは機械学習アルゴリズムの配置や評価の重要なパートです。人工神経ネットワークの重さのランダムな初期化、ランダムなセットへの分割、あるいはデータセットのランダムシャッフル、どれにせよ、乱数(実際には、再現可能な擬似乱数の数)の生成をできることは欠かせません。Generator.integers
を使って、最小値から最大値までのランダムな整数を出力できます(Numpyでは最小値は含まれ、最大値は含まれない事に注意してください)。endpoint=True
を設定して、最高値を含む乱数を生成することができます。
0から4までのランダムな整数から成る2×4行列を生成することができます:
>>> rng.integers(5, size=(2, 4))
array([[2, 1, 1, 0],
[0, 0, 0, 4]]) # may vary
重複しない要素を取り出し数える方法
この節では np.unique()
を扱います
Np.unique
で配列の要素を重複なしに一つずつ取り出すことができます。
たとえば、この配列を例にします。
>>> a = np.array([11, 11, 12, 13, 14, 15, 16, 17, 12, 13, 11, 14, 18, 19, 20])
Np.unique
を使って配列中のユニークな値を知ることができます。
>>>
>>> unique_values = np.unique(a)
>>> print(unique_values)
[11 12 13 14 15 16 17 18 19 20]
Numpy配列のユニークな値のインデクス(配列のユニークな値それぞれの最初のインデックス)を得るためには、np.unique()
に配列とともにreturn_index
引数を渡してください。
>>> unique_values, indices_list = np.unique(a, return_index=True)
>>> print(indices_list)
[ 0 2 3 4 5 6 7 12 13 14]
Numpy配列のユニークな値がそれぞれいくつあるかをしるために、np.unique()
に配列とともにreturn_counts
引数をわたすことができます。
>>> unique_values, occurrence_count = np.unique(a, return_counts=True)
>>> print(occurrence_count)
[3 2 2 2 1 1 1 1 1 1]
これは二次元配列でも作用します! この配列を用いた場合、
>>> a_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [1, 2, 3, 4]])
こうしてユニークな値を見つけることができます。
>>> unique_values = np.unique(a_2d)
>>> print(unique_values)
[ 1 2 3 4 5 6 7 8 9 10 11 12]
軸引数が渡されなかった場合、二次元配列は一次元に平坦化されます。
もしユニークな行や列を知りたい場合、必ずaxis引数をわたすようにしてください。ユニークな行をしるためにはaxis=0
を指定し、列にはaxis=1
を指定してください。
>>> unique_rows = np.unique(a_2d, axis=0)
>>> print(unique_rows)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
ユニークな列やインデックス位置や出現数を得るためには、以下のようにします:
>>> unique_rows, indices, occurrence_count = np.unique(
... a_2d, axis=0, return_counts=True, return_index=True)
>>> print(unique_rows)
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
>>> print(indices)
[0 1 2]
>>> print(occurrence_count)
[2 1 1]
行列の転置と変形
この節では arr.reshape()
, arr.transpose()
, arr.T()
を扱います
行列の転置を必要とすることはしばしばあります。Numpy配列は行列を転置させるプロパティT
を持ちます。
また、配列の次元を入れ替える必要があるかもしれません。これはたとえば、データセットと異なる入力の配列形を想定したモデルがある場合などに起こります。このような場合にreshapeメソッドが役に立ちます。必要なのは、行列に必要な新しい寸法[dimensions]を渡すだけです。
>>> data.reshape(2, 3)
array([[1, 2, 3],
[4, 5, 6]])
>>> data.reshape(3, 2)
array([[1, 2],
[3, 4],
[5, 6]])
.transpose
を使って、指定した値に従い、配列の軸を反転させたり変更したりすることもできます。
この配列を例にとりましょう:
>>> arr = np.arange(6).reshape((2, 3))
>>> arr
array([[0, 1, 2],
[3, 4, 5]])
arr.transpose()
を使って配列を転置できます。
>>> arr.transpose()
array([[0, 3],
[1, 4],
[2, 5]])
配列を反転させる方法
この節は np.flip
を扱います
NumPyのnp.flip()
はaxisを基準に、配列の軸を反転させることができます。np.flip()
を使うときは反転させたい配列と軸を指定してください。軸を指定しない場合、NumPyは与えられた配列をすべての軸に関して反転させます。
1D配列を反転させる
次のような一次元配列を例にとりましょう:
>>> arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
こうやって配列を反転できます:
>>> reversed_arr = np.flip(arr)
反転された配列を表示したいなら、このコードを実行しましょう:
>>> print('Reversed Array: ', reversed_arr)
Reversed Array: [8 7 6 5 4 3 2 1]
2D配列を反転させる
2D配列も、ほぼ同じやり方で反転します。
この配列を例にとりましょう:
>>> arr_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
すべての列と行で内容を反転させることができます。
>>> reversed_arr = np.flip(arr_2d)
>>> print(reversed_arr)
[[12 11 10 9]
[ 8 7 6 5]
[ 4 3 2 1]]
行だけを反転するのはこうするだけです:
>>> reversed_arr_rows = np.flip(arr_2d, axis=0)
>>> print(reversed_arr_rows)
[[ 9 10 11 12]
[ 5 6 7 8]
[ 1 2 3 4]]
列だけを反転するには:
>>> reversed_arr_columns = np.flip(arr_2d, axis=1)
>>> print(reversed_arr_columns)
[[ 4 3 2 1]
[ 8 7 6 5]
[12 11 10 9]]
一つの行や列だけを反転させることもできます。たとえば、インデックスが1の行(2行目)を反転させることできます:
>>> arr_2d[1] = np.flip(arr_2d[1])
>>> print(arr_2d)
[[ 1 2 3 4]
[ 8 7 6 5]
[ 9 10 11 12]]
インデックスが1の列(2列目)を反転させることもできます:
>>> arr_2d[:,1] = np.flip(arr_2d[:,1])
>>> print(arr_2d)
[[ 1 10 3 4]
[ 8 7 6 5]
[ 9 2 11 12]]
多次元配列を再整形・平坦化させる方法
この節では .flatten()
, ravel()
を扱います
配列をフラットにする一般的な方法は二つあります。.flatten()
と .ravel()
です。この二つの主要な違いは、.ravel()を使って作られた配列は、実は親配列への参照(つまり「ビュー」)なのです。したがって、新しい配列を何か変更すると、親配列もまた同様に変更されるということになります。ravel
はコピーを作らないので、メモリ効率が良いです。
>>> x = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
flatten
を使って、配列を1D配列にすることができます。
>>> x.flatten()
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
flatten
‘を使うと、配列への変更は親配列に適用されません。
たとえば:
>>> a1 = x.flatten()
>>> a1[0] = 99
>>> print(x) # Original array
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
>>> print(a1) # New array
[99 2 3 4 5 6 7 8 9 10 11 12]
しかしravel
を使うと、配列への変更は親配列に適用されません。
>>> a2 = x.ravel()
>>> a2[0] = 98
>>> print(x) # Original array
[[98 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
>>> print(a2) # New array
[98 2 3 4 5 6 7 8 9 10 11 12]
docstringにアクセスして詳細を知る
この節では help()
, ?
, ??
を扱います。
データサイエンスエコシステムに関しては[when it comes to]、PythonとNumPyはユーザーのことを念頭に置いて作られています。このことの良い例の一つが、ドキュメンテーションへのアクセスが備え付けられていることです。すべてのオブジェクトは文字列への参照があり、その文字列はdocstringとして知ら得ています。たいていの場合、このdocstringは手短かつ簡潔なオブジェクトの概要と使い方を含みます。Pythonは組み込みのhelp関数を持ち、これはdocstringにアクセスするのを手助けします。つまり、より情報が必要なときはたいてい、help()を使って必要な情報をすぐに見つけることができます。
たとえば、
>>> help(max)
Help on built-in function max in module builtins:
max(...)
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value
With a single iterable argument, return its biggest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the largest argument.
更なる情報へのアクセスはなかなか役立つので、IPythonは?をドキュメンテーションと関連した他の情報にアクセスするための略号として用います。IPythonは複数の言語で使える、インタラクティブな計算を行うためのコマンドシェルです。IPythonについてのさらなる情報はこちら。
たとえば、
In [0]: max?
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value
With a single iterable argument, return its biggest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the largest argument.
Type: builtin_function_or_method
この表記法を、オブジェクトメソッドやオブジェクトそのものにさえ使うことができます。
次の配列を作ったとしましょう。
>>> a = np.array([1, 2, 3, 4, 5, 6])
すると、たくさんの役立つ情報を得ることができます(最初にオブジェクトそのものの詳細、次にaがインスタンスであるndarrayのdocstringが続きます)。
In [1]: a?
Type: ndarray
String form: [1 2 3 4 5 6]
Length: 6
File: ~/anaconda3/lib/python3.7/site-packages/numpy/__init__.py
Docstring: <no docstring>
Class docstring:
ndarray(shape, dtype=float, buffer=None, offset=0,
strides=None, order=None)
An array object represents a multidimensional, homogeneous array
of fixed-size items. An associated data-type object describes the
format of each element in the array (its byte-order, how many bytes it
occupies in memory, whether it is an integer, a floating point number,
or something else, etc.)
Arrays should be constructed using `array`, `zeros` or `empty` (refer
to the See Also section below). The parameters given here refer to
a low-level method (`ndarray(...)`) for instantiating an array.
For more information, refer to the `numpy` module and examine the
methods and attributes of an array.
Parameters
----------
(for the __new__ method; see Notes below)
shape : tuple of ints
Shape of created array.
...
これはあなたが作成した関数や他のオブジェクトにも作用します。ただ。関数内に文字リテラルを使ってdocstringを入れるのを忘れないで下さい(""" """
か ''' '''
でドキュメンテーションを囲む)。
たとえば、次の関数を作った場合、
>>> def double(a):
... '''Return a * 2'''
... return a * 2
この関数について情報を得るには、次のようにします。
In [2]: double?
Signature: double(a)
Docstring: Return a * 2
File: ~/Desktop/<ipython-input-23-b5adf20be596>
Type: function
関心あるオブジェクトのソースコードを読むことで、また違った水準の情報に触れることができます。二重疑問符(??)を使うことで、ソースコードにアクセスできます。
たとえば、
In [3]: double??
Signature: double(a)
Source:
def double(a):
'''Return a * 2'''
return a * 2
File: ~/Desktop/<ipython-input-23-b5adf20be596>
Type: function
当該のオブジェクトがPython以外の言語でコンパイルされている場合、??を使うと?と同じ情報が返ってきます。このことは、たくさんの組み込みオブジェクトやタイプで見ることができます。たとえば、
In [4]: len?
Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type: builtin_function_or_method
そして :
In [5]: len??
Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type: builtin_function_or_method
これらが同一の出力なのは、これらがPythonではない言語でコンパイルされているからです。
数式を扱う
NumPyが科学のPythonコミュニティでここまで広く使われている理由の一つに、配列上で作用する数式を簡単に実装できることがあります。
たとえば、これが平均二乗誤差(回帰を扱う教師付き機械学習モデルで使われる中心的な公式)です。
この式の実装はNumPyではシンプルで式そのままです:
error = (1/n) * np.sum(np.square(predictions - labels))
これが非常にうまく機能するのは、予測値とラベルが1個でも1000個の値でも含むことができるからです。必要なのは、予測値とラベルが同じサイズであることだけです。
これは次のように可視化することができます。
この例では、予測とラベルは3つの値をもつベルトルであり、それゆえnは3という値をとります。引き算を行った後、ベクトルの値は二乗されます。そしてNumPyが値を合計し、その結果が予測値の誤差とモデルの品質のスコアとなります。
NumPyをセーブ・ロードする方法
この節は np.save
, np.savez
, np.savetxt
, np.load
, np.loadtxt
を扱います
いつか、配列をディスクに保存して、コードをもう一度実行することなくロードしたいと思うことがあるでしょう。さいわい、NumPyではオブジェクトを保存したりロードしたりする方法がいくつかあります。ndarrayオブジェクトは、loadtxt
と savetxt
関数で通常のテキストファイルをロード・セーブし、load
and save
関数で.npz拡張子のNumPyバイナリファイルを扱うことができます。そして、savez
関数で.npz拡張子のNumpyファイルを扱うことができます。
.npyと .npzファイルは、ファイルが異なるアーキテクチャ上でも正しく復旧できるように、データ、形状、dtypeやその他の、ndarrayを再構築するために必要な情報を格納しています。
1つのndarrayオブジェクトを保存したい場合、np.saveを使って.npyファイルとして保存してください。複数のndarrayを配列に保存したい場合、np.savezを使って.npzとして保存してください。また、savez_compressedを用いて圧縮されたnpz形式で保存することで、複数の配列を1つのファイルに保存することもできます。
np.save()
でセーブ・ロード・配列するのは簡単です(It’s easy to save and load and array with np.save().)。保存したい配列とファイルネームを指定することを忘れないでください。たとえば、この配列をつくった場合、
>>> a = np.array([1, 2, 3, 4, 5, 6])
"filename.npy"として保存できます。
>>> np.save('filename', a)
np.load()で配列を復元できます。
>>> b = np.load('filename.npy')
もし配列を確かめたいなら、このコードを実行すればよいです。
>>> print(b)
[1 2 3 4 5 6]
np.savetxtを使えば、.csvや.txtファイルのようなプレーンテキストとしてNumPyファイルを保存できます。
たとえば、次の配列を作った場合、
>>> csv_arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
```shell
You can easily save it as a .csv file with the name “new_file.csv” like this:
```shell
>>> np.savetxt('new_file.csv', csv_arr)
loadtxt()を使って、簡単に保存したテキストファイルを読み込めます。
>>> np.loadtxt('new_file.csv')
array([1., 2., 3., 4., 5., 6., 7., 8.])
savetxt()とloadtxt()関数はヘッダー、フッター、区切り文字といった追加パラメーターを受け付けます。テキストファイルは共有に便利な一方で、.npyと.npzファイルは小さく、読み書きが高速です。もしテキストファイルのもっと洗練された取り扱いが必要なら(たとえば欠損値を含む行列[lines]を扱う場合)、genfromtxt
関数を使う必要があるでしょう。
savetxt
を使うとき、ヘッダー、フッター、コメントなどを指定することができます。
Learn more about input and output routines here.
CSVをインポート・エクスポートする方法
既存の情報が入っているCSVファイルを読み込むのは簡単です。最も簡単な方法はPandasを使うことです。
>>> import pandas as pd
>>> # If all of your columns are the same type:
>>> x = pd.read_csv('music.csv', header=0).values
>>> print(x)
[['Billie Holiday' 'Jazz' 1300000 27000000]
['Jimmie Hendrix' 'Rock' 2700000 70000000]
['Miles Davis' 'Jazz' 1500000 48000000]
['SIA' 'Pop' 2000000 74000000]]
>>> # You can also simply select the columns you need:
>>> x = pd.read_csv('music.csv', usecols=['Artist', 'Plays']).values
>>> print(x)
[['Billie Holiday' 27000000]
['Jimmie Hendrix' 70000000]
['Miles Davis' 48000000]
['SIA' 74000000]]
配列のエクスポートもPandasを使えば簡単です。NumPyに慣れていない方は、配列の値からPandasデータフレームを作り、そのデータフレームをPandasでCSVファイルに書き出すのが良いでしょう。
配列"a"を作ったとしましょう。
>>> a = np.array([[-2.58289208, 0.43014843, -1.24082018, 1.59572603],
... [ 0.99027828, 1.17150989, 0.94125714, -0.14692469],
... [ 0.76989341, 0.81299683, -0.95068423, 0.11769564],
... [ 0.20484034, 0.34784527, 1.96979195, 0.51992837]])
Pandasデータフレームを次のように作成できます。
>>> df = pd.DataFrame(a)
>>> print(df)
0 1 2 3
0 -2.582892 0.430148 -1.240820 1.595726
1 0.990278 1.171510 0.941257 -0.146925
2 0.769893 0.812997 -0.950684 0.117696
3 0.204840 0.347845 1.969792 0.519928
次のようにしてデータフレームを保存できます。
>>> df.to_csv('pd.csv')
CSVはこう
>>> data = pd.read_csv('pd.csv')
NumPyのsavetxtメソッドを使って保存することもできます。
>>> np.savetxt('np.csv', a, fmt='%.2f', delimiter=',', header='1, 2, 3, 4')
コマンドラインを使っているなら、次のようなコマンドで保存したCSVをいつでも読み込むことができます。
$ cat np.csv
# 1, 2, 3, 4
-2.58,0.43,-1.24,1.60
0.99,1.17,0.94,-0.15
0.77,0.81,-0.95,0.12
0.20,0.35,1.97,0.52
または、テキストエディタでいつでも開くことができます。
Pandasについてもっと詳しく知りたい方は、official Pandas documentationを見てみてください。Pandasをインストールする方法については、official Pandas installation informationを参照してください。
Matplotlibで配列をプロットする
値のプロットを作成する必要がある場合、Matplotlibを使用すると非常に簡単です。
たとえば、このような配列があるかもしれません。
>>> a = np.array([2, 1, 5, 7, 4, 6, 8, 14, 10, 9, 18, 20, 22])
Matplotlibをすでにインストールしているなら、このようにインポートできます。
>>> import matplotlib.pyplot as plt
# If you're using Jupyter Notebook, you may also want to run the following
# line of code to display your code in the notebook:
%matplotlib inline
値をプロットするには、面倒な操作はいりません。
>>> plt.plot(a)
# If you are running from a command line, you may need to do this:
# >>> plt.show()
たとえば、1D配列を次のようにプロットできます。
‘>>> x = np.linspace(0, 5, 20)
>>> y = np.linspace(0, 10, 20)
>>> plt.plot(x, y, 'purple') # line
>>> plt.plot(x, y, 'o') # dots
Matplotlibでは、膨大な数の可視化オプションを使うことができます。
>>> from mpl_toolkits.mplot3d import Axes3D
>>> fig = plt.figure()
>>> ax = Axes3D(fig)
>>> X = np.arange(-5, 5, 0.15)
>>> Y = np.arange(-5, 5, 0.15)
>>> X, Y = np.meshgrid(X, Y)
>>> R = np.sqrt(X**2 + Y**2)
>>> Z = np.sin(R)
>>> ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis')
To read more about Matplotlib and what it can do, take a look at the official documentation. For directions regarding installing Matplotlib, see the official installation section.
mage credits: Jay Alammar http://jalammar.github.io/