Pythonのosモジュールを使ってシンボリックリンクを作成する

概要

pythonのosモジュールの関数を使ってシンボリックリンクを作成する方法です。

os.symlink(src, dst)
# src を指しているシンボリックリンクを dst に作成します。利用できる環境: Unix。

シンボリックリンクを作成するときに、よくどっちがsrcでどっちがdstか分からなくなるので、その辺を整理するためにまとめてみます。

コード(symlink.py)

# -*- coding: utf-8 -*-
import os
import sys

src = sys.argv[1]
dst = sys.argv[2]

# シンボリックリンク作成
#(src を指しているシンボリックリンクを dst に作成します)
os.symlink(src, dst)

使ってみる

  • ディレクトリ構成
 + test
    + symlink.py
    + module1
     + resource
    + module2
     + (ここからtest/module1/resourceにリンクをはる)
  • 実行例

 python symlink.py test/module1/resource test/module2/symlinkfile

これは、lnコマンドを以下のように実行したのと同じ意味です。

 ln -s test/module1/resource test/module2/symlinkfile

なお、lnコマンドでは、以下のようにシンボリックファイル名を省略した場合、自動的にリンク先のファイル名でシンボリックリンクが作成されます。(この例では、test/module2/resourceが作成される)

ln -s test/module1/resource test/module2

これに対して、pythonのsymlink関数ではシンボリックリンクのファイル名を省略するとエラーとなるようです。

python symlink.py test/module1/resource test/module2
Traceback (most recent call last):
File "symlink.py", line 14, in
os.symlink(src, dst)
OSError: [Errno 17] File exists

また、シンボリックリンクの作成先に既に同名のファイルがある場合もエラーとなります。

(test/module2/symlinkfileが存在する状態で)
python symlink.py test/module1/resource test/module2/symlinkfile
Traceback (most recent call last):
File "symlink.py", line 14, in
os.symlink(src, dst)
OSError: [Errno 17] File exists

lnコマンドの場合は-fオプションで強制的に上書き出来ますが、symlink関数の場合はそういう指定はないようなので、一旦ファイルを削除->シンボリックリンク作成という流れになると思います。

以上です。

駄目なプレゼンテーションの11の特徴

最近、他人のプレゼンを聞く機会が多く、様々なプレゼンを聞いています。
分かり易いプレゼンもあれば、全く理解出来ないプレゼンもあったりするわけですが、プレゼンには人それぞれのスタイルがあるのでどのようなプレゼンが良い、と断定するのは難しいです。
しかし逆に、分かりづらいと感じる、または退屈だと感じるプレゼンには共通した特徴があるように思います。
ということで、個人的にこれは駄目だというプレゼンの特徴を以下にまとめてみました。
自分がプレゼンをする際には、これに該当しないように気をつけたいところ。
前半の5つはプレゼン資料に関する項目、後半の6つはプレゼン態度に関する項目です。

資料に記載されている日付がおかしい

プレゼン資料の最初のページに記載された発表日がおかしな資料。
この日付が去年のものだったりすると、去年の資料を使いまわしていることがバレバレ。
資料を使いまわすこと自体は悪いことではない。ただ、去年使った資料を見直しもせず(もちろんリハーサルも行わず)使いまわしているとなると聴衆にとってはあまり気分のいいものではない。
こういう発表は、発表者が昨年の資料を思い出しながら、歯切れの悪い感じで進んでいくことが多い。

図表が見づらい

図表が小さすぎる、または配色がおかしいなどの理由で見づらい資料。
発表中の「え〜、ちょっと図が見づらいですが」は禁句。
見づらいことを自覚しているなら、見やすいように修正して発表すべき。
重要そうな図表なのに、内容が全く把握できないと聴衆はストレスを感じる。

フォントサイズが小さい

文字が小さすぎて読めない資料。
どれだけ内容を詰め込んでも文字が読めなければ何も伝わらない。

無駄なアニメーションや挿絵が多い

