GIS奮闘記

元GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

PythonでTwitterデータを取得しマップ上で表現する

さて、本日は「PythonTwitterデータを取得しマップ上で表現する」です。つまり、ツイート情報から緯度経度を取得し、その緯度経度ごとにポイントを配置してみようと思います。手順は以下を参照してください。

Twitter APIに接続するためのアカウント情報取得

Twitter APIにアクセスするための情報(consumer_key、consumer_secret、access_token、access_secret)を取得してください。

各種必要なライブラリ等のインストール

必要な各種ライブラリをインストールしてください。

Twitter REST APIsを使うための認証ライブラリは必ずインストールしてください。
pip install requests_oauthlib

また、MongoDBもインストールが必要です。詳細は以下を参照してください。
PythonでMongoDBを弄ってみる - GIS奮闘記

これで準備完了です。まずはツイート情報を取得してみたいと思います。今回は「フットサル」という文言を含んだツイートを取得します。
ただ、ここでちょっと制限が。ツイート情報は1回のアクセスで100ツイートしか取得できず、アクセスも15分で180回までになったらしいです。

■GetTweet.py

# -*- coding: utf-8 -*-
from requests_oauthlib import OAuth1Session
import json, datetime, time, pytz, re, sys,traceback, pymongo
from pymongo import MongoClient
import numpy as np

KEYS = { # 自分のアカウントで入手したキーを下記に記載
        'consumer_key':'*****************',
        'consumer_secret':'*****************',
        'access_token':'*****************',
        'access_secret':'*****************',
       }

twitter = None
tweetdata = None
meta    = None

def initialize(): # twitter接続情報や、mongoDBへの接続処理等initial処理実行
    global twitter, tweetdata, meta
    twitter = OAuth1Session(KEYS['consumer_key'],KEYS['consumer_secret'],
                            KEYS['access_token'],KEYS['access_secret'])
    connect = MongoClient('localhost', 27017)
    db = connect.footsal     #「footsal」というDBを作成する
    tweetdata = db.tweetdata #「tweetdata」というコレクション(テーブル)を作成する
    meta = db.metadata       #「metadata」というコレクション(テーブル)を作成する

def getData():
    #「フットサル」を含んだツイートを検索
    res = getTweetData(u'フットサル')

    #成功した場合のみ
    if res['result']==True:
        # metadata処理
        if len(res['statuses'])==0:
            sys.stdout.write("statuses is none. ")
        elif 'next_results' in res['metadata']:
            # 結果をmongoDBに格納する
            meta.insert({"metadata":res['metadata'], "insert_date": now_unix_time()})
            for s in res['statuses']:
                tweetdata.insert(s)
            next_url = res['metadata']['next_results']
            pattern = r".*max_id=([0-9]*)\&.*"
            ite = re.finditer(pattern, next_url)
            for i in ite:
                mid = i.group(1)
                break

# 検索ワードを指定して100件のTweetデータをTwitter REST APIsから取得する
def getTweetData(search_word):
    url = 'https://api.twitter.com/1.1/search/tweets.json'
    params = {'q': search_word,
              'count':'100',
             }

    # Tweetデータの取得
    req = twitter.get(url, params = params)

    # 取得したデータの分解
    if req.status_code == 200: # 成功した場合
        timeline = json.loads(req.text)
        metadata = timeline['search_metadata']
        statuses = timeline['statuses']
        limit = req.headers['x-rate-limit-remaining'] if 'x-rate-limit-remaining' in req.headers else 0
        reset = req.headers['x-rate-limit-reset'] if 'x-rate-limit-reset' in req.headers else 0
        return {"result":True, "metadata":metadata, "statuses":statuses, "limit":limit, "reset_time":datetime.datetime.fromtimestamp(float(reset)), "reset_time_unix":reset}
    else: # 失敗した場合
        print ("Error: %d" % req.status_code)
        return{"result":False, "status_code":req.status_code}

# 現在時刻をUNIX Timeで返す
def now_unix_time():
    return time.mktime(datetime.datetime.now().timetuple())

