GIG

赴くままに技術を。

IPythonで最初からimportさせておく設定

IPythonを使っていると最初からimportしていてほしいなと思うときがしばしば。 前回行ったIPythonプロファイルで設定できるようなので、以下やってみる。

~/.ipython/profile_default/配下にあるプロファイルを編集しても良いのですが、 後で見直したいときもあるかなと思い、まずはIPythonプロファイルを作成するところから。

$ ipython profile create basic_settings

すると~/.ipython/profile_basic_settings配下にプロファイルが作られます。 この中のipython_config.pyにimportさせたいライブラリを加筆します。

# lines of code to run at IPython startup.
c.InteractiveShellApp.exec_lines = [
        "import numpy as np",
        "import pandas as pd",
        "import matplotlib.pyplot as plt",
        "%matplotlib inline"
]

このプロファイルを使ってIPythonを起動します。

ipython notebook --profile=basic_settings

これで明示的にimportを書くことなくライブラリを利用できます。

「Pythonによるデータ分析入門」を読む準備をする

今までJavaPerlをその場しのぎとして業務をしてきましたが、昨今の流れもあってPythonを使い始めました。

Amazonやアキバのヨドバシを眺めてみましたが、Pythonってあまり書籍がでていない...。その一方、RubyなんかはWebシステムの構築で使われる頻度が増えて、本があふれんばかり。

Rubyもいつかは身に付けたいけど、データの前処理用途で使おうと思うと、多次元配列に対応しているかや、科学計算ライブラリとかも気になる点です。 あるみにはあるにはあるみたいですが(いつか試してみよう...)。

SciRuby

Pythonによるデータ分析入門」から始める

そんなわけで、出だしに手をつけたのが「Pythonによるデータ分析入門」です。 著者のWes McKinney氏は、クオンツとしての経験をもとにデータ処理ライブラリのpandasを作成されたことでも知られています。

内容としては、NumPy, pandas, matplotlib, IPython, Scipyといったデータ分析で利用されるライブラリをハンズオン形式で解説しています。ところどころ事例紹介があり、後々参考になりそうです。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

読むための環境を整備する

IT関連の書籍にありがちですが、手を動かさないと頭に入ってこないので、環境を構築します。 備忘録代わりに日記にしたためておくとしよう。

手元のPCのスペック;

Macにがっつり構築しようとするとバージョンアップ時やに困りそうなので、 仮想環境として構築します。

VMの準備

手始めに、Oracle VM VirtualBox, Vagrantをインストールします。

1. Boxのダウンロード

利用したい仮想マシンをダウンロードしてきます。 社内の環境でも使っていることから、Redhat系のディストリビューションということで、CentOSを入れます。 ( centos70仮想マシンの識別名なので、適宜変更してください。)

$ vagrant box add centos70 https://dl.dropboxusercontent.com/s/srw2tqh58507wik/CentOS7.box

Boxはいくつか提供しているところがあるみたいですね(前者しか知りませんでした...)。

2. 仮想マシンの作成

仮想マシンを作成すると設定ファイル(Vagrantfile)が生成されるため、適切なディレクトリを作成してそこで下記のコマンドを実行します。

$ vagrant cenos70

ログがつらつらと吐き出され、仮想マシンが立ち上がりました。 下記コマンドで入れます。

$ vagrant ssh

ログインユーザはvagrantとなっています。 またrootユーザの初期パスワードは、vagrantです。

また今回は自宅なので、特に必要はないのですが、会社などでproxyの設定が必要な方は、 後々のパッケージのインストールに先立ち、vagrant-proxyconfを入れておきましょう。

$ vagrant plugin install vagrant-proxyconf

Vagrantfileに下記の行を追加します。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

...(以下の行を追記)
     config.proxy.http = "http://(ユーザID):(パスワード)@(Proxyのドメイン名):(Proxyのポート)"
     config.proxy.https = "http://(ユーザID):(パスワード)@(Proxyのドメイン名):(Proxyのポート)"
     config.proxy.no_proxy = "localhost,127.0.0.1"
...(以上の行を追記)

end

仮想マシンを再起動します。

$ vagrant reload 
CentOSのセットアップ

1. 基本セットアップ

開発環境を一式入れておきましょう。

$ sudo yum groupinstall "Development tools"

さらに、pyenvの利用に際して以下を追加します(参考: Common build problems · yyuu/pyenv Wiki · GitHub)

$ sudo yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel

あと、matplotlibのインストールする際に必要なものとして、