発表内容と無関係なアニメーションや挿絵が散りばめられた資料。
アニメーションや挿絵は目立つので、資料の中にこれらがあるとどうしても目をとられる。
で、いつのまにか発表内容についていけなくなる。
こうならないために、発表内容と関係のないものは使わないor極力削るべき。

聴衆が誰であるかを意識していない

聴衆がどういう知識を持った、どういう立場の人であるかを意識出来ていない資料。
聴衆が聞きたいこと以外を延々と述べ、聴衆が聞きたいことをサクッと流すなど。

例えば、新入社員が工場での実習内容報告のプレゼンテーションを行う際に、工場の工程について延々と述べるなどはダメな例。工場の偉い人は自分の工場の工程については発表してもらわなくても知ってる。
聞きたいのはそんなことではなく、そこで何を感じ、何を学んだか、また実習生という立場からの工場に対しての新鮮な意見、提言などである。

聴衆に向かって話していない

ずっとスクリーンまたはPCを見つめたままで、聴衆の方を向かない発表。
聴衆の方を向かずにPCばかり見つめていては説得力に欠ける。
聴衆の方を向くのが苦手な人は、うなづきグセのある人を聴衆の中から見つけて、その人に訴えかけるように話すと気分的に楽。

原稿を読み上げているだけ

自分の言葉で話さずに、ひたすら原稿を読み上げるだけの発表。
原稿を読み上げられると、どうしても淡々と発表が進むため聴衆がついていけない。
外国語での発表ならいざ知らず、母国語の発表は自分の言葉で発表した方がよい。

話すスピードが早い

一つ前の原稿読み上げと重なる部分もあるが、緊張などの理由により、話すスピードが早く聞き取りにくい発表。
自分では遅いと思うぐらいがちょうどよかったりする。自分の発表を録画して見てみるとスピードをつかみやすい。

レーザーポインタの使いどころがおかしい

レーザーポインタが常にスクリーンに向けられており、ちょこまかと目障りな発表。
テキストだけのページ等では、無理にレーザーポインタを使う必要は無い。ポインタで指し示さなくても聴衆は読んでくれる。
レーザーポインタは、本当に必要なときだけ使うようにする。

「え〜」「あのー」が多い

無意識に「え〜」「あのー」を連発している発表。
クセで出てしまう場合もあるが、発表内容の論理展開がおかしい場合などに、自分でも何を言っていいか分からなくなって思わず口に出してしまうケースが多い。
論理展開がしっかりとしており、言いたい事が頭の中で整理されていれば、言葉は自然と出てくる。
「え〜」「あのー」を連発してしまうようであれば、発表資料についても再検討すべき。

時間オーバー

規定の時間を過ぎた発表。
司会や座長に迷惑がかかる。時間内に発表を終えることはプレゼンの最低限のマナー。



以上です。
ここに挙げた項目に注意するだけでも、プレゼンテーションはかなり分かりやすいものになるんじゃないでしょうか。
本記事の内容は、完全に個人的な意見なので、それはオカシイ!などありましたらご意見お待ちしております。

PythonでElementTreeを使ってXMLを処理する方法

ElementTreeはpythonXMLを扱うためのライブラリです。バージョン2.5からはpythonに標準で内蔵されています。
このライブラリを使うと、XMLをパースしてプログラム内で利用しやすい形に変換したり、XMLファイルを生成したりすることが出来ます。そこで今回は、XMLをパースして利用する方法についてまとめたいと思います。

準備

Python2.5以降にはElementTreeが標準で内蔵されているため、パッケージを個別で入手する必要はありません。
以下のようにインポートするだけで利用できます。

from xml.etree.ElementTree import *

Python2.5より古いバージョンを利用している場合は、

からパッケージを入手し、以下のようにインポートして下さい。

from elementtree.ElementTree import *

利用データ

今回の説明では、以下の内容のXMLファイルをサンプルとして扱います。