if __name__ == '__main__':

    #初期化
    initialize()

    count = 0

    #Tweetデータを取得(とりあえず180回1セットで)
    while(count < 180):
        try:
            count = count + 1
            #データ取得
            getData()
        except:
            print "Unexpected error:", sys.exc_info()[0]
            break

こんな感じでツイート情報を取得しました。一回の実行ではデータが少ないので、何度か実行した方がいいかもしれません(←今さらながら自動化すればよかったと。。。)。

そして、ツイート情報自体に"coordinates"というフィールドが含まれており、GPSなどの位置情報付きでつぶやいた場合はここに緯度経度が含まれます。だがしかし、位置情報付でツイートを行うユーザーは皆無に等しいことが判明・・・(←ツイッター未経験のため知りませんでしたorz)。そこで形態素解析ですね!
形態素解析 - Wikipedia

Mecabという形態素解析エンジンを使用するのですが、Windowsで使用するにはMinGWVisual Studioのインストール、コードの修正が必要でかなり面倒くさいので、Pythonモジュールはid:fgshunさんがコンパイルしたバイナリを使わせてもらいました。形態素解析エンジン MeCab 0.98pre3 野良ビルド - 銀月の符号
以下、導入方法です。

1.MeCabの本サイトでダウンロードしたWindows版のmecab-0.98.exeをインストール
2.形態素解析エンジン MeCab 0.98pre3 野良ビルドからダウンロードしたlibmecab-1.dll、MeCab.py、_MeCab.pydをパッケージフォルダ(Python2.7ならC:\Python27\Lib\site-packages)にコピー。

早速試してみます。

import MeCab
tagger = MeCab.Tagger("-Ochasen")
text = u"私は横浜に住んでいます。"
text_encode = text.encode('utf-8')
result = tagger.parse(text_encode)
result = result.decode('utf-8') # 必ずdecode
print result

「私は横浜に住んでいます。」という文章を形態素解析した結果が以下です。

私	ワタシ	私	名詞-代名詞-一般		
は	ハ	は	助詞-係助詞		
横浜	ヨコハマ	横浜	名詞-固有名詞-地域-一般		
に	ニ	に	助詞-格助詞-一般		
住ん	スン	住む	動詞-自立	五段・マ行	連用タ接続
で	デ	で	助詞-接続助詞		
い	イ	いる	動詞-非自立	一段	連用形
ます	マス	ます	助動詞	特殊・マス	基本形
。	。	。	記号-句点		

「横浜」という文言は固有名詞-地域と認識されます。この仕組みを利用してツイート本文から地名を抜き出したいと思います。

ツイート本文からの位置情報推測

以下ツイート本文から地名を抜き出しMongoDBにデータを取込ます。

■GetLocationName.py

#coding:utf-8
from pymongo import MongoClient
from collections import defaultdict
import MeCab

def main():
    connect = MongoClient('localhost', 27017)
    db = connect.footsal
    tweetdata = db.tweetdata

    for d in tweetdata.find({'spam':None,'retweeted_status': None},{'_id':1, 'text':1}):
        ret = location_name_mecab(d['text'])
        if len(ret) > 0:
            #地域名称をDBに入れる(強引だが一番最初の要素を地名とする)
            tweetdata.update({'_id' : d['_id']},{'$push': {'location_name':ret[u'地域名称'][0]}})

def location_name_mecab(sentence):
    #形態素解析
    t = MeCab.Tagger('-Ochasen')
    sentence = sentence.replace('\n', ' ')
    text = sentence.encode('utf-8')
    node = t.parseToNode(text)
    result_dict = defaultdict(list)
    for i in range(140):
        if node.surface != "":  # ヘッダとフッタを除外
            # 固有名詞かつ、地域の単語を選択
            if (node.feature.split(",")[1] == "固有名詞") and (node.feature.split(",")[2] == "地域"):
                plain_word = node.feature.split(",")[6]
                if plain_word !="*":
                    result_dict[u'地域名称'].append(plain_word.decode('utf-8'))
        node = node.next
        if node is None:
            break
    return result_dict

if __name__ == '__main__':
    main()

地名から緯度経度に変換する

上記で地名を取得することができましたので、ジオコーディングの出番ですね。ジオコーディングについては以下参照です。
Pythonでジオコーディングをやってみる - GIS奮闘記

