福岡は今日も雨

情報系大学生のブログ。主に技術,音楽について。

大学二年生でweb系を1からサービスを立ち上げたりするまでに読んだ本

大学三年生になりました. 明日からまた学校なので, 2年生に読んだ本とかまとめてみようと思う.
ちなみに大学2年の段階ではweb系は全くで, 1年の12月くらいにC言語の本を読んだくらいの能力しかなかったけど,
それでも4~8月でサービスをデプロイするところまでは行けたので, 勉強することは多いけど気合いがあればなんとかなると思う.

4~6月

自分はロック音楽が好きで,ART-SCHOOLというバンドが好きなのだが,周りに同じような人がいなかった.
でも,周りの聞いているアーティストとかみてるとこっちも好きになってくれてもいい潜在的なファン(?)がいるように思えた..
ので,そんなサイトを作りたいなーとかちょっと考えたのがきっかけ.

HTML/CSSとかからスタートした.

作りながら学ぶ HTML/CSSデザインの教科書

作りながら学ぶ HTML/CSSデザインの教科書

これ結構分厚かったけど, 一からサイト作るって感じで分かりやすかった.
jQueryとかもちょろっと乗ってて次何をやればいいかとかも明確になった.
同時進行でPHPもスタートしていて,
いきなりはじめるPHP~ワクワク・ドキドキの入門教室~

いきなりはじめるPHP~ワクワク・ドキドキの入門教室~

気づけばプロ並みPHP 改訂版--ゼロから作れる人になる!

気づけばプロ並みPHP 改訂版--ゼロから作れる人になる!

この辺りも読んだ. これも一から作る感じでモチベーションは高く望めた(ただLAMPのバージョンがちと古すぎた気が..)
ショッピングカートの方は変数がたくさん出てきて少し混乱するところもあった気もする.

この辺でどうやら装飾をするならJavaScriptっていうのをやらないといけないなと気づいて,

これを読んで, なんかそのままこれも読んだ. この段階だと多分読まなくていい本なんだけど,
でも後々いろいろJSを触るときに今でも役立っているから読んでよかったと思う.
(JSのオブジェクト指向とかはちょっと違うからなぁと)
それで一応jQueryも読んだ.
改訂版 Webデザイナーのための jQuery入門

改訂版 Webデザイナーのための jQuery入門

わかりやすかった.

さて, この辺でPHPでサービスを作ろう!とはなったのだけれど, 実際にどこから書けばいいのか分からなかった.
調べていくうちにどうやらサービスを作るときは1から作るというより,フレームワークを利用するらしい.
CakePHPの本を読んだのだけれど, MVCがさっぱりな状態からだったので挫折. もっとわかりやすい本ないかなあと調べていたところ,

はじめてのフレームワークとしてのFuelPHP 改訂版

はじめてのフレームワークとしてのFuelPHP 改訂版

これが分かりやすかった.
一応パーフェクトPHPとかも文法を補うべく読んでた.
パーフェクトPHP (PERFECT SERIES 3)

パーフェクトPHP (PERFECT SERIES 3)

7月

なぜかリクナビに登録して, 福岡のベンチャー企業でペーペーなのにインターンさせていただくことに.
(今思うとよく通ったな..), PHPで働くのかと思っていると,その会社はRubyだったので,急遽Rubyを勉強した.
Rubyは書きやすく分かりやすかった.

作りながら学ぶRuby入門 第2版

作りながら学ぶRuby入門 第2版

Ruby, Web開発とくればRuby on Rails!となって, Railsをメンターの方に教えてもらいながら,conpassみたいなサイトを作った.
パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

これだけだとアレだったので, 自分でもう一冊読んでた.
改訂3版 基礎 Ruby on Rails 基礎シリーズ

改訂3版 基礎 Ruby on Rails 基礎シリーズ

こっちの方が初めてなら分かりやすい.

この頃に自作したいアプリは, 機械学習っていうのが必要だ..!って気づいて,
機械学習ならPythonやれみたいな感じだったので,Pythonをはじめた. これがいい言語で, 今もメインで使っている.

Pythonスタートブック

Pythonスタートブック

入門 Python 3

入門 Python 3

