PythonでMockを使ったユニットテスト

PythonでMockを使ったユニットテストを書いてみる。

Python3.3以降は標準でMockが使えるようだけど、Python2.7系では標準では入ってないのでインストールする。

$ pip install mock

サンプルコードは以下に。

# -*- coding:utf-8 -*-

import unittest
from mock import Mock, patch


# DBにつなぐのでテストのときは使いたくないクラス
class HogehogeDao(object):
    def __init__(self):
        pass

    def find(self, param_1, param_2):
        print('DBにつなぐよ')
        print('find ' + param_1 + param_2)


# テストの対象となるクラス
class CalculationModel(object):
    def __init__(self, dao):
        self.__dao = dao

    def execute(self):
        return self.__dao.find('hoge', param_2='foo')

# 使い方
calc = CalculationModel(HogehogeDao())
calc.execute()

# DBにつなぐよ
# find hogefoo
# と表示される。

# ------------------------------ ここからユニットテスト --------------------------#


class SideEffectTestResult(object):
    """side_effectの返り値用
    """
    pass


class CalculationModelTestCase(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def dataProvider(self, param_1, param_2):
        # 返り値を置き換える関数と同じ数の引数でないといけない。
        return SideEffectTestResult()

    def test_execute_1(self):
        """Mockインスタンスに置き換えてのテスト
        """
        mock_dao = Mock()  # Mockの作成
        # findメソッドとその返り値を作成
        mock_dao.find.return_value = 'testtest'
        # テスト対象のインスタンスを作成
        test_obj = CalculationModel(mock_dao)
        # 返り値が変わっていることを確認。
        self.assertEqual('testtest', test_obj.execute())
        # findメソッドが1回呼ばれていることを確認。
        self.assertEqual(mock_dao.find.call_count, 1)
        # メソッドが呼ばれたときの引数を取得できる
        # 0番から呼ばれた順に格納されている
        # callオブジェクトに格納されるのでlist化
        # 配列の0番目にタプルで引数、1番目に辞書でキーワード引数が格納される
        self.assertEqual(list(mock_dao.find.call_args_list[0]),
                         [('hoge',), {'param_2': 'foo'}])

    def test_execute_2(self):
        """メソッドをMockインスタンスに置き換えてのテスト
        """
        HogehogeDao.find = Mock(return_value='method_mock_test')
        dao = HogehogeDao()
        test_obj = CalculationModel(dao)
        self.assertEqual('method_mock_test', test_obj.execute())

    def test_execute_3(self):
        """with構文とpatch()を使って置き換えてのテスト
        """
        with patch('__main__.HogehogeDao') as mock_dao:
            mock_dao.find.return_value = 'with_mock_test'
            test_obj = CalculationModel(mock_dao)
            self.assertEqual('with_mock_test', test_obj.execute())

    def test_execute_4(self):
        """side_effectを使ってメソッドを返り値にする
        """
        mock_dao = Mock()
        mock_dao.find.side_effect = self.dataProvider
        # テスト対象のインスタンスを作成
        test_obj = CalculationModel(mock_dao)
        # 返り値が変わっていることを確認。
        self.assertIsInstance(test_obj.execute(), SideEffectTestResult)

unittest.main()

クラスをmock.Mock()で置き換えることができる。

クラスを置き換えるときは、メソッド名.return_valueで返り値を設定できる。 メソッド名.side_effectだと返り値を返す関数を設定することもできる。 そのときは元のメソッドと同じ数の引数をもたないといけない。

メソッドを置き換えるときも特に大きくは変わらない。

with構文で使うときはpatch()を使う。

そんなところで、わりと簡単にできるのでおすすめ。

関連エントリ

Python + PypeRでPythonからRをつかってみる

Python機械学習周りのパッケージは充実している感じがあるのですが、どうにも統計周りのパッケージが不足している感じがあって、PythonからRを叩くパッケージを試してみることにしました。

PythonからRをつかうパッケージとしてはRPy2が有名っぽいですが、

There is currently no binaries or support for Microsoft Windows (more for lack of ressources than anything else).

とのことで・・・

試しにeasy_installでインストールしてみましたがエラーが出てインストールできなかったので、違う方法を試すことにしました。

PythonからRを使いたい -だがRPy2おめーはダメだ- - 盆栽日記を見てPypeRのほうを試すことにしました。

インストールはいつもどおりeasy_installで簡単にできました。

> easy_install PypeR

RでいうDataFrameと同じようなことができるPandasについてもインストールします。

> easy_install pandas

依存するパッケージも自動的にインストールされます。

この前書いたPythonで回帰分析スクリプトを少し変えてPypeRを使ったコードにしてみました。

ソースコードは以下に置いてます。

Python + PypeRでPythonからRをつかってみる · GitHub

実行すると以下のような出力とグラフが表示されます。

try({summary(df)})
X                 Y 
Min. :157.8       Min. :43.07 
1st Qu.:167.6    1st Qu.:56.30 
Median :172.0    Median :62.99 
Mean :171.8      Mean :62.62 
3rd Qu.:175.3    3rd Qu.:68.42 
Max. :187.9      Max. :85.74

try({summary(result)})

Call:
lm(formula = Y ~ X, data = df)

Residuals:
Min 1Q Median 3Q Max 
-9.8270 -2.7967 0.1225 3.0575 11.7299

Coefficients:
              Estimate   Std. Error   t value   Pr(>|t|) 
(Intercept) -143.70770   9.27459     -15.49     <2e-16 ***
        X     1.20116   0.05396      22.26     <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4.382 on 198 degrees of freedom
Multiple R-squared: 0.7145, Adjusted R-squared: 0.713 
F-statistic: 495.5 on 1 and 198 DF, p-value: < 2.2e-16

統計については、Scikit-learnよりもやはりRのほうが出力が詳しくていいです。

PythonからRを使うなら全部Rでいいような気もしますが、単純なスクリプトならともかく、データの加工までをRでやろうとすると結構大変だったりするので、Pythonでその部分を書けるのはありがたいかなと。

関連エントリ

Pythonとscikit-learnで単回帰分析

前回の記事で整えたPython環境で単回帰分析をしてみます。

単回帰分析というのは単純に言えば各データ(x1, y1)・・・(xn, yn)からy=ax+bという式を推定するということです。 推定したyの値と実際のyの値の差の自乗値を最小とするような直線を引きます。

0. モジュールのインストール

今回はscikit-learnのモジュールで実行してみます。

scikit-learnはWindows用インストーラーが配布されていますので、それを使ってインストールします。

1. 実際に実行してみる

ソースコードは以下に置きました。

Pythonとsklearnで単回帰分析 · GitHub

実行すると以下のような出力と図が出ます。

Coefficients :[ 1.08085339]
Intercept :-122.796925076
R2 :0.762157708979

図の青点が実際のデータで、赤線が回帰して求めた直線になります。 ちょうど中心をずばっと通っているのがいいですね。

上の出力結果より、この直線(y=ax+b)の傾き(a)は1.08085339、切片(b)は-122.796925076です。

R2というのはこの直線の当てはまりの度合いを表す指標で0~1の値をとります。1に近いほど当てはまりが良いってことになります。 だいたい0.8くらいあるとかなり当てはまりがいいということなので、今回は0.76とそれなりに良い数値です。

以上で、単回帰分析までやったので次からは実際にデータを使ってやってみることをしていきます。

追伸

一昨日くらいにstatsmodelsモジュールをいろいろと試してみたのですが、linear_model.LinearRegression()で回帰して.predict()メソッドで取得した結果をplot()すると左図のようになってしまい、結局うまくいかず。

解決法知っている人がいれば教えていただけると助かります。

statsmodelsのほうがサマリの出力も以下のように充実していていいのですがねー。

OLS Regression Results                            
==========================================================
Dep. Variable:                      y   R-squared:                       0.991
Model:                            OLS   Adj. R-squared:                  0.991
Method:                 Least Squares   F-statistic:                 2.157e+04
Date:                Sat, 24 Aug 2013   Prob (F-statistic):          7.51e-205
Time:                        11:54:16   Log-Likelihood:                -644.07
No. Observations:                 200   AIC:                             1290.
Df Residuals:                     199   BIC:                             1293.
Df Model:                           1                                         
==========================================================
                 coef    std err          t      P>|t|      [95.0% Conf. Int.]
------------------------------------------------------------------------------
x1             0.3669      0.002    146.866      0.000         0.362     0.372
==========================================================
Omnibus:                        4.268   Durbin-Watson:                   0.124
Prob(Omnibus):                  0.118   Jarque-Bera (JB):                5.503
Skew:                          -0.036   Prob(JB):                       0.0638
Kurtosis:                       3.809   Cond. No.                         1.00
==========================================================

関連エントリ

ニコニコデータセットで「www」と「GJ」コメント数の散布図を描く

ニコニコデータセットで、少し分析してみました。

今回は「w」と「GJ」に相関関係はあるかなーというもの。 感覚的には、動画中で笑うタイミング、つまり「w」ってコメントするタイミングがある動画ほど、最後に「GJ」ってコメント打つことが多いんじゃないかなーと思って、今回の分析です。

対象データはこの前のLTの時と同じく「ニコニコ技術部」「ニコニコ手芸部」「作ってみた」のタグのついた動画に限定しています。 全カテゴリではないです。

それぞれの動画について「w」のついているコメントと「GJ」のついているコメントを数えて、その2つの関係を見ています。 両方のコメントがついていない動画は今回の対象に含まれていません。

MySQLからの集計をPythonでやって、プロットと回帰をRでやりました。 ソースコードは、https://gist.github.com/mia-0032/6310570に置いています。

特定の動画のコメント数が他と比べて非常に多いのですが、今回は自然対数をとることで、そういう値の影響をそいでいます。

今回の出力結果。

Call:lm(formula = gj ~ www, data = data)
Residuals:    Min      1Q  Median      3Q     Max
            -2.1416 -0.4148 -0.1045  0.1226  9.4828 
Coefficients:
                Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.272213   0.007219  -37.71   <2e-16 ***
    www        0.247792   0.002210  112.10   <2e-16 ***
---Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6825 on 34384 degrees of freedomMultiple 
R-squared:  0.2677,  
Adjusted R-squared:  0.2676 
F-statistic: 1.257e+04 on 1 and 34384 DF,  p-value: < 2.2e-16

プロットの縦軸はGJコメント数の対数値、横軸はwコメント数の対数値です。

点のプロットを見る限りは、ある程度の相関はありそうです。 回帰直線を引いてみましたが、ちょっと0付近の値に引っ張られてしまっている感じで、あんまり精度良くないですね。

しかも、どちらかというと二次曲線的な回帰曲線を引くべきですね・・・

しかし、こういう分析は因果関係も複雑で、正直、難しい。 もっと問題自体を考えなおさないと・・・

とりあえず今日はここまで。

Pythonで統計の環境構築から集計とグラフ描画まで

これまでRとかExcelとかで回帰分析やってたけど、Pythonでもできるように環境整えるところから始めます。 メモって置かないと次、構築するときに忘れるのでメモです。

OSはWindows7(64bit)で試しましたが8でも同じはず。 Pythonは2.7.5(32bit)。

0. Pythonのインストール

すでに終わってるとは思いますが念のため。 http://www.python.org/ からPython2.7.x系のインストラーをダウンロードします。 対応しているモジュールの関係でまだ32bit版のほうがいいです。 3.3.x系でも特に問題はないと思いますが、多少文法変わってたり、モジュールが使えなかったりするので、2.7.x系のほうが無難だと思います。

インストールが終わったら、環境変数に「python.exe」のあるディレクトリ(特に変更していない場合はC:\Python27)を追加するのを忘れずに。 ちゃんと追加されていればコマンドプロンプトで以下のように入力して実行するとインストールしたPythonのバージョンが表示されるはずです。

> python --version

1. NumPy, SciPy, Matplotlibのインストール

NumPyはN次元の配列を扱いやすくするパッケージ。

SciPyは科学計算関係の統合パッケージ。

Matplotlibは様々なグラフを描くためのパッケージ。

http://www.numpy.org/

http://www.scipy.org/

http://matplotlib.org/

の各ページからWindows用のインストーラーをダウンロードして実行。 Pythonのバージョンと32bit, 64bitを間違えないように注意。

2. easy_install, pipのインストール

easy_install

easy_installはPHPでいうPEARみたいなもので、各種パッケージのインストールをその名の通り簡単にしてくれるツール。 pipはそれを置き換えるものとして開発されています。

https://pypi.python.org/pypi/setuptools 上記サイトからWindows用のインストールに進み、「ez_install.py」をダウンロードする。

コマンドプロンプトから

> python ez_install.py

と実行すると、Pythonのインストールディレクトリ/Scripts/に「easy_install.exe」ができているはず。

環境変数にScriptsディレクトリを追加。標準だとC:\Python27\Scripts;と追加すればOKなはず。

その後コマンドプロンプトを再起動して、

> easy_install --help

Global options:~~~って感じでずらずらとオプションの説明が出てくれば完了。

pip

easy_installからインストールする。 コマンドプロンプトから以下のコマンドを実行。

> easy_install pip

以下のコマンドを実行してずらずらっと表示が出ればOK。

> pip -h

3. その他のパッケージのインストール

Matplotlibに必要なパッケージをeasy_installでインストールします。 pyparsing コマンドプロンプトから

> pip install pyparsing

でインストールできます。

dateutil

> easy_install python-dateutil==1.5

でインストールできます。 バージョン指定しないと3.0以降でしか使えないバージョン2がインストールされてしまうので注意。

4. 実際に適当に集計してみる

適当な乱数を出力して、それの基本的な統計量をとってみます。 ソースコードは以下に置きました。

Pythonで基本的な統計量を出力してみる · GitHub

実行すると以下のように出力されるはずです。(乱数でデータを生成しているので数値は変動します)


Max x: 189.75 y: 84.73
Min x: 153.17 y: 41.2
Avg x: 172.1967 y: 62.3155
1Q x:167.815 y: 57.0675
Med x: 172.425 y: 61.68
3Q x:176.495 y: 67.35
Var x: 39.31919611 y: 63.87700275
S.D. x: 6.27050206204 y:7.99230897488
Correlation Coefficient : 0.870409290776

各項目の説明。

最大値、最小値

配列の中で最も大きな数値と小さな数値です。

平均値

算術平均をとった値です。

中央値

全体の配列をソートしたときに、ちょうど真ん中に位置する値です。 平均値と違い、極端に大きい値や小さい値(外れ値)が入っていても影響を受けないという点があります。

第1四分位、第3四分位

全体の配列をソートしたときに、それぞれ25%と75%に位置する値です。 この値と中央値をみると、データがどのような分布になっているのかが少しわかります。 図にしたほうがわかりやすいですけどね。

分散、標準偏差

分散はデータのばらつき具合を表す数値です。 標準偏差は、分散の平方根をとった値で、同じくデータのばらつき具合を表します。

相関係数

2つのデータが、どの程度比例を表す数値です。 1に近いほど同じ傾向があることになり、0だと全く関係がないことになります。 -1だと反対の傾向があるということになります。

5. 実際にグラフを描いてみる

ソースコードは以下に置きました。

Pythonでヒストグラムと箱ひげ図を描く · GitHub

実行すると以下のように図が出ます。

左側の棒グラフはヒストグラムで、下軸の各区間に入るデータが何個あるかを表しています。 右図は箱ひげ図というもので、中央の赤い線が中央値、箱の上下が第3四分位、第1四分位を表します。

箱から生えている棒は、箱長さの最大1.5倍(引数で変更可)まで伸び、データの存在範囲を示します。

もしそれを超えるデータがある場合は点でプロットされます。

以上でグラフ描画まで終わりです。

この後、Pythonで回帰分析してみようと思って、statsmodelsとかを調べていたのですが、predict()とかがうまく動かなくて断念。

Pythonの統計パッケージ、種類いっぱいあって、逆に資料が少なくてよくわかないです・・・。

このへんの分析パッケージの使い方はRのほうがいろいろ資料まとまってていいなぁと思いましたが、グラフはPythonのほうが描きやすいと思ったのでどうしたものかなぁという次第です。

関連エントリ

CoffeeScriptとProcessing.jsで「あの楽器」(その2)

今日も更新。

変更点は以下の点です。

ProcessingのクリックイベントハンドラーからjQueryのクリックイベントハンドラーに変更したのは、今後マルチタッチ対応するときにJSのライブラリとか使いやすくなるだろうと思いましたので。

ただcanvas要素に.click(fn)でイベントを付与しようとしましたが、それだとうまくいかなくて、.bind('click', fn)だとうまくいきました。

なぜなのかはちょっとわからないですね・・・。

他に気づいたこととしては、ProcessingのmouseX, mouseYevent.offsetX, event.offsetYは同じ値になるということ。

Processingのコンストラクタには要素がひとつしかない場合でも$('canvas')[0]という形で[0]をちゃんと指定しないとダメということ。

ソースコード自体はGithubに公開しています。

mia-0032/anogakki_js · GitHub

サンプルは以下に設置しています。

http://mia-0032.github.io/anogakki_js/

CoffeeScriptとProcessing.jsで「あの楽器」作り始めた

タイトルの通り、CoffeeScriptとProcessing.jsで「あの楽器」のプログラムを書きなおし始めました。

理由としてはマルチタッチに対応するのがJavaScriptなら楽そうというのと、マルチプラットフォームに対応しやすいということ。

あと、単にCoffeeScript書きたかったのです。

CoffeeScriptソースコードがすっきりしていて個人的に好きな言語です。

まだまだ未完成でやっとマウスクリックで丸のエフェクトが出るところまで、しかも歪な感じですが、ソースコード自体はGithubに公開しています。

mia-0032/anogakki_js · GitHub

サンプルは以下に設置しています。

http://mia-0032.github.io/anogakki_js/

基本的にはChromeのみ対応です。

他のブラウザで試してみたら、Firefoxはカクカクでダメっぽいです。

IE10は意外と健闘してて、よほど連打してエフェクト出さない限りは割りとスムーズに動きます。

IE見なおしました。

これから随時更新していこうと思っているので気長に待っていただければなと。。。

「あの楽器」の製作動画を投稿

ついに「あの楽器」の製作動画を作って投稿。

超会議のときから数えるとちょうど1年くらいで、1年間もずっと同じ作品を作ってたことは今までになかったので、すごく感慨深い。

もちろん1年間ずっと作ってわけではなく他の作品も作ったりはしてたんだけど、試作を繰り返してこういう形にできた感じ。

まだまだ改良すべき点はあるので、まずはソフトウェアのほうを中心に改良していきたいところ。

新しい言語勉強するかー。

ConsistentHashing法の実装

PHPでConsistentHashingの実装をしてみたので公開。

 
ConsistentHashingのわかりやすい説明とかメリットは以下のページが詳しかった。
 
 
 
PHPで実装するにあたり、ActionScriptで実装した方がいらっしゃったので、非常に参考にさせてもらった。
 
 
 
コードは以下。


検証用のプログラムを作成してちゃんと分散できているか確認してみた。
https://gist.github.com/4460171


ノード1~4に分散させたとき、各ノードに割り当てられる個数
node1:2670
node2:2227
node3:2259
node4:2844

ノード1~4に5を加えたときに元のノードに割り当てられる割合
node diff:1:80.15%

単なる剰余のときの上記の割合
(remainder)node diff:1:20%

ということで、ConsistentHashingのほうが圧倒的に高いヒット率を維持できるっぽいです。

「あの楽器」プログラム少し修正

鍵盤の描画に使っている関数をvertex()からrect()に変更しました。

主な変更理由は、コードの読みやすさのため。

うえちょこ@ぼろぐ» [processing]2.0a5から大きく変わること(OpenGL化とか)

このあたりを読むと、どうもPShapeを使うようにしたほうが高速っぽい?

そのうち試してみよう。

あと、さっき【初音ミク】Innocence【3DPV】 ‐ ニコニコ動画:GINZAを見直したら今作ってるプログラムと結構エフェクト違うなーと。

できるだけ忠実に再現したいので今度以下のような修正を入れようと思う。

  • エフェクトは線のものが中心
  • 円とかのエフェクトはもっと大きくなるスピードが速い
  • 円とかのエフェクトはもっと全体に広がるように大きくなる
  • ピアノ鍵盤を今使っているけど、オリジナルに近づけるならそういう描画ではないほうがいいんかなー。

まあぼちぼち更新していきます。

Arduino用の赤外線シリアル通信クラス

ちょっと赤外線でシリアル通信したいなーと思うことがあったのでArduino用に作ったので、いつも通りgistに公開しました。



ArduinoのDigitalPin12に赤外線LEDをつけて送信するようになっています。

PORTBを直接いじってる関係でPORTB上にほかのデバイスをつけることができなくなります。

PWM出力の周波数をいじれば、もっとうまいことできるような・・・?
ちょっとそのあたりの知識がまだ不足気味です。

受信するときは赤外線受信モジュールのVoutをSerialReadすればOK(だと思います)。
自分が試したときはSerial-USB変換モジュールのRXに直結してPCから信号を見ていまた。

しかしこのプログラム、delay関数で直接、1周期の時間を指定しているせいで、ほかの処理が遅くなると発信周波数がずれて、正しい信号を送れなくなるのです・・・

16MHzのArduinoなら特に問題にはならなかったけど、8MHzのArduinoだとうまく動かない。

うーん、どうしたものか。

 関連エントリ

あの楽器製作の進捗

あの楽器を作りはじめてから、そろそろ4ヶ月くらい経つので、いったんここらでまとめておこうかと思う。

一応、ニコニコ超会議でひっそりと出展できるレベルまで作った。



 

この時の筐体は時間がなかったのでスチレンボードで作った簡易なもの。
 
液晶パネルとタッチパネルはaitendoで購入した15.4インチワイドのもの。




このままちゃんとした筐体を作って完成にしようと思っていたんだけど、この前調整中にタッチパネルを割ってしまった・・・

ということで、今は液晶パネルを外して、フレームだけに戻っている。

液晶パネルとタッチパネルを新たに購入して再製作しようかと思ってるけど、どうせ再製作するなら前の欠点だった重量が重たい点を改良しようかと、部品を選定しなおしている。


プログラムのほうは超会議のころより少し動作を改善して、キーを連続して押すと処理が詰まったり、鍵盤と音がずれている問題を修正した。

まだ粗削りで、もう少し改良したいけど、githubに公開している。

https://github.com/mia-0032/ano_gakki

本体を再製作したらプログラムのほうも更新していこうかなと。

アクリルスピーカーの新作

以前から作ってるアクリルスピーカーの新作ができたので動画を投稿しました。

今回はlakugakiさんがイラストを描いてくれました。

なので、いつもより丁寧に作っています。

動画には撮ってないんですが、イラストは一度彫りなおしています。

左が彫り直す前、右が後。

ちょっと左は眼のあたりを失敗した感があり。

今回のアクリルスピーカーはいろいろ取り外して交換できる構造になってて、板の枚数が多め。

このスピーカーを作るときに時間かかったところは、アクリル板の研磨作業。

耐水ペーパーで地道に磨いて研磨剤で仕上げるのをひたすら繰り返し。

動画には入れてないけど、結構、時間かかってたりします。

Arduinoに書き込んでいるコードは以下のものです。

次はあの楽器についてまとめていかないと。

新年はじめの工作(ネギ振り賽銭箱)

新年最初の工作として、技術部に動画を投稿していながら、今まで作っていなかったネギ振りはちゅねミクを作りました。

本編が、賽銭箱にお金を入れるとネギを振るはちゅねミク、おまけ編が音楽の低音に合わせてネギを振るはちゅねミクです。

おまけの方が実際には時間かかってます。




Arduinoに書き込んだコード。

アクリルスピーカーを作ってみた

1週間前くらいに投稿したけど、こっちでは書いてなかったので・・・ MTM07で出展したアクリルスピーカーのうち、1代目のほうの動画を作ってアップロードしました。

Arduinoに書き込んだソースコードは以下です。

©2023 みや All rights reserved.