$ sudo yum install python-devel libpng-devel freetype-devel

必要に応じてproxyの設定をします。

# sudo vi /etc/profile

...(以下の行を追記)
PROXY=http://(ユーザID):(パスワード)@(Proxyのドメイン名):(Proxyのポート)
export http_proxy=$PROXY
export https_proxy=$PROXY
...(以上の行を追記)

あと、F/Wも切っておきたいところです(甘えですが)。

# systemctl stop firewalld
# systemctl disable firewalld

2. Pythonのインストール

2.X系、3.X系とありますが、何をしたいかで変更したい場合もあるかと思いますので、インストールバージョンを管理できるpyenvを利用します(以下では、XXXenv系をまとめて管理するanyenvを利用)。

$ git clone https://github.com/riywo/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
$ exec $SHELL -l

pyenvをインストールします。

$ anyenv install pyenv
$ exec $SHELL -l
$ anyenv versions      # インストールされたか確認

それではpythonをインストールしましょう。 まずはどれをインストールするかリストから選びます。

$ pyenv install --list
Available versions:
  2.1.3
  2.2.3
  2.3.7
  2.4
  2.4.1
  2.4.2
  2.4.3
...(以下、省略)

今回は、3.4.2をインストールします。 また、今後バージョンを使い分けることを想定して、~/dev/python配下のみ 3.4.2を使うことにします。

$ pyenv install 3.4.2
$ mkdir -p ~/dev/python
$ cd ~/dev/python
$ python --version
Python 2.7.8
$ pyenv local 3.4.2
$ python --version
Python 3.4.2
ライブラリのインストール

pythonのライブラリを管理するpipを使ってインストールしていきます。 順番は、

  1. Numpy
  2. Scipy <- Numpyが先に必要
  3. Pandas
  4. matplotlib
  5. IPython

1. Numpyのインストール

$ pip install numpy

2. Scipyのインストール

  • 科学計算ライブラリ
  • Numpyライブラリに依存
  • 必要なBLASLAPACKといったライブラリを有償のものを使うなどカスタムインストールすることも

インストール結構時間かかるな...

$ pip install scipy

3. Pandasのインストール

  • RのあのDataFrameが利用可能
  • group byやmergeなどデータの取り回し機能ライブラリ
$ pip install pandas

4. matplotlibのインストール

  • 2Dのチャート描画ライブラリ
$ pip install matplotlib

5. IPythonのインストール

$ pip install “ipython[notebook]"

外部からブラウザでアクセスできるようにします。 デフォルトはいじらず、プロファイルmyprofを作り、その設定に修正を加えます。 ポートの変更もこのプロファイルで変更できます。

$ ipython profile create myprof
$ vi ~/.ipython/profile_myprof/ipython_config.py

...(以下の行を追記)
c.NotebookApp.ip = u'*'
...(以上の行を追記)
ようやくスタートライン

これでようやく読み始められます。

  • --no-browserオプションは起動時にブラウザを自動的に起動しないため
  • --matplotlib inlineオプションはmatplotlibをnotebook上で描画するため
 ipython3 notebook --profile=myprof --no-browser --matplotlib inline &

あと使用されているデータは、pydata/pydata-book · GitHubからcloneしてきましょう。 ipythonを起動(上記のコマンドを実行した)ディレクトリに降りて、下記のコマンドを入力します。

$ git clone https://github.com/pydata/pydata-book

本の中では~/ch0X/となっている点を~/pydata-book/ch0X/と読み替えればいけるかと。 最後にhttp://localhost:8888から始めるとしましょう。

iPhone 4sからiPhone 6に変えてみた

iPhone 5が出てからもがんとしてiPhone 4s(これだけで3代目)を手放しませんでした。 個人的な信条からなのですが、画面の大きさにこだわるAppleなんて見たくなかったし、OSが更新されていくので、機能面で購入する動機がないなーとぼんやり思ってました。

替えやがった

そんなある日、2015/1月中に下取りしたらiPhone 6(16GB)の機種代がカバーされると知って、うっかり帰り道アキバヨドバシへっ (信条なんてホコリなみの軽さでふっとんだ)

