おっぱいそん!

pythonを数値計算に使うときのテクニックとかをまとめていきたい。

MacBook Pro (Mid 2012, 13インチ)のSATA cableの交換

2週間ほど前に、MACのOSが立ち上がらなくなりました。
結論から言うと、SATAケーブルを交換したら治ったのですが、その記録をここに書いておきます。
*特にMACBookPro Mid 2012モデルの方向けの追記をしました。

時系列

  • MACを久々に再起動させようとすると、OSが起動しなくなってしまいました。最初はソフト的な問題かと思い、PRAMのリセットやディスクの修復などWebにある対処法を一通りやってみましたが、ダメでした。
  • 次に、データやアプリケーションは消さずに(HDDをフォーマットせずに)OSXのみの再インストールを試みましたが、これも失敗しました。そこで、諦めて、HDDをフォーマットしてしまうことにしました。重要なデータはDropBox等に置いているのですが、バックアップを取っていないデータもあったので、救出しました。HDDから、データを救出する方法は、例えば、下のようなやり方があると思います。僕の場合は、1.の方法では外付けHDDとして認識されなかったので、方法2.でデータを取り出しました。
  1. MACの中のHDDを外付けのHDDとして、動かす(具体的には、他のMACとThunderboltケーブルでつないで、ターゲットディスクモードで他のMACからHDDのデータを取り出す)。詳しくは「ターゲットディスクモード Thunderbolt」などでググってください。
  2. リカバリーモードで使えるターミナルで、MACの中のHDDのデータをUSBメモリなどに移す。http://techracho.bpsinc.jp/morimorihoge/2014_07_09/18182を参照。上のリンクでは、ネットワーク経由でデータを吸いだしていますが、USBメモリや外付けHDDなどを使うほうが楽だと思います。また、最初、ターミナルからdfコマンドで、Macintosh HDが見えなくても、暗号化されている場合、ディスクユーティリティから、パスワードを入れることで、マウントできます(ディスクユーティリティからHDDが見れない場合はこの方法は無理だと思います)。この方法では、ターミナルからコマンドを使って、データを移動させないとダメです。シェルコマンドに慣れてない人はググる
  3. MacBookからHDDを物理的に取り出して、SATA-USBケーブルなど(内蔵のHDDを外付けにできるケースを買うのが楽かもしれません)を使って、他のMACに差して、データを取り出す。
続きを読む

2つのarrayが近似的に同じか比べる

2つのNdarrayが近い値かどうかを比べるには、numpy.allcloseを使う。

  • numpy.allclose(a, b, rtol=1e-05, atol=1e-08)

2つのarray a,bを受け取って(a,bは同じshapeのarrayでないといけない)、すべての成分が
absolute(a - b) ≦ (atol + rtol * absolute(b))
を満たせばTrueを、満たさない成分があればFalseを返す。

成分ごとに、比較したい場合はnumpy.iscloseを使う。
また、近い値かどうかではなく、exactに一致しているかどうかを調べたい時には、numpy.array_equalもしくはnumpy.array_equivを使う(numpy.array_equalはshapeが一致している時のみTrueで、numpy.array_equivはbroadcastedして一致するならTrue)。

Ref:
http://ibisforest.org/index.php?python%2Fnumpy#q0f5fdb7
http://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html
http://docs.scipy.org/doc/numpy/reference/generated/numpy.isclose.html
http://docs.scipy.org/doc/numpy/reference/generated/numpy.array_equal.html
http://docs.scipy.org/doc/numpy/reference/generated/numpy.array_equiv.html

例)

import numpy as np
A = np.array([1.0, 1.0])
B = np.array([1.0000001, 1.0])
print np.allclose(A,B)
print np.isclose(A,B)
print np.array_equal(A,B)

True
[ True True]
False

複素数型が実数に近かったら、実数型にする

complex型のndarrayが与えられた時に、もし複素数の部分が小さいなら、floatなどの実数の型で近似する方法。
そういう関数、ありそうだと思ってググったらNumpyの関数にnumpy.real_if_closeというのがあった。

numpy.real_if_close(a, tol=100)

この関数はNdarray aの虚部a_imagを見て、それがa_imag≦マシンイプシロン*tol(Tolerance)なら、虚部を無視した実部だけのNdarrayを返す。
a = a_real + i*a_imag → a = a_real

マシンイプシロン
np.finfo(np.float).eps
で調べることができ、Pythonでは普通、2.22044604925e-16になっているみたいです。
tolはデフォルトでは100に設定されているので、a_imag≦2.22044604925e-14であれば虚部を無視することになります(tolはオプションで変更できます)。


このように、np.real_if_closeは虚部の絶対値をみて、小さいかどうかを判断する仕様になっている。
しかし、普通aの絶対値が大きくなると、誤差の絶対値も大きくなるので、虚部/絶対値が小さいかどうかを見るほうが、良い場合が多いと思う。:

print np.finfo(np.float).eps
a_real = np.array([1.0e4])
a_imag =  np.array([3e-12j])
a = a_real + a_imag
print a.dtype
a = np.real_if_close(a, tol=100*np.absolute(a))
print a.dtype

2.22044604925e-16
complex128
float64