■GetLatLng.py

# -*- coding: utf-8 -*-
import geocoder
from pymongo import MongoClient

def main():
    connect = MongoClient('localhost', 27017)
    db = connect.footsal
    tweetdata = db.tweetdata

    for d in tweetdata.find({'location_name':{"$ne":None},'spam':None,'retweeted_status': None},{'_id':1, 'location_name':1}):
        for name in d['location_name']:
            loc = get_coordinate(name)
            if loc.lat is not None:
                #緯度経度をDBにつっこむ
                tweetdata.update({'_id' : d['_id']},{'$push': {'lat':loc.lat, 'lng': loc.lng}})
            loc = None

def get_coordinate(location_name):
    try:
        #地名から座標を取得する
        ret = geocoder.google(location_name)
    except KeyError, e:
        "KeyError(%s)" % str(e)
        return

    return ret

if __name__ == '__main__':
    main()

これで緯度経度の取得完了です。

位置情報の可視化

それでは上記で取得した緯度経度を元に地図上にポイントをプロットします。今回はGoogleMapではなくMatplotlib basemapというライブラリを使用しますので、以下サイトでWheelをダウンロードしてpip install basemap-1.0.8-cp27-none-win32.whl のような感じでお願いします。
http://www.lfd.uci.edu/~gohlke/pythonlibs/

■PlotPoint.py

# -*- coding: utf-8 -*-
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pymongo import MongoClient

def main():
    connect = MongoClient('localhost', 27017)
    db = connect.footsal
    tweetdata = db.tweetdata

    #マップの設定
    fig = plt.figure(figsize=(10,10))

    #緯度経度は日本全体が入るように適当に設定
    m = Basemap(projection='merc',llcrnrlat=25.836887 ,urcrnrlat=47.336893 ,\
                llcrnrlon=126.919922,urcrnrlon=152.671875 	 ,lat_ts=20, resolution='f')
    m.drawcoastlines( linewidth=0.25, color='k' )
    m.fillcontinents(color='#eeeeee',lake_color='#ddeeff')
    m.drawstates( linewidth=0.25, color='k' )
    m.drawcountries()
    m.drawmapboundary(fill_color='#ddeeff')

    #ポイントをプロット
    for d in tweetdata.find({'lat':{"$ne":None},'lng':{"$ne":None},'spam':None,'retweeted_status': None},{'_id':1, 'lat':1,'lng':1}):
        lat = d['lat']
        lon = d['lng']
        t_x, t_y = m(lon,lat)
        m.plot( t_x, t_y, "bo",markersize=5)

    #マップを表示
    plt.show()

if __name__ == '__main__':
    main()

これでポイントの配置が完了しました。結果を見てみます。

f:id:sanvarie:20151211093343p:plain

おぉーちゃんとプロットされましたね!今回はデータが少なかったので、あまり数がありませんが、本格的にデータを格納すれば分布図などができそうです(都市部を中心にプロットされそう)。
ただ、マップ上の表現としてはGoogleMapにしておけばよかったです(笑)。これはマップの鮮明さなどがちょっといまいちですかね。ただ、これはこれで便利なライブラリだと思うので、使いこなすことができたらまたおもしろいことができるかもしれませんね。

今回わかったことは
・ツイート情報に位置情報はほとんど付与されていないこと
・ツイート本文から位置情報を推測してもやはり正確な位置をつかむのは難しいこと(例えば、「川崎フロンターレのユニフォームを着て、フットサルなう」というツイートの場合、川崎として認識してしまう)
形態素解析の素晴らしさ
・ツイート情報は1回のアクセスで100ツイートしか取得できず、アクセスも15分で180回という制限がある
・geocoderの使用制限(同一IPから一日2500件が上限らしいです)
・自分のプログラミング能力の低さ
あたりでしょうか。

色々なサイトを参考にさせてもらい、とても勉強になりました。今回の経験をもとにbotを作ってみてもおもしろいかも!

以上、本日は「PythonTwitterデータを取得しマップ上で表現する」でした。

PythonでMongoDBを弄ってみる