4.7inchというほぼ本体を握っているような薄さ。 取り付けが若干苦労しましたが、満足してます。 外し方はそのときにでも考えるか(汗

  • フィルム: (商品名どわすれ) ガラスフィルム

ケースにも付属のフィルムが付いてますが、前々からガラスフィルムを使って見たかったので、購入してみました(赤いハイヒールで画面を踏んでいるパッケージのやつ)。

ところが... iPhone 6ようにもかかわらず、画面の方がややはみ出している--; 画面のさわり心地は良いのですが、いまいち。

結局よかったのか?

やはり通勤中の片手使いには決して使いやすいものではないなと思います。 その分、電子書籍の見易さは、画面の大きさと綺麗さで段違いに良い! そういう意味だとタブレットの2台持ちはしなくなったなー

Angularジェネレータでプロジェクトの生成時にエラー

そろそろ腰を据えて、AngularJSを修得せんと。 angular-seedをcloneして使おうかと思ったけど、最近はYeomanということで、 早速AngularJSのプロジェクトを生成しようとしたら、下記のようなエラーが発生。

> node-gyp rebuild

gyp ERR! configure error
gyp ERR! stack Error: Python executable "python" is v3.3.5, which is not supported by gyp.
gyp ERR! stack You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.
gyp ERR! stack     at failPythonVersion (/Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:108:14)
gyp ERR! stack     at /Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:97:9
gyp ERR! stack     at ChildProcess.exithandler (child_process.js:645:7)
gyp ERR! stack     at ChildProcess.emit (events.js:98:17)
gyp ERR! stack     at maybeClose (child_process.js:755:16)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (child_process.js:822:5)
gyp ERR! System Darwin 14.0.0
gyp ERR! command "node" "/Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/hoge/dev/angular/minimumlist/node_modules/karma/node_modules/chokidar/node_modules/fsevents
gyp ERR! node -v v0.10.29
gyp ERR! node-gyp -v v1.0

node-gypTooTallNate/node-gyp · GitHubPythonの3系に対応してないみたい。 こういうときにXXXenvは便利。プロジェクトを作りたいディレクトリにおりて、2系に切り替えればOK。

$ pyenv local 2.7.6
$ python --version
Python 2.7.6

さて、チュートリアルでもやるか。

iframeを自動的にリサイズ

iframeで他のhtmlを埋め込みたくなったので、iframeのサイズを自動調整するような そういうjquery pluginがないか探していました。 Google先生に聞いてみたところ、下記が良くヒットします(日本語では)。

bowerを通してインストールしたいので、bowerにも聞いてみたところ、 下記のような結果が得られました。

$ bower search iframe
Search results:

    jquery.iframe-transport git://github.com/cmlenz/jquery-iframe-transport.git
    iframe-resizer git://github.com/davidjbradshaw/iframe-resizer.git
    jquery-bgiframe git://github.com/brandonaaron/bgiframe.git
    responsiveiframe git://github.com/npr/responsiveiframe
    iframe-auto-height-jquery-plugin git://github.com/Sly777/Iframe-Height-Jquery-Plugin
    versal-iframe-launcher git://github.com/versal/iframe-launcher.git
    jquery.iframetracker.buzzstarter git://github.com/ggenov/iframeTracker-jquery.git
    jquery-iframeshim git://github.com/alexcheng1982/jquery-iframeshim.git
    basic-seamless-iframe 

この中でサイズの自動調整は、davidjbradshaw/iframe-resizer · GitHubSly777/Iframe-Height-Jquery-Plugin · GitHubです。 後者は先のjquery-iframe-auto-heightと機能として同じみたいですね。 メンテナンスが今もされているという観点で、前者のiframe-resizerを利用してみます。

使い方

Bowerでインストールします(なければ、GitHubからcloneして任意の場所に置いておきます)。

$ bower install -S iframe-resizer

インストールされたJavaScriptは2種類あります。

  • iframeResizer.min.js ; iFrameを埋め込む方のHTMLで読ませる
  • iframeResizer.contentWindow.min.js ; iFrame側のHTMLで読ませる

あとはiframeを埋め込んだ側で、以下のように呼び出して上げます。

$(‘iframe’).iFrameResize({});

これらを使った例を見てみましょう。 まずはiframeでchild.htmlを取り込むparent.htmlの方です。

  • parent.html
<!DOCTYPE html>
<html>
<head>
     <meta charset="utf-8">
    <title>Iframe Resize Demo</title>
</head>
<body>

     <iframe src="./child.html" width="100%" scrolling="no" frameborder="0"></iframe>
    
    <!-- JavaScript -->
    <script src="../bower_components/jquery/dist/jquery.js"></script>
    <!--script src="../bower_components/iframe-resizer/src/iframeResizer.js"></script>
    <script>
         $('iframe').iFrameResize({ });
    </script-->
</body>
</html>

それでもって、iFrameで参照される側の方。

  • child.html
<!DOCTYPE html>
<html>
<head>
     <meta charset="utf-8">
    <title>Child</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
</head>
<body>

    <div class="jumbotron">
        <h1>Child</h1>
        <p>これはiframe側の要素。</p>
        <p><a class="btn btn-primary btn-lg" role="button">Learn more</a></p>
    </div>
    
    <!-- JavaScript -->
    <script src="../bower_components/jquery/dist/jquery.js"></script>
    <script src="../bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="../bower_components/iframe-resizer/src/iframeResizer.contentWindow.js"></script>
</body>

どうなるの?

  • Before

何も調整していない場合、下の方が切れてしまっていますね。

f:id:hermesian:20140810221923p:plain

  • After

無事下の方まで見えるようにheightが調整されていることが分かります。

f:id:hermesian:20140810221924p:plain

サイズが合わない?

“Troubleshooting > IFrame not sizing correctly”(https://github.com/davidjbradshaw/iframe-resizer#iframe-not-sizing-correctly)に記載されてます。heightCalculationMethodで、maxまたはlowerstElementを設定すると良いみたいです。

スピリチャル・ワールド

f:id:hermesian:20140629214646j:plain

東京写真美術館で5/13から7/13まで開催されている写真展。

文字面だけみると怪しげな、何か新興宗教的な匂いを嗅ぎ取ったけど、日本人の精神世界にフォーカスした写真展でした(日曜美術館のアートシーンで分かった)。

伊勢神宮、富士山のような有名所の写真もあったり、忘れられてしまったような土着の神様もいたり。

どこか不安をかき立てられる写真もあったりしたけど、それも含め落ち着ける空間でした。

あれが良かったな。神事の格好に扮した人のポートレイト。調べたみたら土田ヒロミ氏の「続・俗神」という作品のようで。

そういう土着のものの中に身を置きたい。 そうだから一刻も早く都会を去りたいなと。

DBからモデルにデータを読み込ませる(後編)

チュートリアルを参考に、DBから読み込んで計算

前回作ったDBからデータを引っ張ってきて、計算させてみる。 ほぼチュートリアル(Transport9.java)が参考になりそう。接続させるところだけ修正して、動かしてみた。

他に参考になりそうなところでは、いったんMySQLからcsvファイルを作り、それを読み込むような手法もあった(databases:mysql [GAMS Interfaces Wiki])。 ということで下のコードはTransport9.javaほぼそのまま。

public class TransportFromDB {

    /**
     * Modelを組み立て実行する
     */
    public void runJob() {
        
        GAMSWorkspace ws = new GAMSWorkspace();
        
        // ひな形となるモデルを読み込み
        GAMSJob tp = ws.addJobFromFile("/PATH/TO/YOUR/Model.gms");
        
        GAMSDatabase gdb;
        try {
            // GAMSDatabaseをDBから読み込み、組み立て
            gdb = readDataFromDB(ws);

            // Optionを設定
            // ソルバとしてxpressを設定している
            GAMSOptions opt = ws.addOptions();
            opt.defines("gdxincname", gdb.getName());
            opt.setAllModelTypes("xpress");

            // 計算の実行
            tp.run(opt, gdb);
        
            // 計算結果の出力
            for (GAMSVariableRecord rec : tp.OutDB().getVariable("x")) {
            System.out.println("x(" + rec.getKeys()[0] + ","
                    + rec.getKeys()[1] +
                    "): level=" + rec.getLevel() +
                    " marginal=" + rec.getMarginal());
            }
        } catch (SQLException ex) {
            Logger.getLogger(TransportFromDB.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    /**
     * MySQLからデータを読み込む
     * @param ws GAMSWorkspace
     * @return 返却するGAMSDatabase
     * @throws SQLException 例外
     */
    private GAMSDatabase readDataFromDB(GAMSWorkspace ws) throws SQLException {
        // プロパティファイル(mysql.properties)からパラメータを読み取り
        ResourceBundle rb = ResourceBundle.getBundle("mysql");
        GAMSDatabase gdb = ws.addDatabase();
        
        // Connectionを取得
        Connection conn = DriverManager.getConnection(
                    rb.getString("DB_URL"),
                    rb.getString("DB_USER"),
                    rb.getString("DB_PASSWORD")
        );
        
        // read GAMS sets
        readSet(conn, gdb, "SELECT Plant FROM Plant", "i", 1, "canning plants");
        readSet(conn, gdb, "SELECT Market FROM Market", "j", 1, "markets");
        
        // read GAMS parameters
        readParameter(conn, gdb, "SELECT Plant, Capacity FROM Plant", "a", 1, "capacity of plant i in cases");
        readParameter(conn, gdb, "SELECT Market, Demand FROM Market", "b", 1, "demand at market j in cases");
        readParameter(conn, gdb, "SELECT Plant,Market,Distance FROM Distance", "d", 2, "distance in thousands of miles");

        return gdb;
    }
    
    /**
     * Set項目をDBから取得してGAMSDatabaseに設定する
     * @param conn DBコネクション
     * @param gdb GAMSDatabase
     * @param query SQLクエリ
     * @param setName Set名
     * @param setDimension Setの取り得る次元
     * @param setDescription Setの説明
     * @throws SQLException 例外
     */
    private void readSet(Connection conn, GAMSDatabase gdb, String query, String setName, int setDimension, String setDescription) throws SQLException {
        
        Statement st = conn.createStatement();
        
        ResultSet rs = st.executeQuery(query);
        ResultSetMetaData meta = rs.getMetaData();
        
        if (meta.getColumnCount() != setDimension) {
            System.err.println("ERROR!");
            conn.close();
            System.exit(-1);
        }
        
        GAMSSet set = gdb.addSet(setName, setDimension, setDescription);
        String[] keys = new String[setDimension];
        
        while (rs.next()) {
            for (int i=0; i<setDimension; i++) {
                keys[i] = rs.getString(i+1);
            }
            set.addRecord(keys);
        }
        st.close();   
    }

    /**
     * Parameter項目をDBから取得してGAMSDabaseに設定する
     * @param conn DBコネクション
     * @param gdb GAMSDatabase
     * @param query SQLクエリ
     * @param paramName Parameter名
     * @param paramDim Parameterの取り得る次元
     * @param paramDesciption Parameterの説明
     * @throws SQLException 例外
     */
    private void readParameter(Connection conn, GAMSDatabase gdb, String query, String paramName, int paramDim, String paramDesciption) throws SQLException {
        
        Statement st = conn.createStatement();
        
        ResultSet rs = st.executeQuery(query);
        ResultSetMetaData meta = rs.getMetaData();

        int numberOfColumns = meta.getColumnCount();
        if (numberOfColumns != (paramDim + 1)) {
            System.err.println("ERROR!");
            conn.close();
            System.exit(-1);
        }
        
        GAMSParameter parameter = gdb.addParameter(paramName, paramDim, paramDesciption);
        String[] keys = new String[paramDim];
        
        while(rs.next()) {
            for(int i=0; i<paramDim; i++) {
                keys[i] = rs.getString(i+1);
            }
            parameter.addRecord(keys).setValue(Double.valueOf(rs.getString(numberOfColumns)));
        }
        st.close();
    }
}

コードの中で読んでいるmysql.propertiesは下記で、src/main/resourcesに置いておく。

# DBのプロパティ
DB_URL=jdbc:mysql://localhost:3306/transport
DB_USER=work
DB_PASSWORD=(workに対して設定したもの)

以前書いた最適化計算をGAMSで行う - GIGでは、Netbeans上から動作を確認したが、Netbeans8に上げてからか、システムの環境変数を拾ってくれない。 どこかで調べなくては。

結局コマンドライン上から動かして見たが、次に「おや?」と思ったのが、結果が以前の同じモデルの計算と異なる(ダメじゃん...)。

*.lstファイルを比較してみたけど、展開した式は同じ...。これも調べなくては。

モデルをひっぺがす

上記の場合、当然SQL分をべた書きしている箇所などは外から読ませほうがいい。 Set, Parameterなどデータの型や次元は、今データベースにいれていない。これらはモデルのメタデータというくくりで、実際のデータと分けた方が良いかもしれない。

例えば、以下のmodel.jsonをModelを登録時に作成するなど(そういえば結果の格納は何にも考えていなかった...)

{
    "Model" : {
        "name" : "transport"
    },
    "Data" : {
        "i" : {
            "type" : "set",
            "dim" : "1"
        },
        "j" : {
            "type" : "set",
            "dim" : "1"
        },
        "a" : {
            "type" : "paramer",
            "dim" : "1"
        },
        "b" : {
            "type" : "paramer",
            "dim" : "1"
        },
        "d" : {
            "type" : "paramer",
            "dim" : "2"
        }
    },
    "Results" : ["x.l", "x.m"]
}

まだまだ考えがまとまっていない。。