ラベル IPython の投稿を表示しています。 すべての投稿を表示
ラベル IPython の投稿を表示しています。 すべての投稿を表示

2019年8月17日土曜日

numpyを使った行列の計算

最近線形代数を復習している。学生の時の講義で単位は取ったはずだが、もう様々なことがうろ覚えになってしまっている。とりあえず、先月にごく簡単な本を一冊読んだ。色々とうろ覚えだったことがはっきりしてきた。ただ単に復習をしているだけのはずだが、結構楽しい(むしろ、学生の時よりも楽しいのはなぜだろう?)。これなら、まだ時間も記憶力もあった(はずの)20代の頃にもっと勉強しておけばよかったと悔やむ気持ちだ。

大学の講義では、章末問題などを解きながら計算を覚えていくが、今回は少し違ったアプローチを取って、傍らでpythonによる計算をしながら行列演算の概念を押さえようと思っている。pythonで行列の演算をするには、いくつか方法があるようだ。ごく簡単に行列っぽい扱いをするには、普通にリストが要素となっているリストを作れば良い。
List1 = [[1,2,3],[4,5,6],[7,8,9]]
print(List1)
# [[1, 2, 3], [4, 5, 6]]
これは今までにも使ってきたやり方だが、行列の演算をするには不便だ。pythonの場合、numpyのarrayやmatrixがあり、これを使えば計算ができるようだ。どちらも基本的に同じことができるようだが、名前の通り、numpy.matrixの方が二次元の行列に特化したクラスのようだ。

まずはnumpyをインポートする。定石の方法に従ってnpとしてインポートする。行列を作るときは以下のようにする。
import numpy as np
ExArr1 = np.array([[1,2,3],[4,5,6]])
print(ExArr1)
# [[1 2 3]
#  [4 5 6]]

ここでは、最初の[1,2,3]が一行目、[4,5,6]が二行目となる。または、matrixを使い、
ExMat1 = np.matrix([[1,2,3],[4,5,6]])
print(ExMat1)
# [[1 2 3]
#  [4 5 6]]
となる。
行列の積を求めるときは、numpy.dot()、numpy.matmul()、または@が使える。
Arr1 = np.array([[1,2,3],[4,5,6],[7,8,9]])
Arr2 = np.array([[1,0,0],[0,1,0],[0,0,1]])
print(np.dot(Arr1, Arr2))
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
print(np.matmul(Arr1, Arr2))
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
print(Arr1 @ Arr2)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
np.matrixについては、*でも行列の積が計算できる。注意するべきは、numpy.ndarrayでは、*を使うと行列の要素ごとの積になることだ。
Mat1 = np.matrix([[1,2,3],[4,5,6],[7,8,9]])
Mat2 = np.matrix([[1,0,0],[0,1,0],[0,0,1]])
print(Mat1*Mat2)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
print(Arr1*Arr2)
# [[1 0 0]
#  [0 5 0]
#  [0 0 9]]
転置行列は.Tで計算できる。
print(Arr1.T)
# [[1 4 7]
#  [2 5 8]
#  [3 6 9]]
逆行列も簡単に計算できる。**-1と.Iが使えるのはnumpy.matrixの方だけで、numpy.ndarrayでは.linalg.invだけが使える。
mat1 = np.matrix([[2,3],[1,4]])
print(mat1)
# [[2 3]
#  [1 4]]
print(mat1.I)
# [[ 0.8 -0.6]
#  [-0.2  0.4]]
print(mat1**-1)
# [[ 0.8 -0.6]
#  [-0.2  0.4]]
print(np.linalg.inv(mat1))
# [[ 0.8 -0.6]
#  [-0.2  0.4]]
こういうところが、pythonを横で書きながら勉強していった方が捗るような気がする。例えば、逆行列をかけると単位行列になるのは簡単に確かめることができる。
print(mat1 * mat1.I)
# [[1. 0.]
#  [0. 1.]]
print(mat1.I * mat1)
# [[1. 0.]
#  [0. 1.]]
こういう細かい確かめをしながら進めていくのは結構有効なのでは、と思ったのだ。教科書の例題をスクリプトに書き起こしながら読んでいくとか。そういう方法での授業や講義はされているのだろうか?

固有値問題の場合は、numpy.linalg.eig()を使う。
mat1 = np.matrix([[1,2],[-1,4]])
w, v = np.linalg.eig(mat1)
print(w)
# [2. 3.]
print(v)
# [[-0.89442719 -0.70710678]
#  [-0.4472136  -0.70710678]]
固有値がw、それぞれの固有値に対応する固有ベクトルがvの列になっている。これを使い、
print(v**-1 * mat1 * v)
# [[2. 0.]
#  [0. 3.]]
が簡単に計算してみることができる。
pythonの計算の仕方についてはこの方法で教科書の例をそのままスクリプトに書きおこす方式で一通りやってみようと思う。スクリプトを書いて計算する分には、証明を斜め読みにしながら進んでいくことも可能だろう。学び方として褒められたものではないかもしれないが。