ちなみに, はてなインターンは行けずだった。初心者だから当然と言えば当然..
この時に一回だけはてなのエンジニアさんとお話しできる機会があり, 「Web系と言えど基礎が大切」という話を伺った
その助言は今もずっと残っている

8月

ちょうどよく夏休みで, アプリの作り方もわかってきて, この夏に機械学習でロジック部分を完成させてアプリを出してやろうと考えていた.
まず数学を一年レベルはしっかり固めようということで,

微積(ラグランジュの未定乗数法は絶対にいる)

微分積分 (理工系の数学入門コース 1)

微分積分 (理工系の数学入門コース 1)

線形代数
プログラミングのための線形代数

プログラミングのための線形代数

統計学

そして機械学習. この本面白かった, おすすめ.

ITエンジニアのための機械学習理論入門

ITエンジニアのための機械学習理論入門

k-meansやら協調フィルタリングを駆使して, 自分なりに作ってインターンで発表した.
(今見るとなんでそのモデリングなのか不適切だが)高評価だったので嬉しかった.

www.slideshare.net


これは別のところで発表した資料.

9月

アプリ公開. herokuを利用した.

MusicLanguage

アーティストを打ったらおすすめアーティストをレコメンドしてくれるというもの.
はじめてのアプリということになりそう. 正直この月のことはあまり記憶に残っていない..

10月

大学の後期が始まり, 機械学習にのめり込むようになり, 最適化数学を勉強した.

これなら分かる最適化数学―基礎原理から計算手法まで

これなら分かる最適化数学―基礎原理から計算手法まで

この本は程よく数式があり, 具体例豊富でわかりやすく, とてもよかった.
Deep Learningにも手を出し始めたような気がする..。

定番中の定番.

基礎的な技術が身に付けたいと思い, 競技プログラミングを始めた.
あり本をICPCサークルの人と一緒に読み解いたりした.

このあたりでインターンをやめた
もっと学問として情報科学/計算機科学, 機械学習と向き合いたいと思ったからである

11~12月

あまり覚えていないが, DeepLearningにかなり傾倒していくようになり,
Chainerを利用してデータをぶち込んでぐるぐる回したり, あといろんなDeepLearningを試していたりした

Chainerによる実践深層学習

Chainerによる実践深層学習

実装 ディープラーニング

実装 ディープラーニング

Chainerはバージョンアップで内容が古くなってはいるが, 雰囲気を掴むにはいいかもしれない.

1~3月

親と相談して, 15ヶ月食費を抜きにしてDeepLearning用PCを購入することに成功した.
これにより, ようやくまともにビッグデータをぶちこめるようになった. 購入したのはGTX1070がついたBTOのPC. Ubuntu+Chainer+GPUのセットアップで苦労した.
この頃から, PRMLをしっかり読もう, もうそれしかないなと思ってPRMLを購入. この頃はとりあえず歯を食いしばって頑張っていたが, 最近になってようやくベイズ的考え方が身についてきたと思う. もうちょっと数学をやる必要は感じているが..

パターン認識と機械学習 上

パターン認識と機械学習 上

2月末にLINE BOT AWARDS用に「顔をみてそこから髪型をおすすめするアプリ」をDeepLearningで作り, コンペに提出した.
ファイナリストには残らなかったが, そのあとの後夜祭(4月)でLINE本社で発表する機会をもらえて嬉しかった.交通費も出たのはありがたい..
東京は楽しかったし, きている人のエンジニアのレベルが高いのが頭に残っている. LINEのインターンもいけたらいいな.

www.slideshare.net


4月のはブログに別でまとめている.

これからの予定

ニューラルネットワークに限らず, 機械学習/ベイズ推論/確率的プログラミングの基礎を勉強したい.
SICPみたいな本も読んでいきたい.
設計について深く考えたい.
競プロ力あげたい.
アプリ作りたい.

などなど. 基礎技術を勉強しながら手を動かしてアプリやツールも作っていきたいと思う.
OSSに貢献したりもやってみたいなぁ. まだまだこれからなので, どんどん勉強していきたいと思う

numpyについて2

インデキシング, ブロードキャスティングなどについて詳しく見ていく.

基本インデキシング

