ステージ1: Python演習の補足 (情報工学実験 3 : データマイニング班)

# ステージ1: Python演習の補足 (情報工学実験 3 : データマイニング班)

2〜3週間での目標
Pythonならではの特徴
Tips
機能別演習課題

インタプリタ/スクリプト
数学関数
リスト型の基本、関数
フロー制御
文字列処理・操作
File I/O

2〜3週間での目標
言語処理100本ノック 2015の第1章〜第3章程度を記述できるレベルを目指す。最低目標は「どのように書けそうか」を説明でき、一部の部品について具体的に調べながら記述できること。調べながらでOK。
進め方
Pythonのプログラミング能力は少しずつ向上していければ十分。これまでに学んだC/Javaと比べてどの点に注意すべきかを学びつつ、基本的なノウハウとして四則演算、リスト型、辞書型、フロー制御、関数、クラス、文字列処理を部分的に身につけるを当面の目標とする。他要素については適宜その都度調べながら書けるようになろう。

### Pythonならではの特徴

変数は動的に型付けされる。

- 例えば適当な変数を用意して数字／文字列／リスト等を保存した後で、その変数の型を type() で確認してみよう。
- インデントとスコープが連動している。
- 例えばC言語のfor文においてどこからどこまでを繰り返すかは{}で区切られるが、Pythonではインデントで指定する。関数のスコープ、クラスのスコープも同様。
- switch/case文が無い。
- 開発者の方針で存在しません。if ... elif ... elif ...で代用。
- スクリプト実行時とモジュールロード時とで異なる動作を記述できる。
- 例えばmaintest.pyをダウンロードし、(1)ターミナル上からスクリプトとして実行した場合、(2)Pythonインタプリタを起動し、"import maintest"として読み込んだ場合とで動作結果（print出力結果）を比較しよう。これを利用することのメリットとして、例えばユニットテスト自体を同じファイル内に記述することができる。

### Tips

インタプリタ（対話モード）でのタブ機能による補完

- 環境構築でやっているはずのrlcompleterを設定してあると、インタプリタ内でタブによる補完機能が利用できます。これは、例えばクラス変数のように「いくつかの関数や変数を有するオブジェクト」のような変数に対して「クラス変数名.」まで記入した時点でタブキーを押すと、どのような関数／変数が用意されているかを確認することができます。
- オブジェクトや関数についてのヘルプ: help()
- インタプリタで「help(変数/関数/クラス)」を実行すると、そのオブジェクトに関するヘルプ文（正確にはpydocで記述されたドキュメント）を参照することができます。

### 機能別演習課題

### 以下は基本的なコードを書けるかどうかを判断するための課題例です。構築した仮想環境で実行すること。具体的にはpy34で構築したのなら「workon py34」しよう。

インタプリタ/スクリプト

参考:公式チュートリアル2節
課題例1-1: スクリプトの実行。

- hello.pyをダウンロードし、スクリプトとして実行せよ。
- download
- 課題例1-2: インタプリタの実行。
- Pythonインタプリタ（対話モード）を起動し、hello.pyと同じ処理を実行せよ。
- 課題例1-3: 変数の出力。
- 任意の整数／実数／文字列いずれかを変数に保存し、それを print() で出力せよ。
- 課題例1-4: 複数変数の出力。
- 変数2個以上用意（何かしら値を保存）し、それを一つの print() で出力せよ。

- 参考1: Python 3 の文字列フォーマット formatメソッド
参考2: Python3のprintを使ってみよう！
- importの挙動とファイル名の関係

スクリプト実行とimport時の違いを確認してみよう。

- py.tgzをダウンロードし、展開。
動作確認1。ターミナル上で「python maintest.py」を実行。コードを確認しつつ、どの順番で実行されているか確認せよ。
動作確認2。Pythonインタプリタを起動し、変数__name__の中身を確認。その後、「import maintest」を実行し、コードを確認しつつ、スクリプト実行時との違いを確認せよ。（補足:実行されない行があるだけではありません）
- Pythonスクリプトのファイル名について。

- maintest.py を複製し、新しいファイル「maintest-1.py」を作成せよ。複製したmaintest-1.pyに対し、前述と同様に動作確認1,2を確認せよ。その結果、2晩目は正しく動作しないはずである。どのようなエラーが出力されるか確認し、その理由を考えよ。また、このことからファイル名についての制限を検討せよ。
- 数学関数: 四則演算, 絶対値fabs(), 自然対数log(), N乗pow(), 平方根sqrt(), 正弦sin(), 余弦cos(), 正接tan()等