<window width="1920">
	<title font="large" color="red">sample</title>
	<buttonbox>
		<button>OK</button>
		<button>Cancel</button>
	</buttonbox>
</window>

Elementの作成

ElementTreeを使う上でメインとなるのがElementというオブジェクトです。まずは、このElementの作成方法について以下に例を示します。

# 文字列から作成
xmlString = '<window width="1920"><title font="large" color="red">sample</title><buttonbox><button>OK</button><button>Cancel</button></buttonbox></window>'
elem = fromstring(xmlString) # ルート要素を取得(Element型)

#ファイルから作成
tree = parse("sample.xml") # 返値はElementTree型
elem = tree.getroot() # ルート要素を取得(Element型)

XMLを表す文字列からElementを作成するには、fromstringメソッドを呼び出します。WebAPIなどから取得したデータをパースしたい場合はこの方法を利用することが多いと思います。
また、XMLファイルからデータを読み込む場合はparseというメソッドを利用します。parseの返値はElementTree型となります。このElementTree型はXMLファイルからデータを読み込んだり、書き込んだりする際に利用されるラッパークラスです。ここでは、とりあえず上記の様にparseメソッドを呼んで、その後にgetrootメソッドを呼べばElement型のルート要素が取得できると覚えておけば問題ありません。

データの参照

タグ名、属性(attribute)を参照する方法。

# 要素のタグを取得
print elem.tag
# attributeの取得
print elem.get("width")
# デフォルトを指定してattributeを取得
print elem.get("height", "1200")
# attribute名のリスト取得
print elem.keys()
# (attribute, value)形式タプルのリスト取得
print elem.items()

実行結果
window
1920
1200
['width']
[('width', '1920')]

要素の検索

条件にマッチする要素を検索する方法。

# 条件に一番最初にマッチした要素を返す
print elem.find(".//buttonbox").tag
# 条件にマッチする要素をリストで返す
for e in elem.findall(".//button"):
    print e.tag
# 条件にマッチする一番最初の要素のテキストを返す
print elem.findtext(".//button")
# findtextを分けて書くと以下の通り
print elem.find(".//button").text

実行結果
buttonbox


button
button


OK
OK

find,findall,findtextの引数にはXPath形式を利用することが出来ます。詳細については以下を参照して下さい。
http://effbot.org/zone/element-xpath.htm

要素への順次アクセス

XML内の要素を1つずつ参照する方法。

# すべての要素にアクセス
for e in elem.getiterator():
    print e.tag
# 条件指定(tag名を指定)
for e in elem.getiterator("button"):
    print e.tag
# 子要素のリストを取得(再帰的ではなく、直系の子要素のみ)
for e in list(elem):
    print e.tag

実行結果
window
title
buttonbox
button
button


button
button


title
buttonbox

子要素から親要素へのアクセス

子要素と親要素の関係を保持する方法。公式のマニュアルではジェネレーターを定義する方法と、対応関係を保持するマップを作成してしまう方法が紹介されています。

# ジェネレーターを使った方法
def iterparent(elem):
    for parent in elem.getiterator():
        for child in parent:
            yield parent, child

for p,c in iterparent(elem):
    print c.tag+":"+p.tag

# (child, parent)形式のマップを生成する方法
parent_map = dict((c, p) for p in tree.getiterator() for c in p)
for k in parent_map.keys():
    print k.tag+":"+parent_map.get(k).tag

実行結果
title:window
buttonbox:window
button:buttonbox
button:buttonbox


title:window
buttonbox:window
button:buttonbox
button:buttonbox

以上です。

WindowsにIPythonをインストールして使ってみた(Python2.6)

IPythonとはPython標準シェルの拡張版です。
なかなか評判がいいようで、色々なところでIPythonオススメという内容の記事をみかけます。

TABキーによる補間と、色分けが使えるだけでもインストールする価値があるだろうということで、開発用ローカルPCにIPythonをインストールしてみました。
環境は以下の通りです。

  • OS:Windows Vista
  • Pythonのバージョン: 2.6.1
  • IPythonのバージョン:0.9.1(2009/01/10時点での最新版)