まず, numpyのデータに変数を与えることは普通の値を渡す状態なのではなく, 参照渡しであることに注意する.
つまり, ひとつのndarrayオブジェクトに変数を複数渡り当てた場合,ひとつの変数から値を変えると他の変数にも影響が出ることになる
これはビューといって, 余計なメモリを生み出さないための方法である.

In [1]: import numpy as np

In [2]: x2d = np.arange(12).reshape(3, 4)

In [3]: x2d
Out[3]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

# ここは, x2d[1, 1:]とも書ける(numpyのみ)
In [4]: a = x2d[1][1:]

In [5]: a
Out[5]: array([5, 6, 7])

In [6]: a[0] = -1

In [7]: a
Out[7]: array([-1,  6,  7])

In [8]: x2d
Out[8]:
array([[ 0,  1,  2,  3],
       [ 4, -1,  6,  7],
       [ 8,  9, 10, 11]])

応用インデキシング

先ほどのように, ビューが生成されるのではなく, コピーが生成される.
このため, メモリの量には気をつける必要がある.(大きいデータを扱うことがメインのため)

In [11]: dat = np.random.rand(2, 3)

In [12]: dat
Out[12]:
array([[ 0.98810811,  0.53605981,  0.80088131],
       [ 0.44439242,  0.4916097 ,  0.07521805]])

In [13]: bmask = dat > 0.5

In [14]: bmask
Out[14]:
array([[ True,  True,  True],
       [False, False, False]], dtype=bool)

In [15]: highd = dat[bmask]

In [16]: highd
Out[16]: array([ 0.98810811,  0.53605981,  0.80088131])

In [17]: highd[0] = 1000

# datには無影響である
In [18]: dat
Out[18]:
array([[ 0.98810811,  0.53605981,  0.80088131],
       [ 0.44439242,  0.4916097 ,  0.07521805]])

In [19]: highd
Out[19]: array([  1.00000000e+03,   5.36059805e-01,   8.00881315e-01])

整数配列でのインデキシングは, リストを渡すことによって可能である

In [24]: nda = np.arange(10)

In [25]: ndb = nda[1:4]

In [26]: ndb
Out[26]: array([1, 2, 3])

In [27]: ndc = nda[[1, 2, 3]]

In [28]: ndc
Out[28]: array([1, 2, 3])

浅いコピー, 深いコピー

numpyには浅いコピーと深いコピーがある.
浅いコピーには参照が渡される. 基本インデキシングはこちらになる.

深いコピーでは, 値が渡されるので, 元々のデータと別物になる.
応用インデキシングで見せた, ブール配列によるインデキシングや, 整数配列によるインデキシングだけでなく,
copy()メソッドや, flatten(一次元にする), a.clip(min, maxを指定し, min以下のものはminへ, max以上のものはmaxへ)などもこちらに属する

ufunc. ユニバーサル関数について

numpyには, map関数として配列の要素のそれぞれにアクセスして特定の処理をして返す関数があり,
ユニバーサル関数と言われる.

In [30]: nda = np.arange(12).reshape(2, 6)

In [31]: nda
Out[31]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11]])

In [32]: np.square(nda)
Out[32]:
array([[  0,   1,   4,   9,  16,  25],
       [ 36,  49,  64,  81, 100, 121]])

既存のpythonの関数を, frompyfuncを利用することによってufunc化することもできる

In [34]: hex_array = np.frompyfunc(hex, 1, 1)

In [35]: hex_array((10, 30, 100))
Out[35]: array(['0xa', '0x1e', '0x64'], dtype=object)

ブロードキャスティング

  1. , /, *, -などの計算は, 同じ大きさ同士のものでなければ計算することはできない.

この点を解消してくれるのが, ブロードキャスティングである. これが結構ややこしい(というか, 言葉にするより図に書くべきだと思う).
詳しくは, ここをみてほしい

In [36]: nda = np.arange(24).reshape(4, 3, 2)

In [37]: ndb = np.arange(6).reshape(3, 2)

In [38]: ndc = np.arange(3).reshape(3, 1)

In [39]: nda + ndb - ndc
Out[39]:
array([[[ 0,  2],
        [ 3,  5],
        [ 6,  8]],

       [[ 6,  8],
        [ 9, 11],
        [12, 14]],

       [[12, 14],
        [15, 17],
        [18, 20]],

       [[18, 20],
        [21, 23],
        [24, 26]]])