Numpyは欲しい関数がたいてい用意されているのですごい!
http://docs.scipy.org/doc/numpy/reference/generated/numpy.real_if_close.html
http://docs.scipy.org/doc/numpy/reference/generated/numpy.absolute.html

2015.6.26 追記
オプションtolで1より小さい値を設定すると、real_if_closeは配列を常に実数型にしてしまうようです(本来は複素数型のままにして欲しい)。
そもそも、tolが1より小さいとa_imagがゼロでない値を持っていたら、最小値はマシンイプシロンなので、常に複素数のままになるべきですが、たぶんそういうtolを想定して関数が作られておらず、バグってしまうのだと思います。
上のtol=100*np.absolute(a)という設定はaの絶対値が大きい場合を想定していますが、逆に絶対値が小さい場合は使うときに注意してください。

Pythonの代入の挙動

Pythonで代入をした時に、各変数がどのオブジェクトを指すかをまとめておく。特に、list(リスト)とNdarray(配列)の違いをば。
前回の記事も参照 : http://oppython.hatenablog.com/entry/2015/01/15/014729

例1

前回の記事でも書いたようにPythonでは、代入(=)は変数に値(=オブジェクト)を格納しているのではなく(値渡し)、値(=オブジェクト)への参照を格納している(参照渡し)。

x = np.array([1])
y = x
print 'id(x)_before =', id(x)
print 'id(y)_berore =',  id(y)
x = np.array([2])
print 'id(x)_after =', id(x)
print 'id(y)_after =',  id(y)
print y

id(x)_before = 4300100832
id(y)_berore = 4300100832
id(x)_after = 4300101152
id(y)_after = 4300100832
[1]

y = xとすると、変数yもxと同じオブジェクトnp.array([1])を指すようになる(参照渡し)。
変数xに新しいオブジェクトnp.array([2])を代入すると、xは新しいオブジェクトを参照するようになる。
もちろん、yには何もしていないので、始めのオブジェクトを参照したままである。
上の挙動は、ndarrayだけでなく、タプル、文字列、リスト、数値などでも共通(型が変更可能かどうかに依らない)。

続きを読む

Pythonの型の分類と代入

変数が指すオブジェクトがどれかなどの振る舞いをすぐ忘れてしまうのでメモしておく。

組み込み型の分類

まず、Pythonの組み込み型についてまとめておく。

管理方式

管理方式の違いで分類すると、以下のようになる。

  • コレクション型(=コンテナ型):複数のデータをまとめたもの

+ シーケンス型:要素を順番に並べて管理。整数のindexでデータを管理できる。
例)文字列、リスト、タプル
+ マップ型:キーと値のペアで要素を管理。
例)ディクショナリ

  • 非コレクション

例)数値型(int, float, long, complex)、ブール型、None

文字型がシーケンス型なのは面白い。シーケンスなので、たとえば、

a = "oppai"
print a[4] #i

のようにスライスを使って、要素の指定とかができる。

変更可能性

変更可能性で分類すると、以下のようになる。

  • 変更可能 (mutable)

例)リスト、ディクショナリ

  • 変更不能 (immutable)

例)数値型、文字列、タプル

基本的に、代入なりなんなりをした時の挙動は、変更可能かどうかで決まる。
オブジェクトはメモリに保存されているが、変更可能ならば、そのメモリに書かれているオブジェクトは書き換え可能になる。

続きを読む

numpy.matrixとnumpy.ndarrayの違い

numpy.matrixと2次元のnumpy.ndarrayは同じことができるが、numpy.matrixでできることはすべてnumpy.ndarrayでできる。
numpy.matrixがある理由はたぶん、行列を扱う時にはnumpy.ndarrayより見やすいので。
行列以外の高次のテンソルを扱う時には、混乱を避けるため、numpy.ndarrayだけ使うようにしたほうがよいと思われ。

個人的には、行列以外のテンソルを扱うことも多いし、numpy.matrixを使う理由はなさそう。

Ref:
http://wiki.scipy.org/NumPy_for_Matlab_Users
http://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/linalg.html

固有値問題

Python固有値問題を解く方法についてメモしておく。
メジャーな方法として、以下の3つがある

  1. numpy.linalgの関数を使う。
  2. scipy.linalgの関数を使う。
  3. scipy.sparse.linalgの関数を使う。


numpy.linalgとscipy.linalgには以下の4つの関数がある。

関数名のhはHermitianの略。Scipyだと一般化固有値問題*1もオプションで出来る。
ちなみに、scipy.linalgはnumpy.linalgに含まれる関数はすべて含んでいて、さらに追加で他の関数も含んでいる。また、scipy.linalgが常にBLAS/LAPACKコンパイルされるのに対し、numpy.linalgはそうとは限らないので、scipyの方が早い場合もある。なので、特別な理由(コードが既にnumpy.linalgで書かれていて、書きなおすのがめんどいとか)がない限り、scipy.linalgを使うほうがよい。たぶん。
http://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/linalg.html


scipy.sparse.linalgには以下の2つの関数がある。

関数名のsはsparseの略。一般化固有値問題も出来る。

*1:普通の固有値問題がAx=axに対し、一般化固有値問題はAx=aMxのような固有値問題のこと。

続きを読む