インストールの大まかな流れは公式ドキュメントに従うことにします。

IPython本体のインストール

WindowsにIPythonをインストールするには以下の3つの方法があるようです。

  1. Install using easy_install.
  2. Install using our binary .exe Windows installer, which can be found at here
  3. Install from source, but using setuptools (python setupegg.py install).

今回はeasy_installを使った方法でいきたいと思います。
easy_installとは、PHPPEAR, Rubyのgemみたいなものです。これについては詳しく説明しませんが、もしまだ利用したことのない方は以下のリンクを参考にすると分かりやすいです。

easy_installを使えば、IPythonのインストール自体は非常に簡単です。コマンドライン上で

easy_install ipython

と入力すれば完了です。OSがVistaの場合UACのダイアログがあらわれて煩わしいですが、基本的に「許可」をクリックでOK。以上でIPython自体のインストールは終了ですが、TABキーによる補間機能、色分け機能などを使うためにはPyReadlineというモジュールをインストールする必要があります。以下では、PyReadlineのインストールについて説明します。

PyReadline

ここでは、readlineモジュールのWindows専用版であるPyReadlineをインストールします。公式ドキュメントによると、以下のサイトからダウンロードしたインストーラを使うのが楽だということです。

いくつかファイルがありますが、「pyreadline-1.5-win32-setup.exe」というファイルをダウンロードして実行します。

pythonのバージョンが2.4以下の場合は、さらにctypesモジュールのインストールも必要になるようですが、今回はpython2.6なので必要ありません。そのため、これでIPythonのインストールは完了です。

使ってみる

コマンドライン上でipythonと入力すれば、IPythonが実行できます。パスが通っておらずエラーが出る場合はpythonのインストールフォルダ以下のScriptsフォルダを環境変数に追加してください。
おそらく、IPythonを実行すると以下のようなWarningが出ると思いますが、これはsetsモジュールがpython2.6から非推奨になったためなので仕方ないです。気にせずいきましょう。

C:\Python26\lib\site-packages\ipython-0.9.1-py2.6.egg\IPython\Magic.py:38: DeprecationWarning: the sets module is deprecated
from sets import Set

補間、色分け以外にもたくさん機能があるようなので、利用法については以下のページ等を参考にするといいと思います。

集合知プログラミング - Chapter3 Discovering Groups

集合知プログラミング

集合知プログラミング

最近、日本語訳版が発売されてなにかと話題の「Programming Collective Intelligence(集合知プログラミング)」を買って読んでいます。
上のリンクは日本語訳版になっていますが、実際は原著を買いました。他の方のブログ等でも、内容が濃い、サンプルコードが充実しているなどと評判の本なので、この本で英語&pythonも一緒に勉強してしまおうという魂胆です。
まだ、全体を読み通したわけではありませんが、これまで読んだ部分だけでもかなり満足しています。
1つ1つのテーマを深く掘り下げてはいないものの、様々なテーマを網羅的に扱っており、それぞれについて実際のデータを使ったサンプルコードが記載されているのがこの本のよさだと思います。また英語についても難しい単語や表現はほとんど出てこないのでかなり読みやすいです。
この辺の分野に興味のある人であれば買っておいて損はないかと。

Chapter3 Discovering Groups読了後メモ

この章は、教師なし学習の一種であるクラスタリング手法に関する章。実際の例として、ブログ内の単語出現頻度を特徴としたクラスタリングを行っている。

クラスタリング手法には、大きく分けて階層的手法、非階層的手法の2種類がある。それぞれの特徴等については以下のページが分かりやすい。

非階層的手法の代表的なものにk-means法がある。k-means法のサンプルプログラムを改良する課題が章末にあったのでこれに挑戦。
課題内容は、クラスタリング結果を返す際に各データと、そのデータが属するクラスタのセントロイドとの距離合計を返すというもの。すなわち、クラスタリング精度を算出するということ(距離合計の値が小さければクラスタリング精度が高いことになる)