この例だと, ndaに合わせていく形になり, ndb, ndcの次元を一つ増やして3にする. あいているところは, 今と全く同じベクトル/行列で埋める.
次に大きい軸(z, x, y)ならyで埋めていくと, nbcは(4, 3, 2)の形にまで大きくすることが可能. これにより, (4, 3, 2)を出力の行列として計算することができる. このように, ぴったりと埋まらない場合にブロードキャスティングでエラーが発生する. 例えば, 以下の例である.

In [47]: nda = np.arange(12).reshape(3, 4)

In [48]: ndb = np.arange(4)

In [49]: nda + ndb
Out[49]:
array([[ 0,  2,  4,  6],
       [ 4,  6,  8, 10],
       [ 8, 10, 12, 14]])

In [56]: ndb.reshape(4, 1)
Out[56]:
array([[0],
       [1],
       [2],
       [3]])

In [60]: nda + ndb
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-60-c79787edb196> in <module>()
----> 1 nda + ndb

ValueError: operands could not be broadcast together with shapes (3,4) (4,1)

言葉より図をかいて考えるべきだった..

numpyについて

numpyとは,配列の処理に特化したライブラリで, Pythonのそれよりも高速に処理することができる.
行列演算を簡単に, 高速に行うことができるものであり, 以下の3つの機能を提供している.

(1)ユニバーサル関数
(2)各種関数群
(3)C/C++など, 他言語へのインターフェース

numpyが高速な理由の一つに, RAMのメモリに配列のデータがびっしりと敷き詰められるから, というのがある.
こういう形にすることで, オーバヘッドを抑えることが可能になる.

numpyの組み込みデータ型

組み込みデータ型はたくさんある. 数値を扱うものが大半だが,pythonのオブジェクトを格納するものもある.
これらを使って以下のように書く.

import numpy as np

# 組み込みデータ型(配列)の形で生成する
a = np.complex128([3.2, 4.2+1.09j])

# np.array()で生成し, dtypeに属性を付加することもできる
b = np.array([2234.432, 2.23], dtype=np.single)

多次元配列オブジェクトndarrayについて

Pythonのオブジェクトには, 配列と行列があり, これらを区別して使用しなければならない
たとえば, S = (HVH^T)^-1という行列を計算するとき,以下のような書き方ができる.

# 行列を利用する
import numpy as np

H = np.matrix([[1, 2], [3, 4]])

V = np.matrix([[3, 4], [5, 6]])

S = inv(H * V * H.T)

行列を使えば直感的にかけるが, 配列とごっちゃになる可能性があるので, あまり使用しない.
Python3.5~からは, 配列でも同様に演算できるように @演算子が追加された

import numpy as np

H = np.array([[1, 2], [3, 4]])

V = np.array([[3, 4], [5, 6]])

#従来
S = inv(H.dot(V.dot(H.T)))

#python3.5~
S = inv(H @ V @ H.T)

行列は原則使用しないようにする

ndarrayの生成

pythonのリストやタプルは, そのままnp.array(..)にいれることで, ndarrayオブジェクトに変換できる.
しかし, pythonの配列と違い, いくつか注意点がある

(1) 基本的に同じ型になる
(2) 各次元の要素数が同じ

import numpy as np

mat = [[3, 1, 9], [2, 5, 8], [4, 7, 6]]

array1 = np.array(mat)

その他, arrayだけでなく, arange, one, zerosなどがある.
ones, zerosは1のみ, 0のみの行列を生成する. np.zeros(行, 列)で指定する
arangeはrangeと同じで, (start, end, step)で指定する.

import numpy as np

a = np.zeros(2, 3)

b = np.arange(10, 20, 2)

np.arange(..)は浮動小数点も可能だが, そのときendの値が入ってしまうことには注意しておく.

データ型の指定

(1) Generic Typeによる指定
(2) Pythonの組み込みデータ型に対応する型指定
(3) numpyの組み込みデータ型による指定
(4) 文字コードによる指定

がある. 個人的には(3)が一番使われているような気がする

import numpy as np