今回はMongoDBについてです。MongoDBって最近よく聞くけど一体何なの?!という方もまだ多いかと思います。MongoDBは,いわゆる「ビッグデータ」を処理可能なパフォーマンスをもつNoSQLの性能と,RDBMSのもつ豊富な機能をバランスよく取り込んだソフトウェアです。

■特徴
・NoSQL(Not only SQL)の1種でありドキュメント指向データベース。
スキーマレス(テーブルの構造が固定ではなく、自由にデータを突っ込める)。
・RDMBSと比較するとスケールアウトが容易。
・レコードはBSON (Binary JSON) の形で扱われる。
・JOINはできない。JOINをしなければならない場合はRDBMSをチョイスする必要がある。
・etc

この中でもやはりスキーマレスというのがRDBMSとの決定的な違いでしょうか。
完全にRDBMS脳になってしまっているので、慣れるまで少し時間が必要そうですね。

f:id:sanvarie:20151207161048p:plain


詳しくは以下サイトをご参照ください。
http://itpro.nikkeibp.co.jp/article/COLUMN/20120306/384811/

インストール

まずは以下サイトからインストーラーをダウンロードします。

https://www.mongodb.org/downloads#production

  • インストール後、exeファイルの場所をPathに追加

 - 例:C:\Program Files\MongoDB\Server\3.0\bin
 - コマンドプロンプトでmongod --versionが通ってればOK

起動方法

  • データを保存する場所とログを保存する場所を確保する

 - データの保存場所の例:D:\mongodb\data
 - ログの保存場所の例:D:\mongodb\logs

 - 例:mongod --dbpath D:\mongodb\data --logpath D:\mongodb\logs\mongodb.log

PyMongoのインストール

PyMongoとはPythonのMongoDBクライアントのことです。
pip install pymongoでインストールしてください。

MongoDBの導入については以上です。さて、続いてはPythonでMongoDBを操作してみましょう!

■sample.py

# -*- coding: utf-8 -*-
import pymongo

def main():
    client = pymongo.MongoClient(host='localhost', port=27017) #接続
    test_db = client.test                                      #「test」というDBを作成する
    collection = test_db.my_first_collection                   #コレクション=テーブル
    collection.insert_one({'name': 'foo', 'age': 10})          #コレクションにドキュメント(レコード)を挿入。JSON形式
    data = collection.find_one()                               #一件目のデータを取得する

    #各種値の確認
    print u'DB = ' + test_db.name
    print u'テーブル= ' + test_db.collection_names(include_system_collections=False)[0]
    print u'レコード= '
    print collection.find_one()

if __name__ == '__main__':
    main()

コンソールの確認をします。

f:id:sanvarie:20151208071134p:plain

簡単にMongoDBデビューができました!
次はこれを使ってもう少しビッグデータの処理っぽいことをしてみたいですね。

Pythonでスクレイピングした結果をGoogleMapに反映してみた

本日はPythonでスクレイピングした結果をGoogleMapに反映してみました。具体的に言いますと、スクレイピングした情報(緯度経度)の位置にGoogleMap上でマーカーを作成します。スクレイピングについては以下ご参照ください。
ウェブスクレイピング - Wikipedia
なんだか小難しく書いてありますが、Webサイトから情報を引っこ抜くくらいの認識で問題ないと思います。

まず、スクレイピングはBeautifulSoupを推しているサイトもけっこうあったのですが、lxmlを使用してみました。pip install lxmlか以下サイトからダウンロードをする必要があります。
https://pypi.python.org/pypi/lxml/2.3
その他必要なライブラリはインストールをお願いします。

対象のサイトは以下です。
神奈川県内の水道事業者一覧 - 神奈川県ホームページ
神奈川県の水道事業者の一覧からその場所の緯度経度を取得して、そこにGoogleMap上でマーカーを作成しようと思います(実はわたくし水道関係の仕事をしております)。

■sample.py

# -*- coding: utf-8 -*-
from lxml.html import parse
from urllib2 import urlopen
import geocoder
import json