Exercise5

import random
def kcluster(rows,distance=pearson,k=4):
  # Determine the minimum and maximum values for each point
  ranges=[(min([row[i] for row in rows]),max([row[i] for row in rows]))
  for i in range(len(rows[0]))]

  # Create k randomly placed centroids
  clusters=[[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0]
  for i in range(len(rows[0]))] for j in range(k)]

  lastmatches=None
  for t in range(100):
    print 'Iteration %d' % t
    bestmatches=[[] for i in range(k)]

    # Find which centroid is the closest for each row
    for j in range(len(rows)):
      row=rows[j]
      bestmatch=0
      for i in range(k):
        d=distance(clusters[i],row)
        if d<distance(clusters[bestmatch],row): bestmatch=i
      bestmatches[bestmatch].append(j)

    # If the results are the same as last time, this is complete
    if bestmatches==lastmatches: break
    lastmatches=bestmatches

    # Move the centroids to the average of their members
    for i in range(k):
      avgs=[0.0]*len(rows[0])
      if len(bestmatches[i])>0:
        for rowid in bestmatches[i]:
          for m in range(len(rows[rowid])):
            avgs[m]+=rows[rowid][m]
        for j in range(len(avgs)):
          avgs[j]/=len(bestmatches[i])
        clusters[i]=avgs

  # この処理を追加
    # Calculate the total distance between all the items and their respective centroids
    distancesum = 0
    for i in range(k):
      for rowid in bestmatches[i]:
        distancesum += distance(clusters[i], rows[rowid])

    #print distancesum


  return bestmatches, distancesum

Exercise6

k-means法のkの値(クラスタ数)を変化させた場合のクラスタリング精度を確認するという内容。
1≦k≦30の範囲でkを変化させた結果は以下の通り。横軸がkで、縦軸がクラスタリング精度(値が小さい程精度がよい)。k-means法は初期クラスタの配置による結果への影響が大きい手法なので、一度試しただけではなんとも言えないがk=15あたりからクラスタリング精度の変化が小さくなっている?

ThinkPad X40にPythonをインストールする際の注意

とりあえず結論からいくと、ThinkPad x40にはデフォルトでPython2.2がインストールされているので、自分で新たにPythonをインストールする場合は要注意という話。
ちなみに、ThinkPad X40では以下のディレクトリにPythonがインストールされており、システム環境変数のPathに値が設定されている。

インストール先
C:\IBMTOOLS\Python22

システム環境変数
%SystemDrive%\IBMTOOLS\Python22

この様に、システム環境変数でPython2.2へのパスが設定されているため、新しくインストールしたPythonのディレクトリにPATHを通してもシステム環境変数の方が優先されてしまいPython2.2が実行されてしまうっぽい。
古いのは必要ないのでとりあえず、システム環境変数からPython2.2へのパス設定を削除して対応。

ThinkPadにデフォルトでPythonが入っているなんて全く思ってもみなかったので、気づくのに無駄に時間がかかった。
今やX40ユーザは少なくなっていると思うけど、備忘録という意味合いもこめてのメモ。

CakePHPでHABTMを使う際の「with」と「joinTable」について

概要