dt = np.dtype('float')

# データ型のdtypeオブジェクトを引数に
x = np.array([1.1, 2.1, 3.1], dtype=dt)

# 文字で指定することも可能
x = np.array([1.1, 2.1, 3.1], dtype='float')

numpyの属性

numpyには行列演算のメソッドとは別に, ndarrayの状態を取得する属性群がある.
その中でも, 今回はshapeとndimに焦点を当てる(最初ndimがわからなかった)

import numpy as np

mat = [[3, 1, 9], [2, 5, 8], [4, 7, 6]]

array1 = np.array(mat)

array1.shape
# => (3, 3)

array1.ndim
# => 2

shapeは行*列でタプルで返す関数である.
ndimは, 行列の次元数であり, ベクトルなら1, 行列ならば2, 3次元のテンソルなら3..となっていく.

ndarrayの行列演算

行列演算について, ndarrayの+, -, *, / などの演算は要素同士の演算になることに注意をする.
もし, 行列積を出したいときは, dotか@演算子を使って演算すること.
逆行列演算や, 行列式計算, ランク計算はnumpyで簡単に計算できる

In [32]: np.linalg.inv(mat)
Out[32]:
array([[ 0.23214286, -0.50892857,  0.33035714],
       [-0.17857143,  0.16071429,  0.05357143],
       [ 0.05357143,  0.15178571, -0.11607143]])

In [33]: np.linalg.det(mat)
Out[33]: -112.00000000000006

In [34]: np.linalg.matrix_rank(mat)
Out[34]: 3

インデキシング以降を明日に見ていく.

ファイル入出力のあれこれ(pandas編)

pandasはデータを扱うためのライブラリである.
テキストファイルやらデータベースとの入出力が簡単にできる. 今まで見てきたcsv, excel, hdf5なども取り扱うことができる.
read_...()という形なのでインターフェースもわかりやすい.

pandasが優れているところは, 欠損データだったり意味のないスペースなどのファイルの異常にも対応できるということである.
なお, 大規模計算にはhdf5が最速でデータを読み書きできるのでそちらを利用するほうが良いとのこと.

今回のデータがこれ.

# テストデータ 2016/1/10, encode=utf-8
time;status;高度;速度
0;search;125
0.1;search;1012.5; 128.5;
0.2;lock;1035.3; 130.5987
0.3;lock;1068.365; 135.45
0.4;lock;1090.2; NaN

こんな形でデータがある. これを整形していくことを考える.
今回はutf-8で扱う. 元々の科学技術計算のためのpython入門にはcp932だったがまぁ...そこは.

(1)encodingを確かめる
utf-8なので, 普通に読む.

import pandas as pd

dat = pd.read_csv('data.csv', encoding='utf-8')

一行目を消し, デリミタとして';'を設定する.

dat = pd.read_csv('data.csv', 
                               encoding='utf-8',
                               skiprows=1,
                               seq=';'
                               )

これで概ねよくなるが, データに意味のない空白があるので, それを取り除く.

dat = pd.read_csv('data.csv',
                                encoding='utf-8',
                                skiprows=1,
                                seq=';',
                                skipinitialspace=True)

これでおめあてのデータにすることができた.

ファイル入出力のあれこれ(バイナリ編)

昨日はcsvやら, エクセルデータの読み書きを見てきた. 今日はバイナリのデータでそれらを見る.
あとpandasも今日中に見ていく.

pickle

Pythonのオブジェクトをバイナリでそのまま保存/読み込みをする時に利用することができる.

import pickle

my_data = [1, 2, 3]

#ファイルに書き込み
with open('pickle1.pickle', 'wb') as f:
    pickle.dump(my_data, f)

#ファイルから読み込み
with open('pickle1.pickle', 'r') as f:
    pickle.load(f)

複数変数のpickle化は, 以下のように連続して書けばいい

import pickle
import numpy as np

a = np.float(2.3)
b = np.array([[1.1, 2.2, 3.3], [4.4, 5.5, 6.6]])
c = {'yokohama':1, 'tokyo':2, 'nagoya':3}

with open('pickle1.pickle', 'wb') as f:
	pickle.dump(a, f)
	pickle.dump(b, f)
	pickle.dump(c, f)