def main():
    parsed = parse(urlopen('http://www.pref.kanagawa.jp/cnt/f1029/p70915.html'))
    doc = parsed.getroot()
    blog_node = doc.xpath('//div[@class="detail_free"]')[0]

    json_data = []
    for a in blog_node.xpath('descendant::a'):
        href = a.get("href")
        if href:
            lat,lng = geo(a.text)
            if lat: #緯度経度が取得できたもののみ
                json_data.append({
                                "name": a.text,
                                "lat": lat,
                                "lng": lng
                                })

    with open('data.json', 'w') as outfile:
        json.dump(json_data, outfile, indent=4, sort_keys=True)

def geo(name):
    Localname = name
    g = geocoder.google(Localname)
    return g.lat,g.lng

if __name__ == '__main__':
    main()

上記では対象のサイトから情報を取得し、JSONとして出力しています。
それでは出力したJSONを読み込みたいと思います。

■sample.js

var map;

function initialize() {
  var latlng = new google.maps.LatLng(35.383575, 139.344170);
  var options = {
    zoom: 10,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  map = new google.maps.Map(document.getElementById("map_canvas"), options);
  map.setCenter(latlng);
}

function loadJson(){
  var json = [];
  $(function(){
    $.getJSON("data.json" , function(data) {
    json = data;
    });
  });

  //JSONの要素数分マーカーを作成
  for (i = 0; i < json.length; i++) {
    latlng = new google.maps.LatLng(json[i].lat,  json[i].lng);
    var marker = new google.maps.Marker({
        position: latlng, 
        map: map,
    });
  }
}

JSONの読み込みには$.getJSONを使用しました。なので、jqueryも必要になりますね。
最後にhtmlです。

■sample.html

<!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <meta name="robots" content="noindex,nofollow,noarchive" />
    <title>GoogleMapsAPI Sample</title>
    <script src="http://maps.googleapis.com/maps/api/js?key=AIzaSyBu5Wf66_X6xMaD8y1B3qdZ8gCQM5gUl70&sensor=false"
            type="text/javascript"></script>
    <script src="./js/sample.js" type="text/javascript"></script>
    <script src="./js/jquery-1.5.2.js" type="text/javascript" ></script>
  </head>
  <body onload="initialize()">
    <div id="map_canvas" style="width: 500px; height: 500px"></div>
    <form>
    <p>
    <input type="button" id="btn" value="マーカー作成" onclick="loadJson()" />
    </p>
    </form>
  </body>
</html>

マップとボタンを作って、ボタンを押したらマーカーが作成される作りにしてみました。さて、実際に動かしてみます。


f:id:sanvarie:20151206103235p:plain


まずはマップが表示されました。続いて「マーカー作成」ボタンを押してみたいと思います。


f:id:sanvarie:20151206103336p:plain


マーカーが作成されました!
拡大してみると、きちんと水道事業者の位置にマーカーが作成されています。いやーすごいですね。今後はこれをもっと動的に実行できるようにしてみたいですね。ジオコーディング、逆ジオコーディングをするサイトのような感じでしょうか。


f:id:sanvarie:20151206103444p:plain


以上、本日はPythonでスクレイピングした結果をGoogleMapに反映でした。

GoogleMapsAPIを使ってみよう

本日はGoogleMapsAPIを使ってみたいと思います。
GoogleMapsAPIとは簡単に言うと「GoogleMapを自分のサイトでも使ってみたい」という方向けにGoogleが無償公開している便利なものです。飲食店のサイトなんかによくあるあれですね。

さて、前回はジオコーディングで国立競技場の緯度経度「35.679933, 139.714465」を取得しました。
Pythonでジオコーディングをやってみる - GIS奮闘記

この情報を使用してGoogleMapをHTMLに埋め込んでみたいと思います。
まず以下を用意する必要があります。
①Googleのアカウント
②APIKey

①は問題ないと思います。②ですが、色々なサイトでやり方がのっているので参考までに。本当に簡単ですね。
Google Maps JavaScript APIの使い方まとめ

準備ができたところでコーディングをしてみたいと思います。まずはJavascriptから

■sample.js

function initialize() {
  var latlng = new google.maps.LatLng(35.679933, 139.714465);
  var opts = {
    zoom: 16,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  
  var map = new google.maps.Map(document.getElementById("map_canvas"), opts);
  var mark = {
    position: latlng,
    map: map
  };

  var marker = new google.maps.Marker(mark);
}

マップの読み込みをするだけの簡単なコードですね。国立競技場の緯度経度「35.679933, 139.714465」を使用してます。また、var marker = new google.maps.Marker(mark)でマーカーも作成しています。
次にHTMLですね。

■map.html

<!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="robots" content="noindex,nofollow,noarchive" />
    <title>Google Maps Sample</title>

    <script type="text/javascript"
      src="http://maps.googleapis.com/maps/api/js?key=APIKey&sensor=false">
    </script>

    <script src="./js/sample.js" type="text/javascript"></script>

  </head>
  <body onload="initialize()">
    <p>国立競技場です。</p>

    <div id="map_canvas" style="width:500px; height:300px"></div>

  </body>
</html>

「APIKey」の個所は取得されたAPIKeyを記述してください。
さて結果はどうなったでしょうか。

f:id:sanvarie:20151128155503p:plain

国立競技場が表示されて、マーカーも作成されました。
これ、スクレイピングとかしたらもっと面白そうなことができそうな気がする。
以上、本日はGoogleMapsAPIについてでした。

Pythonでジオコーディングをやってみる

本日はPythonでジオコーディングをやってみたいと思います。

ジオコーディングって何?という方は以下をご参照ください。
「ジオコーディング(英語: geocoding)は、各種情報に対して、関連する地理座標(典型的には緯度・経度)を付加すること、およびこれに関する技術やソフトウェアをいう。付加された地理座標のことをジオコードと称する。」
ジオコーディング - Wikipedia

なんか説明が小難しいですね。「指定した住所に緯度経度を返してくれる」ことをジオコーディングと言う。こんな認識で問題ないと思います。

ジオコーディングについては以下のエントリーでも別のライブラリを紹介していますので、ぜひ読んでみてください。
www.gis-py.com

今回は「国立競技場」の緯度経度を取得したいと思います。

まずは「geocoder」というPythonのライブラリをダウンロードしましょう。
https://pypi.python.org/pypi/geocoder

ダウンロードしたwheelを以下のような感じでインストールすれば準備完了です。
pip install D:\geocoder-1.6.4-py2.py3-none-any.whl

# -*- coding: utf-8 -*-
import geocoder

def main():
    Localname = u"国立競技場" #検索する住所
    g = geocoder.google(Localname)
    print g.latlng

if __name__ == '__main__':
    main()

これで「国立競技場」の緯度経度が取得できます。すごくシンプルなコードで済みますね。
インタプリタ上の結果を見てみましょう。

f:id:sanvarie:20151121204828p:plain

「35.679933, 139.714465」と出ましたね。
ではこの緯度経度をGoogleMap上で確認します。

f:id:sanvarie:20151121205134p:plain

一致しましたね。とても簡単にできました。
これを応用してGoogleMapと連携すれば以下エントリーのようなこともできます。

www.gis-py.com

以上、本日はジオコーディングについてでした。

PyPyを試してみる

今回はPyPyについてです。Pythonのいいところは手軽さだったり、ライブラリの豊富さだったりあげたらきりがありません。しかし、Pythonには遅いという欠点があります。ここはスクリプトということで致し方ないところはありますが、GISでも大量のデータを扱うことが多いため、どうしても高速化が必要になります(←ブログタイトルに無理矢理こじつけ)。そこでPyPyですよ!

PyPyとは
「PyPy(パイパイ)は、プログラミング言語Pythonの実装の1つであり、Pythonで記述されたPythonの処理系であることが特徴の1つである(セルフホスティング)。PyPyは、実行速度と効率、およびオリジナルのPython実装であるCPythonとの互換性に重点を置いている。」
PyPy - Wikipedia
簡単にいうとPythonを高速で実行できるということですね。やったー!ただ、日本ではこの名前によって受け入れられない気が?!

公式サイトからダウンロードして配置すれば使用できます。
http://pypy.org/

とりあえずサンプルとしてフィボナッチ数列を出力するプログラムを作ってみました。
フィボナッチ数列とは
フィボナッチ数列(フィボナッチスウレツ)とは - コトバンク
不思議ですねー。

#coding:utf-8
import time

def fib2(n):
    if n <= 2:
        return 1
    else:
        return fib2(n-1) + fib2(n-2)

if __name__ == "__main__":
    start = time.time()
    for i in xrange(1,30):
        print fib2(i)
    elapsed_time = time.time() - start
    print("elapsed_time:{0}".format(elapsed_time))

■通常のPythonで実行
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
elapsed_time:0.601999998093

■PyPyで実行
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
elapsed_time:0.169999837875

なんと3~4倍速くなりました。公式的にはもう少し早くなるみたいですね。
今回はPyPyについてでした!

PythonスクリプトのEXE化

さて、前回はWxpythonのインストールについてでしたが、今回は作ったスクリプトをEXEに変換する方法を書きたいと思います。これでArcpyをGUIで動かすことができますね。変換にはpy2exeというライブラリを使用します。

■py2exeのダウンロード
下記リンクよりpy2exeをダウンロードしましょう。

http://sourceforge.net/projects/py2exe/files/py2exe/

ダウンロード終了後、インストールを行います。全てデフォルト設定で構いません。

■exe化
exe化にあたり、必要なものは下記4つとなります。

  • py2exe (変換ライブラリ)
  • sample.py(変換元ソースファイル、ファイル名は任意)
  • setup.py(セットアップスクリプト、ファイル名は任意)

1つ目は問題ないと思います。2つ目は上記でクリア。3つ目と4つ目について書きます。

まずはソースファイルです。
■ファイル名:sample.py

# -*- coding: utf-8 -*-
import wx

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='My Form')
        self.panel = wx.Panel(self, wx.ID_ANY)

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

実行するとフォームが立ち上がるだけの単純なプログラムです。
次はセットアップスクリプトです。

■ファイル名:setup.py

# -*- coding: utf-8 -*-
from distutils.core import setup
import py2exe

option = {
    "compressed"    :    1    ,
    "optimize"      :    2    ,
    "bundle_files"  :    3
}

setup(
    options = {
        "py2exe"    :    option
    },

    windows = [
        {"script"   :    "sample.py"}
    ],

    zipfile = None
)

注意が必要なのは「bundle_files」です。64bitの場合はここを1、2にすると動きません(うーん、なぜなのか)。「bundle_files」=1がランタイムdllを含めてということ。2がその逆です。3は何なのだろうか・・・ちなみに他は

  • 「compressed」 出力を圧縮するかしないか
  • 「optimize」 最適化するかしないか

です。また、GUIの場合は「windows」という指定が必要です。そうでない場合は「console」と指定してください。

これで準備完了です。それではEXE化を始めましょう。

  • 上記2ファイルを適当なディレクトリに配置
  • python setup.py py2exe を実行


実行するとこんなメッセージが出ますが、無視してかまいません。
Make sure you have the license if you distribute any of them, and
make sure you don't distribute files belonging to the operating system.

ole32.dll - C:\Windows\system32\ole32.dll
OLEAUT32.dll - C:\Windows\system32\OLEAUT32.dll
USER32.dll - C:\Windows\system32\USER32.dll
COMCTL32.dll - C:\Windows\system32\COMCTL32.dll
SHELL32.dll - C:\Windows\system32\SHELL32.dll
MSVCP90.dll - C:\Windows\system32\MSVCP90.dll
WINMM.dll - C:\Windows\system32\WINMM.dll
WSOCK32.dll - C:\Windows\system32\WSOCK32.dll
COMDLG32.dll - C:\Windows\system32\COMDLG32.dll
ADVAPI32.dll - C:\Windows\system32\ADVAPI32.dll
WS2_32.dll - C:\Windows\system32\WS2_32.dll
WINSPOOL.DRV - C:\Windows\system32\WINSPOOL.DRV
GDI32.dll - C:\Windows\system32\GDI32.dll
KERNEL32.dll - C:\Windows\system32\KERNEL32.dll
RPCRT4.dll - C:\Windows\system32\RPCRT4.dll

対象のディレクトリに「build」「dist」というフォルダが作成されたと思います。「dist」フォルダにEXEが格納されています。
f:id:sanvarie:20151115103338p:plain

EXEを実行してみます。
f:id:sanvarie:20151115103511p:plain

成功しました。これでEXE化の完了です!