- 参考: 数学関数mathモジュール
- リスト型の基本: スライス, len(), enumerate()
- 参考: 公式チュートリアル3.2.14
課題例4-1: リストの基本。

point = [[0, 1], [1, 2]]

- 上記のように、2次元空間(x,y)における任意の点をリスト型で2つの点が与えられたとする。すなわち1点目は point[0] = [0,1] であり、2点目は point[1] = [1,2] である。
- このとき、(1) point[0] のx座標、y座標を個別に print() 出力せよ（各リストは2個の要素で構成されている。これを1つずつ要素を指定して出力しよう）。また、(2) point[0] のリスト内要素数を print() 出力せよ（勿論正解は2である）。
- 課題例4-2: 距離の算出＋関数の利用。。
- 任意の2点を2つのリスト pos1 = [x1,y1], pos2 = [x2,y2] として与えられたとき、2点間のユークリッド距離を求める関数 euclid_distance() を設計せよ。いくつか2点を与えて正しく計算できているか確認せよ。
- 課題例4-3: リスト型変数へのアイテム追加。
- 課題例4-1のpointを3次元空間へと拡張したい。point[0]=[1,2,0], point[1]=[2,3,0]となるように、z軸上の値を0として追加せよ。また、3つ目の点として[3,4,0]を追加せよ。
- 課題例4-4: スライスによる抽出。

- point = [[1,2,0], [2,3,0], [3,4,0]]
- 課題例4-3終了時点のpointは上記のようになっているはずである。3つ点は各々3次元として記述されているが、やはり3番目の要素zは不要ということになった。pointから全ての3晩目の要素を削除する関数を設計し、動作を確認せよ。
応用課題例4-5: numpyによる行列処理。
- import numpy as np
point = [[1,2,0], [2,3,0], [3,4,0]]
pos_array = np.array(point)
pos_array
pos_array[:,0]
pos_array[:,1]
pos_array[:,2]
pos_array[:,0:2]
pos_array[:,1:2]
delete_z = pos_array[:,0:2]
new_pos = np.array([[10,11],[12,13],[14,15]])
np.r_[delete_z, new_pos]
np.c_[delete_z, new_pos]
pos_array.tolist()

上記のコードは次のような処理をしている。というコードである。各々どのような動作結果になるか観察し、考察せよ。特に、このような処理がどういう時に役立ちそうか検討せよ。（一通り覚えるというよりは、「Numpy 行列」でググって必要な処理を見つけて使えれば十分。転置行列、逆行列、行列積、特異値分解等、一般的な処理はNumpy, Scipyライブラリで対応できるはずです。）

point を数値演算ライブラリNumpyにおけるndarray型(N次元配列)に変換し、
変換結果を保存している pos_array の中身を確認。
pos_array[:,0] の結果を確認。
以下同様に ndarray 型への処理例を確認。
(空行)
3晩目の要素を削除して delete_z に保存。
新しい2点を new_pos に用意。
delete_zとnew_posを結合（後ろの行に）。
delete_zとnew_posを結合（後ろの列に）。
(空行)
ndarray型をlist型に変換。

- 補足: array型とlist型は似ているが異なる型である点に注意。処理対象がどんな型かを調べるにはどうしたら良いだろうか？
- フロー制御: if, for, while, 関数: def

参考: 公式チュートリアル4節
以下は@nakarx/ieリストから収集したツイートをTSVで保存した、~/datasets/Tweets*.tsv を参照しながら進めるものとする。

- Quiz: 上記ファイルはどうやったら取得できる？
- 課題例5-1: リストのループ処理。
- あるユーザのツイート履歴（ツイートID(自然数), ユーザID(自然数), ツイート日時(文字列:%Y-%m-%d %H:%M:%S+09), スクリーン名(文字列), ツイート本文(文字列)）をリストとして用意したとする。例えば tweet = [389745333770518529, 61726083, 'naltoma', '2013-06-11 12:02:17+09', '雑炊で痛いというのは胃腸か何か分からないけど相当酷いことになってそうな。。子供の頃絶食断食やった時は終わった後の「水」ですら痛かったのは覚えてるけど、そういうものとも違うだろうしなぁ。'] 。このtweetリストから次のように出力するプログラムを書け。