これだと次の問題が生まれる.
(1)順番にloadしなくては使えなくなる. いくつdumpされているのかがわからない場合もあり, try文で例外を見つける必要がある

(2)変数の名前が損なわれる. もう一回自分で付け直さなければならない

そこで, 辞書型でデータを活用するのが良いのだそう
それなら,(1)の問題も消え, execで実行していくことで,キーを変数にすることができるので(2)の問題も消える

 import pickle
import numpy as np

def pickle_vars(fname, mode='wb', **vars):
	# **varsは可変長のキーワード引数
	dic = {}
	for key in vars.keys():
		exec('dic[key]=vars.get(key)')

	with open(fname, mode) as f:
		pickle.dump(dic, f)

	return dic


if __name__ == '__main__':
	a = np.float(2.3)
	b = np.array([[1.1, 2.2, 3.3], [4.4, 5.5, 6.6]])
	c = {'yokohama':1, 'tokyo':2, 'nagoya':3}

	saved_dat = pickle_vars('pickle1.pickle', a=a, b=b, c=c)

	with open('pickle1.pickle', 'rb') as f:
		dat = pickle.load(f)
		for key in dat.keys():
			exec(key+'=dat.get(key)')

npy, npz形式

npy/npzはnumpyのデータを保存するためのもの.
npyはひとつのnumpyオブジェクトを保存する時に使用して, npzは複数のnumpyオブジェクトを保存する時に利用する. 以下に実際のコードをみていく

import numpy as np

a = np.array([1, 2, 3])
np.save('foo', a)

#ひとつのnumpyオブジェクトの場合, 自動的にnpyに変換される
a = np.load('foo.npy')

#npzは複数のnumpyオブジェクトの時に利用
b = np.array([1, 2], [3, 4])
np.savez('foo.npz', a=a, b2=b)

with np.load('foo.npz') as data:
   a2 = data['a']
   b2 = data['b2']

HDF5形式

データを階層構造にして保存することができる形式
実際にコードを見たほうが早い

import h5py
import numpy as np

t = np.arange(0, 5, 0.1)
y = np.sin(2*np.pi*0.3*t)
dist = [2, 5, 1, 3, 8, 9, 12]

with h5py.File('data1.h5', 'w') as f:
    f.create_group('wave')
    f.create_dataset('wave/t', data=t)
    f.create_dataset('wave/y', data=y)
    f.create_dataset('dist', data=dist)

#データの読み出し
with h5py.File('data1.h5', 'r') as f:
    t = np.array(f['wave/t'])
    y = np.array(f['wave/y'])
    dist = np.array(f['dist'])

ファイルの入出力のあれこれ(上)

Pythonでのファイルの入出力のあれこれのまとめ.
numpyの保存とかその辺りがメイン.

with文

基本的にopen単体で利用することはせず, with文を利用することが大半

with open('path', 'r', encoding='utf-8') as f:
    whole_file = f.read()
    print(f)

read()はファイルのすべてを取り出す. readline()は一行ずつなど. この時, 文字列に全て返還されることに注意する.
数字として扱いたい時はキャストを利用すること.

CSV

ここからは, 各主要なファイルでどのように読み込み, 書き込みをするのかを見ていく.
with文の場合, csvは読み込むことはできるが, 文字列としてインポートされるのであった.
標準モジュールのcsvを利用する場合を見ていこう.

with open('path', 'r', encoding='utf-8') as f:
    whole_file = csv.reader(f)
    print(f)

こちらも文字列として読み込まれるので, 数値を扱うcsvファイルの時はあまり向かない.
numpyにあるloadtxtを利用することもできる.

import numpy as np
data = np.array([1, 2, 3], dtype=np.int32)
np.savetxt('saved.csv', data, fmt='%d', header='time,vel,alt', comments='')

#numpy行列が一行の時, 列で保存されてしまう点に注意
#読み込みは以下(読み込む時はあまり関係ないようだ)

data = np.loadtxt('saved.csv', delimiter=',', skiprows=1, dtype=float)

# array([ 1.,  2.,  3.])

Excel

Excelファイルには拡張子がいくつかあり, それに応じて使うライブラリも異なる