CakePHPのアソシエーションのなかで、「多対多」の関係を表すのがHABTM(hasAndBelongsToMany)です。HABTMは、他のアソシエーション(hasOne, belongsTo, hasMany)に比べて多少複雑なため正確に理解するのが難しく、CakePHPのフォーラム(http://cakephp.jp/)でもよく利用法に関する話題などが挙がっているようです。
これまで、なんとなく使い方は分かるものの、細部まで理解せずに使っていたのですが、ちょっと調べてみたところ非常にわかりやすい記事を見つけました。

この記事では、HABTMにおける「with」と「joinTable」の設定について詳しく解説されています。
元の記事は海外の記事なので、自分用のメモという意味も込めて重要な点を簡単にまとめてみたいと思います。

解説

以下では「with」、「joinTable」の設定を変更し、「auto-model名」、「結合テーブル名」をそれぞれ変更する際の注意点についてまとめたいと思います。なお、HABTMにおける「with」はCakePHP1.2から利用可能になった設定項目なので、対象バージョンはCakePHP1.2以降となります。

解説にあたっては、CakePHPのマニュアルでも使われているPost(記事)とTag(タグ)の関係を題材とします。各モデルの基本的な定義は以下の通りです。

<?php
class Post extends AppModel {
     var $name = 'Post';
     var $hasAndBelongsToMany = array('Tag');
}

class Tag extends AppModel {
     var $name = 'Tag';
     var $hasAndBelongsToMany = array('Post');
}
?>

解説に入る前に、まず「with」、「joinTable」について簡単な説明を以下に示します。

with:
 モデル間の関係を表すauto-modelの名前を設定します。今回の例の場合、デフォルトでは「PostsTag」となりますが、それ以外の名前を設定したい場合はここで設定します。

joinTable:
 結合テーブルの名前を設定します。今回の例の場合、デフォルトでは各モデル名の複数形をアンダーバーで繋いだ「posts_tags」となりますが、それ以外の名前を設定したい場合はここで設定します。

「joinTable」によって、結合テーブルの名前を変更する場合

結合テーブルの名前を変更したい場合、モデルの定義は以下のようになるかと思います。

<変更例1>
<?php
class Post extends AppModel {
     var $name = 'Post';
     var $hasAndBelongsToMany = array('Tag'=>array(
                                         'joinTable'=>'my_cool_join_table')
                );
}
?>


このように結合テーブルの名前を変更する際、注意すべき点が2点あります。

  1. joinTableの設定を変更すると、それに応じてauto-model名が書き換えられる
  2. 設定の変更はHABTMで関連する両方のモデルに適応しなくてはいけない。

まず1点目ですが、joinTableの設定を変更した場合、自動的にauto-model名も変更されます。今回の例の場合、joinTableの設定を「my_cool_join_table」に変更したので、auto-model名が「PostsTag」から「MyCoolJoinTable」に書き換えられます。このため、もしソースコード内で既に「PostsTag」を参照している部分があれば、これをすべて「MyCoolJoinTable」に変更するか、withに明示的に「PostsTag」を設定する必要があります。「with」の設定には何も変更を加えていないので、デフォルトのまま「PostsTag」を使えると考えてしまいそうですが、そうではないようです。

次に2点目ですが、上に挙げた<変更例1>ではPostモデルのjoinTableしか変更していません。HABTMは双方向の関係であるため、設定の変更は関連する両方のモデルに適応する必要があります(つい忘れがちです)。今回の例の場合は、TagモデルのjoinTableにも「my_cool_join_table」を設定する必要があります。

「with」によって、auto-modelの名前を変更する場合

auto-modelの名前を変更したい場合、モデルの定義は以下のようになるかと思います。

<変更例2>
<?php
class Post extends AppModel {
      var $name = 'Post';
      var $hasAndBelongsToMany = array('Tag'=>array('with'=>'PostTagJoin'));
}
?>

「with」の設定を変更し、auto-modelの名前を変更した際には、結合テーブル名には影響を及ぼしません。すなわち、<変更例2>の場合、結合テーブル名は「posts_tags_joins」ではなく、「posts_tags」のままです。
また、設定の変更をHABTMに関連する両方のモデルに適応する必要があるのは、「with」の場合も同様です。

まとめ

いくつか注意点について書きましたが、「joinTable」の設定を変更すると、自動的にauto-model名も変更されるというのは結構ハマリやすい点なのではないでしょうか。
うまく利用すれば非常に便利なHABTMなので、今後もさらに理解を深めるために色々と調べてみたいと思います。