参考:
https://note.nkmk.me
nkmkさんのサイトが充実していて、いつも参考にさせていただいている。

2019年5月5日日曜日

IPythonのシェルコマンド実行が便利

あまり機能として使っていなかったが、コマンドラインで動作する全てのコマンドは、!のマークを前におくことでIPythonのコンソールから実行できる。
  # IPython
  !cd /Users/Me/wd/
  !ls -lh

これが地味に便利で感動している。これまでは、カーネルを変更してbashのコマンドを実行していたが、最近はpython3のカーネルのままで必要なコマンドを実行するようになった。インストールした解析用コマンドも、IPythonコンソールから動かずに実行することができるので楽だ。
シェルとの間で、値を受け渡す時は、代入演算子を利用する。
  # IPython
  !cd /current/data/dir/
  Values = !ls


逆に、python側で使っている変数をシェル側に渡す事もできて、その時は、{変数名}を使う。
  # IPython
  Val1 = 123549
  !echo {Val1}

  123549
注意点は、IPythonからシェルコマンドを使うときに、使用できないコマンドがある事。例えば、!cdとするとにホームディレクトリに移動することを期待するかもしれないが、実際にはディレクトリの移動はできていない。これを知らないと、例えば標準出力で保存したと思っていたファイルが、予期していたところとは別のディレクトリに保存されていたりする。最初わからなかったが、本を読むと、IPythonから実行されるシェルのコマンドは一時的なサブシェルで実行されることが原因らしい。こういったコマンドは、Magicコマンドとして実行する必要がある。マジックコマンドにするには、頭に%をつける。
  # IPython
  %cd /Path/To/My/data/
  
実際には、%をつけずに実行すると、automagic機能が働いて自動的にMagicコマンドに切り替えられるらしい。対象は、cat, cp, env, ls, mkdir, mv, pwd, rm, rmdirなど。

2019年5月2日木曜日

Jupyter notebookで、condaで作った環境を使えるようにする

Jupyter notebookを使って解析することで、一つ一つの解析の結果を見ながら次のコマンドを叩くことができる。また、標準出力などの結果を残すことができる。Markdownを使って解析についてのメモを残す事もできるため、解析の時の思考の流れも含めて記録を残すことができるのもとても便利だと思う。一つ一つのセルに分けて実行していくスタイルも、解析の1ステップを塊で捉えることができて分かりやすい。

こういった理由で、解析のときにJupyter notebookを多用するようになった。こちらの情報にもあるようにJupyterでbashやR、または異なるバージョンのpythonなど、複数のカーネルを使う事もできる。これをするとjupyterの中でRの統計検定や作図でデータを見てみることができるのでかなり便利だった。解析で複数のカーネルを使いたいときにも、ファイルを分けずに一つのnotebookにまとめることができる。

ただ、bashを使っているときに問題が起きた。anacondaでいくつか環境を作って、解析用のプログラムを導入していたが、condaの環境をJupyterでコマンドを打って変えようとしても上手く行かずに以下のようなエラーが出てしまう。
CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.
To initialize your shell, run

    $ conda init 

Currently supported shells are:
  - bash
  - fish
  - tcsh
  - xonsh
  - zsh
  - powershell

See 'conda init --help' for more information and options.

IMPORTANT: You may need to close and restart your shell after running 'conda init'.
色々調べていくと、condaで作成した仮想環境をJupyter Notebookで使えるようにする方法があったので設定した。
設定の概要は、
[1] condaの新しい環境を作る(既存環境が存在する場合は不要)
conda create -n newenv python=3.7
# この環境に入る
conda activate newenv

# jupyterをインストールする
pip install jupyter

# jupyter_environment_kernelsをインストールする
pip install environment_kernels
[2] Jupyterの設定ファイルを生成する。
jupyter notebook --generate-config

"""
この作業により、ホームディレクトリの下の.jupyterの下に
jupyter_notebook_config.pyというファイルができる。

この作業は一回やれば良い。2回目、3回目の環境の設定の時には必要ない
"""
[3] 設定ファイルに、自動でconda環境を読み込む設定を追記する。
エディタでファイルを開き、以下を追記する。
c.NotebookApp.kernel_spec_manager_class = 'environment_kernels.EnvironmentKernelSpecManager' 
c.EnvironmentKernelSpecManager.env_dirs=['/Users/username/anaconda/envs/']

これで、Jupyterを再起動すれば、カーネルとしてnewenvを選択することができるようになる。カーネルは、Kernel > Change kernelから選択できる。
注意点は、それぞれの環境を使えるようにしたければ、その環境でjupyterとjupyter_environment_kernelが入っていないといけないこと。それぞれの環境をactivateして、上の設定を行う。
こうすることで、解析や依存環境ごとに複数の環境を作っていても、それらをjupyterで使えるようになる。

参考:
https://kazusa-pg.com/jupyter-notebook-use-virtual-env-kernel/
https://qiita.com/yoppe/items/38005f415a5b8b884c7d