(1)xls -> xlrd, xlwt
(2)ooxml -> openpyxl

これらを使い分ける.

xls

# xlsを使用する
# xlwt .. 書き込み xlrd .. 読み込み

wb = xlwt.Workbook()

ws = wb.add_sheet('シート1')

#書き込み
ws.write(0, 0, 'UpperLeft')
ws.write(1, 0, 1)
ws.write(1, 1, 2)
ws.write(1, 2, xlwt.Formula('A3+B3'))
wb.save('xlwt.xls')

#読み出す
wb = xlrd.open_workbook('xlwt.xls')
st = wb.sheet_by_name('シート1')
print(st.cell(0, 0).value)

openpyxlを利用する時

from openpyxl import load_workbook
import numpy as np

wb = load_workbook(filename='sample.xlsx', read_only=True, data_only=True, use_iterators=True)
ws = wb['温度変化']

Nrows = 11
time_vec = np.zeros(Nrows)
temp_vec = np.zeros(Nrows)

for i, row in enumerate(ws.iter_rows(row_offset=1)):
    time_vec[i] = row[0].value
    temp_vec[i] = row[1].value

明日はpickle, npyなどのバイナリデータや, pandasなどを見ていく.

IPythonのまとめ(プロファイリング)

便利な便利なIPythonを使いこなすための自分用まとめ.
今回はプロファイリング関係でまとめました. その他は次.

プロファイリング関係

(1) %timeit

プロファイリングの時に実行するコマンド.
何回か最適な回数を自動的に実行し, 早く実行できたものの平均値を出力してくれる.

In [1]: import numpy as np

In [2]: %timeit a = [x*x for x in np.random.randn(10000)]
1000 loops, best of 3: 1.75 ms per loop

In [3]: %timeit a = np.random.randn(10000); a = a*a
1000 loops, best of 3: 367 µs per loop

Python(特にnumpy絡み)は処理の違いで実行時間が大きく異なる. IPythonはそれを検証するのに便利
スクリプト全体をプロファイリングしたいときは, 全体の実行時間分布をプロファイリングできる

    In [2]: %run -p -s cumulative prun1.py

    61 function calls (60 primitive calls) in 0.062 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      2/1    0.000    0.000    0.062    0.062 {built-in method builtins.exec}
        1    0.000    0.000    0.062    0.062 <string>:1(<module>)
        1    0.000    0.000    0.062    0.062 interactiveshell.py:2443(safe_execfile)
        1    0.000    0.000    0.062    0.062 py3compat.py:181(execfile)
        1    0.001    0.001    0.062    0.062 prun1.py:1(<module>)
        1    0.001    0.001    0.061    0.061 prun1.py:13(func_both)
        2    0.054    0.027    0.054    0.027 {method 'randn' of 'mtrand.RandomState' objects}
        1    0.004    0.004    0.048    0.048 prun1.py:8(func_b)
        1    0.002    0.002    0.012    0.012 prun1.py:3(func_a)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.compile}
        2    0.000    0.000    0.000    0.000 {built-in method io.open}
        1    0.000    0.000    0.000    0.000 posixpath.py:355(abspath)
        1    0.000    0.000    0.000    0.000 {built-in method posix.getcwd}
        1    0.000    0.000    0.000    0.000 posixpath.py:145(dirname)
        1    0.000    0.000    0.000    0.000 posixpath.py:318(normpath)
        1    0.000    0.000    0.000    0.000 syspathcontext.py:64(__exit__)
        1    0.000    0.000    0.000    0.000 {method 'read' of '_io.BufferedReader' objects}
        1    0.000    0.000    0.000    0.000 _bootlocale.py:23(getpreferredencoding)
        1    0.000    0.000    0.000    0.000 posixpath.py:71(join)
        1    0.000    0.000    0.000    0.000 syspathcontext.py:54(__init__)
        1    0.000    0.000    0.000    0.000 syspathcontext.py:57(__enter__)
        1    0.000    0.000    0.000    0.000 codecs.py:308(__init__)
        1    0.000    0.000    0.000    0.000 posixpath.py:221(expanduser)
        3    0.000    0.000    0.000    0.000 posixpath.py:39(_get_sep)
        1    0.000    0.000    0.000    0.000 {built-in method _locale.nl_langinfo}
        1    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:61(isabs)
        5    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
  • s cumulativeは積算時間をソートして表示する. あんまり求めてないものも出て見にくいような感じがする.