- (出力例)
ツイートID: 389745333770518529
ユーザID: 61726083
ツイート日時: 2013-06-11 12:02:17+09
スクリーン名: naltoma
ツイート本文: 雑炊で痛いというのは胃腸か何か分からないけど相当酷いことになってそうな。。子供の頃絶食断食やった時は終わった後の「水」ですら痛かったのは覚えてるけど、そういうものとも違うだろうしなぁ。
- 課題例5-2: 文字列の結合、関数の利用。

課題例4-1で動作させたコードを、関数として作成せよ。ただし、以下の条件を守ること。

- ファイル名: exp4-2.py
関数名: convertTweetToString(tweet)
引数: tweet: 課題例4-1におけるtweetそのもの。
戻り値: 出力用に整形したstring型変数（課題例4-1と同じ出力を行う）。
# 利用時の流れ
>>> string = convertTweetToString(tweet)
>>> print(string)
ツイートID: 389745333770518529
ユーザID: 61726083
ツイート日時: 2013-06-11 12:02:17+09
スクリーン名: naltoma
ツイート本文: 雑炊で痛いというのは胃腸か何か分からないけど相当酷いことになってそうな。。子供の頃絶食断食やった時は終わった後の「水」ですら痛かったのは覚えてるけど、そういうものとも違うだろうしなぁ。
- 課題例5-3: ユニットテスト
課題例5-4:

課題例4-1のようなツイート履歴が多数集まったリスト tweets が下記のように与えられたとする。

tweets = [[tweet0], [tweet1], [tweet2]]
# ここで、上記の [tweet0] は、課題例4-1におけるリストと同じ型だとする。
# つまり、[tweet0]の中には5項目のアイテムが保存されている。
# 具体的なデータセットを用意するため、以下の手順で用意しよう。
# (1) shark:~/datasets/Tweets-*.tsv の最新版を取得。
# (2) mod100.py（テキストファイルからmod行単位で抽出するプログラム）をダウンロード。
# (3) (1),(2)を同じディレクトリに保存し、下記コマンドで小規模なデータセットを作成。
./mod100.py Tweets-(略).tsv 1000 > 1000.tsv
# (4) 上記で作成した 1000.tsv に含まれる冒頭11行について、
# 手動で tweets = [[tweet0],~,[tweet9]] として保存せよ。

- このtweetsに対し、(a)ツイート履歴が何件のがあるか、(b)1件のツイート履歴に含まれている要素数は何件か、(c)複数回ツイートしているユーザについてその合計ツイート数はいくらか、を各々プログラムで求めよ。
- 課題例5-5: mod100.pyの解読。
- mod100.pyを読み、どのような手続きで何を実現しているかを解読せよ。
- 文字列処理・操作: len(), +連結, 改行等削除strip(), 正規表現re, 置換
File I/O: csv/tsvファイル読み込み, pickle

課題例7-1: TSVファイルの読み込み。

- 課題例5-4における tweets を作成する途中段階で 1000.tsv を作成した。このファイルの冒頭11行のみを読み込み、tweets として保存せよ（tweetsを再現せよ）。ここでは「1ツイート=1リスト」で構わない（辞書型を用いる必要は無い）。注意点として、(a)1行目の「tweet_id」から始まる行はコメント行のため保存する必要は無い。(2)ツイートIDとユーザIDは数（自然数／整数）として保存すること。
- 課題例7-2: パターンマッチング。
- (1) 前述 tweets の「ツイート日時」は半角スペースを区切りと看做した時、前半が年月日、後半が時間になっている。一日単位のツイート履歴数（件数）を抽出せよ。
- (2) 「ツイート日時」後半の時間に注目し、ツイートをAM(午前)とPM(午後)に分類せよ。ここでは0時から11時までをAM、12時から23時までをPMとして判定するだけで良い。また、文字列からdatetime型に変更するにはdatetime.strptime()が利用できる。（datetimeを使わない方法でも構わない）
- 課題例7-3: pickleによるオブジェクトのファイル出力＆読み込み。
- (1) 前述 tweets をpickleを用いてファイル tweets.txt に書き出せ。
- (2) （一度close()した後で）書き出した tweets.txt を開き、pickleを用いて tweets 変数にロードし直せ。
- 以下、準備中。
クラス
辞書型の基本: keys(), values(), iteritems()