全体ではなく, 一部の関数のみをプロファイリングしたいときは, %prunというマジックコマンドを利用する.

In [4]: %prun [a, b]=prun1.func_both()
         8 function calls in 0.055 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    0.051    0.025    0.051    0.025 {method 'randn' of 'mtrand.RandomState' objects}
        1    0.002    0.002    0.044    0.044 prun1.py:8(func_b)
        1    0.001    0.001    0.055    0.055 prun1.py:13(func_both)
        1    0.000    0.000    0.010    0.010 prun1.py:3(func_a)
        1    0.000    0.000    0.055    0.055 {built-in method builtins.exec}
        1    0.000    0.000    0.055    0.055 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

func_a()に0.010秒, func_bに0.044秒かかっていることが分かる.
このように, prunを利用することで関数レベルで見ることも可能(一応上のにも出てるけど, こちらのほうが絞られていて見やすい)
行レベルでどの式が処理時間をどれくらい必要としているのかを調べるために, line_profilerを使って調べられる. %lprunを使う
ただこのlprunは先に用意が必要で, pip install lineproflierをした後, %load_ext line_profilerを使わなければならない.. 毎回呼び出す必要があるので,
多分configかなんかを触ればその必要が消えるんじゃないかとか思っています.. つかってみると以下

In [6]: %lprun -f func_c func_c()
Timer unit: 1e-06 s

Total time: 0.118285 s
File: /Users/nishimurataichi/today/prun1.py
Function: func_c at line 5

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     5                                           def func_c():
     6         1         1569   1569.0      1.3  	A = np.arange(0, n*n).reshape(n, n) + np.identity(n)
     7         1           21     21.0      0.0  	b = np.arange(0, n)
     8         1       116695 116695.0     98.7  	x = np.dot(np.linalg.inv(A), b)

Timer unitが元となる時間であり, TimeというのがTIme unitの何倍かを表しています. 8行目で116695倍の時間を使っているとわかります. ヒエ〜.
次はメモリのプロファイリングをしていこう

In [9]: %memit a = [x for x in range(1000000)]
peak memory: 148.42 MiB, increment: 62.40 MiB

これだと, 実行文の詳細な行レベルでのメモリ使用量を測定することができない.
それを知りたいときは%mprunマジックコマンドで内訳を見る.

In [11]: mprun -f prun1.func_c prun1.func_c()
Filename: /Users/nishimurataichi/today/prun1.py

Line #    Mem usage    Increment   Line Contents
================================================
     5    127.4 MiB      0.0 MiB   def func_c():
     6    127.4 MiB      0.0 MiB   	A = np.arange(0, n*n).reshape(n, n) + np.identity(n)
     7    127.4 MiB      0.0 MiB   	b = np.arange(0, n)
     8    127.4 MiB      0.0 MiB   	x = np.dot(np.linalg.inv(A), b)


@profileによって, ソースコードに仕込む方法.
ipdb.set_trace()みたいな形で使えるという.

import numpy as np
from memory_profiler import profile

@profile
def func_a():
	a = np.random.randn(500, 500)
	return a**2


@profile
def func_b():
	a = np.random.randn(1000, 1000)
	return a**2


def func_both():
	a = func_a()
	b = func_b()
	return [a, b]


func_both()

結果は以下

In [12]: run prun1
Filename: /Users/nishimurataichi/today/prun1.py

Line #    Mem usage    Increment   Line Contents
================================================
     4    127.4 MiB      0.0 MiB   @profile
     5                             def func_a():
     6    129.4 MiB      1.9 MiB   	a = np.random.randn(500, 500)
     7    131.3 MiB      1.9 MiB   	return a**2


Filename: /Users/nishimurataichi/today/prun1.py

Line #    Mem usage    Increment   Line Contents
================================================
    10    131.0 MiB      0.0 MiB   @profile
    11                             def func_b():
    12    138.6 MiB      7.6 MiB   	a = np.random.randn(1000, 1000)
    13    146.3 MiB      7.6 MiB   	return a**2