Deep Learning勉強会に参加しました

大阪大学人工知能研究会

Deep learningの勉強会に参加して、TensorFlowのBeginnersチュートリアルと、多層パーセプトロンの実装をPythonでやりました。基本的なところは青本やCouseraなどでカバーしてありましたので導入部分は特に問題無く進めましたが、ニューラルネットワークでも理解の難しい誤差逆伝播法の導出や確率的勾配降下法の利点の根拠、などついて説明の仕方が非常にうまいと感じましたし、自分の理解も進んだ気がしました。

また本勉強会の本筋であるDeep learningの内容としては、CNN(畳み込みニューラルネットワーク)の内容を分かりやすく説明してもらえました。 これも青本などでカバーできていたものの、数式の波の前に倒れそうになっていたので、初心者向けの説明をしてもらえたおかげでその内容は理解することができたと思います。

www.tensorflow.org

TensorFlowのBeginnersチュートリアルについては、基本的には以下のチュートリアルに貼り付けてあるコードを集めてコピーするだけで、MNISTを使った手書き文字認識のパーセプトロンの実行が試せるようになっています。

MNIST For ML Beginners

ただし上記は(単層の)パーセプトロンによる解法なので、これを多層パーセプトロンを用いたモデルに拡張しましたが、チュートリアルをそのままに隠れ層を増やすだけではうまく学習できない問題があり、勉強会ではこの解決方法を教えてもらうことができました。 直感的には、ハイパーパラメータである学習係数かSGDを繰り返す回数に改善点があるかなと思いましたが、これらを減らす・増やすしてみても精度はほとんど変わらなかったため、あとはパラメータの初期値がzerosを用いて初期化されているので問題がありそうと思いましたが、実際これが学習を妨げる要素になっていました。 0初期化ではなく、tf.truncated_normal()を用いて正規分布でランダムに初期化することで、96%程度まで精度を上げることが可能になりました。 以下は単層から隠れ層を2つ増やした多層パーセプトロンを実装した例です。

gist.github.com

$ python MNIST_2_hidden.py 
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
0.2543
0.8396
0.9029
0.9011
0.9283
0.927
0.9309
0.9469
0.946
0.9523
0.9535
0.9393
0.9571
0.9571
0.9582
0.9605
0.9634
0.9608
0.9541
0.9646

今後エキスパート向けの勉強会が開催されたときには再度参加したいと思います。

numpyの多次元配列の比較時に発生するエラーについて

numpyの多次元配列を比較しようとすると、配列の各要素を比較した結果を配列として返すので、all()やany()を使ってANDやORでreduceした結果を取得できそうだが、以下のようなエラーに遭遇する。

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

多次元の場合はちゃんとメソッド使いましょう、という話。

また、差集合や積集合が欲しい場合、numpyの関数setdiff1dやintersect1dはその名の通り1次元にflattenしてしまうので、多次元配列の要素をそのまま結果に取り出したい場合はsetを使う。ただし、numpyのメソッドだけで完結しないので速度は出ないかもしれない。

$ python
Python 3.4.3 (default, Oct 24 2015, 14:51:44) 
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> a = np.array([1,2,3])
>>> b = np.array([2,3,4])
>>> c = np.array([1,3,4])
>>> a==b
array([False, False, False], dtype=bool)
>>> a==c
array([ True, False, False], dtype=bool)
>>> all(a==b)
False
>>> any(a==b)
False
>>> any(a==c)
True
>>> all(a==a)
True
>>> (a==b).all()
False
>>> (a==b).any()
False
>>> (a==c).any()
True
>>> (a==a).all()
True
>>> aa = np.array([[10,20,30],[20,30,40],[30,40,50]])
>>> bb = np.array([[20,30,40],[30,40,50],[40,50,60]])
>>> cc = np.array([[10,20,30],[30,40,50],[40,50,60]])
>>> aa
array([[10, 20, 30],
       [20, 30, 40],
       [30, 40, 50]])
>>> aa==bb
array([[False, False, False],
       [False, False, False],
       [False, False, False]], dtype=bool)
>>> all(aa==bb)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> (aa==bb).all()
False
>>> (aa==bb).any()
False
>>> (aa==cc).any()
True
>>> (aa==aa).any()
True
>>> np.setdiff1d(aa,bb)
array([10])
>>> np.setdiff1d(aa,cc)
array([], dtype=int64)
>>> np.intersect1d(aa,bb)
array([20, 30, 40, 50])
>>> np.intersect1d(aa,cc)
array([10, 20, 30, 40, 50])
>>> aas = set([tuple(x) for x in aa])
>>> bbs = set([tuple(x) for x in bb])
>>> ccs = set([tuple(x) for x in cc])
>>> np.array([x for x in aas-bbs])
array([[10, 20, 30]])
>>> np.array([x for x in aas-ccs])
array([[20, 30, 40]])
>>> np.array([x for x in aas-aas])
array([], dtype=float64)
>>> np.array([x for x in aas&bbs])
array([[20, 30, 40],
       [30, 40, 50]])
>>> np.array([x for x in aas&ccs])
array([[10, 20, 30],
       [30, 40, 50]])
>>> np.array([x for x in aas&aas])
array([[20, 30, 40],
       [10, 20, 30],
       [30, 40, 50]])

大きいサイズの配列でどの程度かかるのか試してみる。メモリに乗り切るなるべく大きいサイズの配列を準備して計測する。結果は違うが、numpyの関数と比較してみる。それぞれで差集合と積集合の処理速度が違うようだが、それほど差は無いように見える。

import numpy as np
import time

ai = [[x for y in range(2000)] for x in range(20000)]
bi = [[x+1 for y in range(2000)] for x in range(20000)]

al = np.array(ai)
bl = np.array(bi)

print(al)
print(bl)

start = time.time()
als = set([tuple(x) for x in al])
end = time.time()
print("{0:.5f} sec.".format(end-start))

bls = set([tuple(x) for x in bl])

start = time.time()
a_m_b = np.array([x for x in (als-bls)])
end = time.time()
print("{0:.5f} sec.".format(end-start))

start = time.time()
a_a_b = np.array([x for x in (als&bls)])
end = time.time()
print("{0:.5f} sec.".format(end-start))

print("# of setdiff  : {0}".format(len(a_m_b)))
print("# of intersect: {0}".format(len(a_a_b)))

start = time.time()
a_nm_b = np.setdiff1d(al,bl)
end = time.time()
print("{0:.5f} sec.".format(end-start))

start = time.time()
a_na_b = np.intersect1d(al,bl)
end = time.time()
print("{0:.5f} sec.".format(end-start))

print("# of setdiff1d  : {0}".format(len(a_nm_b)))
print("# of intersect1d: {0}".format(len(a_na_b)))
$ python numpy_test.py 
[[    0     0     0 ...,     0     0     0]
 [    1     1     1 ...,     1     1     1]
 [    2     2     2 ...,     2     2     2]
 ..., 
 [19997 19997 19997 ..., 19997 19997 19997]
 [19998 19998 19998 ..., 19998 19998 19998]
 [19999 19999 19999 ..., 19999 19999 19999]]
[[    1     1     1 ...,     1     1     1]
 [    2     2     2 ...,     2     2     2]
 [    3     3     3 ...,     3     3     3]
 ..., 
 [19998 19998 19998 ..., 19998 19998 19998]
 [19999 19999 19999 ..., 19999 19999 19999]
 [20000 20000 20000 ..., 20000 20000 20000]]
3.99010 sec.
1.13345 sec.
4.29675 sec.
# of setdiff  : 1
# of intersect: 19999
2.65021 sec.
1.83890 sec.
# of setdiff1d  : 1
# of intersect1d: 19999

Haskell Stateモナド(状態モナド遊びについての理解)

状態モナド遊び - あどけない話

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

HaskellのStateモナドは関数を扱うため、Maybeなどのモナドに比べてさらに理解が難しい。上記のサイトや参考書はそれをうまく説明しているが、それでも直感的にはまだ理解しづらいので、上記サイトの内容を以下のように紐解いてみるとより理解が深まると思う。

まず、Monadicモナドとcons0、runの定義は以下である。

instance Monad Monadic where
    Monadic f >>= g = Monadic $ \cnt0 -> let (result1, cnt1) = f cnt0
                                             Monadic h       = g result1
                                             (result2, cnt2) = h cnt1
                                         in (result2, cnt2)
    return x = Monadic $ \cnt -> (x, cnt)

cons0 x xs = Monadic $ \cnt -> (Cons x xs, cnt + 1)

run (Monadic func) initCnt = func initCnt

求めたい式は以下である。

run (cons0 "a" Nil >>= cons0 "b" >>= cons0 "c") 0

ここで、cons0 "a" Nilの部分を、その定義通りに置き換えてみる。

run (Monadic $ \cnt -> (Cons "a" Nil, cnt + 1) >>= cons0 "b" >> = cons0 "c") 0

最初の>>=を、その定義通りに置き換えてみる。

run (
    Monadic $ \cnt0 -> let (result1, cnt1) = \cnt -> (Cons "a" Nil, cnt + 1) cnt0
                            Monadic h      = cons0 "b" result1
                           (result2, cnt2) = h cnt1
                       in  (result2, cnt2)
    >>= cons0 "c"
) 0

cons0 "b" result1の部分も、その定義通りに置き換えてみる。

run (
    Monadic $ \cnt0 -> let (result1, cnt1) = \cnt -> (Cons "a" Nil, cnt + 1) cnt0
                            Monadic h      = Monadic $ \cnt -> (Cons "b" result1, cnt + 1)
                           (result2, cnt2) = h cnt1
                       in  (result2, cnt2)
    >>= cons0 "c"
) 0

ここで一度、置き換えた結果出現した関数の部分をfとおく。

run (
    Monadic $ f >>= cons0 "c"
) 0

次の>>=を、その定義通りに置き換えてみる。

run (
    Monadic $ \cnt0 -> let (result1, cnt1) = f cnt0
                            Monadic h      = cons0 "c" result1
                           (result2, cnt2) = h cnt1
                       in  (result2, cnt2)
) 0

cons0 "c" result1の部分も、その定義通りに置き換えてみる。

run (
    Monadic $ \cnt0 -> let (result1, cnt1) = f cnt0
                            Monadic h      = Monadic $ \cnt -> (Cons "c" result1, cnt + 1)
                           (result2, cnt2) = h cnt1
                       in  (result2, cnt2)
) 0

runを、その定義通りに置き換えてみる。

\cnt0 -> let (result1, cnt1) = f cnt0
              Monadic h      = Monadic $ \cnt -> (Cons "c" result1, cnt + 1)
             (result2, cnt2) = h cnt1
         in  (result2, cnt2)
0

つまり、runに与えた初期値0は、ラムダ式の引数cnt0に渡るので、それがそのままfに渡ることになる。これは、>>=をいくつ連結していても同じことである。fがネストされている状態になり、cnt0の値はfを通る度に規定された状態変化(この場合はcnt + 1)を伴いながら伝搬していく。

ここで再度fを見てみる。上記で見たとおり、cnt0は初期値0である。

Monadic $ \cnt0 -> let (result1, cnt1) = \cnt -> (Cons "a" Nil, cnt + 1) cnt0
                        Monadic h      = Monadic $ \cnt -> (Cons "b" result1, cnt + 1)
                       (result2, cnt2) = h cnt1
                   in  (result2, cnt2)

cnt0=0を代入して計算すると、fの結果は(Cons "b" (Cons "a" Nil), 2)となることが分かる。

ここで以下のincr1を使った例についても考えてみる。

incr1 = Monadic $ \cnt -> (999, cnt + 1)

cons1 x xs = do incr1
                return (Cons x xs)

run (cons1 "c" =<< cons1 "b" =<< cons1 "a" Nil) 0

doは構文糖衣で、>>=を使った式に書き換えられる。ただし、ここでincr1の結果を使っていないので、これは引数を取らないラムダ式になる。

cons1 x xs = incr1 >>= \_ -> return (Cons x xs)

incr1とreturnを、その定義通りに置き換える。

Monadic $ \cnt -> (999, cnt + 1) >>= \_ -> Monadic $ \cnt -> (Cons x xs, cnt)

>>=を、その定義通りに置き換える。すると、ラムダ式の引数に渡されたresult1の結果(ここでは999)は使われないことが良く分かる。

Monadic $ \cnt0 -> let (result1, cnt1) = \cnt -> (999, cnt + 1) cnt0
                       Monadic h       = \_ -> Monadic $ \cnt -> (Cons x xs, cnt) result1
                       (result2, cnt2) = h cnt1
                   in (result2, cnt2)

先ほどのcons0の場合と形は変われど、cons1の場合も同様に>>=の結合になるので、runに初期値として与えられたcnt0は、状態変化を伴いながら伝搬していくことになる。 incr2の例は、状態から取り出した値であるresult1の結果がラムダ式に渡ることになる。

ポイントになったのはcons1 x xsに現れたincr1だと思う。命令型脳で考えていると、このincr1のラムダ式が意味を成さない。また、構文糖衣であるdo式は命令型言語のように式を書ける便利構文である、と単純化してしまっていて、本来は>>=であることをスキップしてしまっている。そして、Haskellが純粋関数型であり、遅延評価を行い、>>=がbindと呼ばれる糊付け役である、ということを常に意識しながら読むことが大事である。

Cousera Machine Learning Course 修了

www.coursera.org

CouseraのMachine Learningコースを修了した。iPhoneのアプリもフルに活用していたので、修了までの期間は始めてから1ヶ月ちょっとくらい。このコースにはQuizとAssignmentがあって必要な点数を取らないと修了とみなされないのだが、Octaveを使った行列計算が必須になるため、それに慣れてなくて思わぬところで時間を食うことになったり。自信が無いところがあるとそこにばかり気を使ってなかなか正解に至らないのだが、結局間違っていたのはそんなところではなくいつも通りやっていればそれほど時間をかけずに解答できただろうという問題もいくつかあった。機械学習の基礎的な知識が身に付いたのはもちろん、行列計算における大事な要素を手が覚えるくらいしっかり理解することができた。内容はあくまでも実践に重きを置いているので、誤差逆伝搬法の導出や、SVMの双対問題、主成分分析の特異値分解、などの詳細に踏み入れることなく比較的カジュアルにコースを進めていける。

f:id:nishidy:20160412220053p:plain

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

ズンドコキヨシをErlangのgen_fsmを使って書いた

  • FSMが保持するStateDataを使わないバージョンを追加

ということで書いてみた。graceful exitがなかなか思うようにいかなかったし、コールバック関数も全部実装していないのでコンパイル時にWarningが出たまんま。Unicodeのformatは~ts、get_stateは文字列の比較で頑張ってたけど、ちゃんとatomが返ってきてた*1。そもそもget_state使うべきでは無い気がするけど、graceful exitがうまく行かなかったのでこのやり方としている。

-module(zundokokiyoshi).
-behaviour(gen_fsm).
-compile(export_all).

start_link(COMP) ->
    gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []).

init(COMP) ->
    {ok, zundoko, {[], COMP}}.

finish_song() ->
    gen_fsm:send_all_state_event(?MODULE, stop).

sing_phrase(Phrase) ->
    gen_fsm:send_event(?MODULE, {sing, Phrase}).

zundoko({sing, Phrase}, {SoFar, COMP}) ->
    case lists:sublist([Phrase|SoFar],5) of
        COMP ->
            io:format("ドコ~nキヨシ!~n"),
            {next_state, finish, {[], COMP}};
        ["ドコ"|_Tail] ->
            io:format("ドコ~n"),
            {next_state, zundoko, {[], COMP}};
        Else ->
            io:format("~ts~n",[Phrase]),
            {next_state, zundoko, {Else, COMP}}
    end.  

handle_event(stop, _StateName, StateData) ->
    {stop, normal, StateData}.

terminate(normal, _StateName, _StateData) ->
    ok.

take_phrase() ->
    Zd = ["ズン","ドコ"],
    Index = random:uniform(length(Zd)),
    lists:nth(Index,Zd).

start_song() ->
    start_link(["ズン","ズン","ズン","ズン","ドコ"]),
    random:seed( os:timestamp() ),
    sing_song().

sing_song() ->
    sing_phrase( take_phrase() ),
    timer:sleep(200),
    case sys:get_state(?MODULE) of
        {finish,_} -> finish_song();
        _ -> sing_song()
    end.
  • SoFarを使わずに全て状態遷移で実現(こちらの方がFSMを使う良い例ぽい)
-module(zundokokiyoshi).
-behaviour(gen_fsm).
-compile(export_all).

start_link(COMP) ->
    gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []).

init(COMP) ->
    {ok, zun1, {[], COMP}}.

finish_song() ->
    gen_fsm:send_all_state_event(?MODULE, stop).

sing_phrase(Phrase) ->
    io:format("~ts~n",[Phrase]),
    gen_fsm:send_event(?MODULE, {sing, Phrase}).

zun1({sing, Phrase}, {_, COMP}) ->
    case Phrase of
        "ズン" -> {next_state, zun2, {[], COMP}};
        "ドコ" -> {next_state, zun1, {[], COMP}}
    end.

zun2({sing, Phrase}, {_, COMP}) ->
    case Phrase of
        "ズン" -> {next_state, zun3, {[], COMP}};
        "ドコ" -> {next_state, zun1, {[], COMP}}
    end.

zun3({sing, Phrase}, {_, COMP}) ->
    case Phrase of
        "ズン" -> {next_state, zun4, {[], COMP}};
        "ドコ" -> {next_state, zun1, {[], COMP}}
    end.

zun4({sing, Phrase}, {_, COMP}) ->
    case Phrase of
        "ズン" -> {next_state, doko, {[], COMP}};
        "ドコ" -> {next_state, zun1, {[], COMP}}
    end.

doko({sing, Phrase}, {_, COMP}) ->
    case Phrase of
        "ズン" -> {next_state, doko, {[], COMP}};
        "ドコ" ->
            io:format("キヨシ!~n"),
            {next_state, finish, {[], COMP}}
    end.

handle_event(stop, _StateName, StateData) ->
    {stop, normal, StateData}.

terminate(normal, _StateName, _StateData) ->
    ok.

take_phrase() ->
    Zd = ["ズン","ドコ"],
    Index = random:uniform(length(Zd)),
    lists:nth(Index,Zd).

start_song() ->
    start_link(["ズン","ズン","ズン","ズン","ドコ"]),
    random:seed( os:timestamp() ),
    sing_song().

sing_song() ->
    sing_phrase( take_phrase() ),
    timer:sleep(200),
    case sys:get_state(?MODULE) of
        {finish,_} -> finish_song();
        _ -> sing_song()
    end.
$ erl
Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V7.1  (abort with ^G)
1> c(zundokokiyoshi).
zundokokiyoshi.erl:2: Warning: undefined callback function code_change/4 (behaviour 'gen_fsm')
zundokokiyoshi.erl:2: Warning: undefined callback function handle_info/3 (behaviour 'gen_fsm')
zundokokiyoshi.erl:2: Warning: undefined callback function handle_sync_event/4 (behaviour 'gen_fsm')
{ok,zundokokiyoshi}
2> zundokokiyoshi:start_song().
ドコ
ズン
ドコ
ドコ
ズン
ズン
ドコ
ズン
ズン
ズン
ドコ
ドコ
ズン
ズン
ドコ
ズン
ドコ
ドコ
ズン
ズン
ズン
ドコ
ズン
ズン
ズン
ズン
ドコ
キヨシ!
ok
3> 

*1:これ見る人が見るとStateはatomだと分かるのだろうか?センスの問題かもしれないけれど。 http://erlang.org/doc/man/sys.html#get_state-1

Pandas DataFrame について

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

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

PandasのDataFrameは行列形式のデータ構造で、indexやcolumnにkeyが指定できたり*1、indexやcolumnをまとめたものにnameを付けられたりする。DataFrameに対する集計関数や条件選択などはある程度直感的に操作できるが、Python標準のデータ構造から階層的なindexを持ったDataFrameを作成する場合や、階層的なindexを持ったDataFrameをどのように自由に変換できるのか、などが分かりづらかったので色々試してみた。

Nested dictを元にDataFrameを作成する場合、一番外側のkeyがcolumn、次に内側のkeyがindex、となってDataFrameを作成してくれる。ただし、tupleをkeyに使ったdictを元にDataFrameを作成する場合は、indexが無いことになるので、indexを指定しないとValueError: If using all scalar values, you must pass an indexというエラーになる。そのため、indexを指定するか、あるいは値をscalarではなくlistとするか、が必要になる(df5とdf6の違い)。*2

gist.github.com

$ python dataframe_run.py 
df1
      col1  col2  col3
idx1     0     4     8
idx2     1     5     9
idx3     2     6    10
idx4     3     7    11

df2
      col1  col2  col3
idx1     0     4     8
idx2     1     5     9
idx3     2     6    10
idx4     3     7    11

df1 == df2
      col1  col2  col3
idx1  True  True  True
idx2  True  True  True
idx3  True  True  True
idx4  True  True  True

df3(MultiIndex) #1
                      col1  col2  col3
idx1-1 idx2-1 idx3-1     0     1     2
idx1-2 idx2-2 idx3-2     3     4     5
       idx2-3 idx3-3     6     7     8
              idx3-4     9    10    11

df3(MultiIndex) #2: Append column
                      col1  col2  col3  col4
idx1-1 idx2-1 idx3-1     0     1     2   100
idx1-2 idx2-2 idx3-2     3     4     5   100
       idx2-3 idx3-3     6     7     8   100
              idx3-4     9    10    11   100

df3(MultiIndex) #3: Access to scalar value
10

df3(MultiIndex) #4: Access to scalar by ix property
11

df3(MultiIndex) #5: Access to record by ix with condtions
                      col1  col3
idx1-2 idx2-2 idx3-2     3     5
       idx2-3 idx3-3     6     8

df3(MultiIndex) #6: Access to record by ix with conds specified by isin method
                      col2  col3
idx1-1 idx2-1 idx3-1     1     2
idx1-2 idx2-3 idx3-3     7     8

df3(MultiIndex) #7: Pivot table with fillna method
col3  2   5   8   11
col2                
1      0  -1  -1  -1
4     -1   3  -1  -1
7     -1  -1   6  -1
10    -1  -1  -1   9

df3(MultiInex) #8: Stack method
idx1-1  idx2-1  idx3-1  col1      0
                        col2      1
                        col3      2
                        col4    100
idx1-2  idx2-2  idx3-2  col1      3
                        col2      4
                        col3      5
                        col4    100
        idx2-3  idx3-3  col1      6
                        col2      7
                        col3      8
                        col4    100
                idx3-4  col1      9
                        col2     10
                        col3     11
                        col4    100
dtype: int64

df3(MultiIndex) #9: Stack and unstack
                      col1  col2  col3  col4
idx1-1 idx2-1 idx3-1     0     1     2   100
idx1-2 idx2-2 idx3-2     3     4     5   100
       idx2-3 idx3-3     6     7     8   100
              idx3-4     9    10    11   100

df3(MultiIndex) #10: Unstack method
                col1                        col2                        col3  \
              idx3-1 idx3-2 idx3-3 idx3-4 idx3-1 idx3-2 idx3-3 idx3-4 idx3-1   
idx1-1 idx2-1      0    NaN    NaN    NaN      1    NaN    NaN    NaN      2   
idx1-2 idx2-2    NaN      3    NaN    NaN    NaN      4    NaN    NaN    NaN   
       idx2-3    NaN    NaN      6      9    NaN    NaN      7     10    NaN   

                                     col4                       
              idx3-2 idx3-3 idx3-4 idx3-1 idx3-2 idx3-3 idx3-4  
idx1-1 idx2-1    NaN    NaN    NaN    100    NaN    NaN    NaN  
idx1-2 idx2-2      5    NaN    NaN    NaN    100    NaN    NaN  
       idx2-3    NaN      8     11    NaN    NaN    100    100  

df3(MultiIndex) #11: Nested unstacks
         col1                                                                 \
       idx3-1               idx3-2               idx3-3               idx3-4   
       idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1   
idx1-1      0    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN   
idx1-2    NaN    NaN    NaN    NaN      3    NaN    NaN    NaN      6    NaN   

        ...     col4                                                          \
        ...   idx3-1 idx3-2               idx3-3               idx3-4          
        ...   idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2 idx2-3 idx2-1 idx2-2   
idx1-1  ...      NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN   
idx1-2  ...      NaN    NaN    100    NaN    NaN    NaN    100    NaN    NaN   

               
               
       idx2-3  
idx1-1    NaN  
idx1-2    100  

[2 rows x 48 columns]

df3(MultiIndex) #12: Swaplevel method
                      col1  col2  col3  col4
idx3-1 idx2-1 idx1-1     0     1     2   100
idx3-2 idx2-2 idx1-2     3     4     5   100
idx3-3 idx2-3 idx1-2     6     7     8   100
idx3-4 idx2-3 idx1-2     9    10    11   100

df4(Nested dict)
                             a1                              a2  \
b1  {'c3': 2, 'c1': 0, 'c2': 1}   {'c3': 11, 'c1': 9, 'c2': 10}   
b2  {'c3': 5, 'c1': 3, 'c2': 4}  {'c3': 14, 'c1': 12, 'c2': 13}   
b3  {'c3': 8, 'c1': 6, 'c2': 7}  {'c3': 17, 'c1': 15, 'c2': 16}   

                                a3  
b1  {'c3': 20, 'c1': 18, 'c2': 19}  
b2  {'c3': 23, 'c1': 21, 'c2': 22}  
b3  {'c3': 26, 'c1': 24, 'c2': 25}  

df5(Dict with tuple key) #1
 a1                         a2 ...      a3                                
 b1       b2       b3       b1 ...  b3  b1          b2          b3        
 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ...  c3  c1  c2  c3  c1  c2  c3  c1  c2  c3
  0  1  2  3  4  5  6  7  8  9 ...  17  18  19  20  21  22  23  24  25  26

[1 rows x 27 columns]

df5(Dict with tuple key) #2: Stack method
    a1        a2          a3        
    b1 b2 b3  b1  b2  b3  b1  b2  b3
 c1  0  3  6   9  12  15  18  21  24
 c2  1  4  7  10  13  16  19  22  25
 c3  2  5  8  11  14  17  20  23  26

df5(Dict with tuple key) #3: Nested stacks
  c1  b1  a1     0
          a2     9
          a3    18
      b2  a1     3
          a2    12
          a3    21
      b3  a1     6
          a2    15
          a3    24
  c2  b1  a1     1
          a2    10
          a3    19
      b2  a1     4
          a2    13
          a3    22
      b3  a1     7
          a2    16
          a3    25
  c3  b1  a1     2
          a2    11
          a3    20
      b2  a1     5
          a2    14
          a3    23
      b3  a1     8
          a2    17
          a3    26
dtype: int64

df5(Dict with tuple key) #4: Nested stacks and swaplevel
  a1  b1  c1     0
  a2  b1  c1     9
  a3  b1  c1    18
  a1  b2  c1     3
  a2  b2  c1    12
  a3  b2  c1    21
  a1  b3  c1     6
  a2  b3  c1    15
  a3  b3  c1    24
  a1  b1  c2     1
  a2  b1  c2    10
  a3  b1  c2    19
  a1  b2  c2     4
  a2  b2  c2    13
  a3  b2  c2    22
  a1  b3  c2     7
  a2  b3  c2    16
  a3  b3  c2    25
  a1  b1  c3     2
  a2  b1  c3    11
  a3  b1  c3    20
  a1  b2  c3     5
  a2  b2  c3    14
  a3  b2  c3    23
  a1  b3  c3     8
  a2  b3  c3    17
  a3  b3  c3    26
dtype: int64

df5(Dict with tuple key) #5: Nested stacks, swaplevel and unstack
        c1  c2  c3
 a1 b1   0   1   2
    b2   3   4   5
    b3   6   7   8
 a2 b1   9  10  11
    b2  12  13  14
    b3  15  16  17
 a3 b1  18  19  20
    b2  21  22  23
    b3  24  25  26

df5(Dict with tuple key) #6: Swaplevel with axis=1
 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ...  c3  c1  c2  c3  c1  c2  c3  c1  c2  c3
 b1 b1 b1 b2 b2 b2 b3 b3 b3 b1 ...  b3  b1  b1  b1  b2  b2  b2  b3  b3  b3
 a1 a1 a1 a1 a1 a1 a1 a1 a1 a2 ...  a2  a3  a3  a3  a3  a3  a3  a3  a3  a3
  0  1  2  3  4  5  6  7  8  9 ...  17  18  19  20  21  22  23  24  25  26

[1 rows x 27 columns]

df6(Dict with tuple key and list values)
  a1                         a2 ...      a3                                
  b1       b2       b3       b1 ...  b3  b1          b2          b3        
  c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 ...  c3  c1  c2  c3  c1  c2  c3  c1  c2  c3
0  0  1  2  3  4  5  6  7  8  9 ...  17  18  19  20  21  22  23  24  25  26

[1 rows x 27 columns]

*1:たぶんこちらをlabelと呼ぶのだと思うがnameとlabelが混同するのを避ける意味でkeyとしている

*2:data5には隠れindexがあるので、swaplevel()でのlevelの指定が直感より1つ大きい

PythonでPandasのPlotを試す

qiita.com

こちらの記事のオマージュです。環境は、Windows/Python3.4です。pandasなどはpipでインストールします。groupbyによる集計や、日本語表記などに対応しました。

gist.github.com

f:id:nishidy:20160309002816p:plain f:id:nishidy:20160309002820p:plain f:id:nishidy:20160309002826p:plain f:id:nishidy:20160309002832p:plain f:id:nishidy:20160309002842p:plain f:id:nishidy:20160309002851p:plain f:id:nishidy:20160309002859p:plain f:id:nishidy:20160309002903p:plain

補記:可変長引数・キーワード可変長引数

$ cat kwargs.py 
def func(x,a=1,z=0,zz=100,*args,**kwargs):
    print("x=",x)
    print("=====")
    print("a=",a)
    print("=====")
    print("args=",args) 
    print("=====")
    print("kwargs",kwargs) 
    print("=====")
    print("z=",z) 
    print("=====")
    print("zz=",zz) 

args = [10,20,30,40,50]
kargs = {"a":10,"b":20,"c":30,"d":40,"e":50}

print("-----func(args)-----")
func(args)
print("-----func(*args)-----")
func(*args)
print("-----func(1,**kargs)-----")
func(1,**kargs)
print("-----func(1,*kargs)-----")
func(1,*kargs)

$ python kwargs.py 
-----func(args)-----
x= [10, 20, 30, 40, 50]
=====
a= 1
=====
args= ()
=====
kwargs {}
=====
z= 0
=====
zz= 100
-----func(*args)-----
x= 10
=====
a= 20
=====
args= (50,)
=====
kwargs {}
=====
z= 30
=====
zz= 40
-----func(1,**kargs)-----
x= 1
=====
a= 10
=====
args= ()
=====
kwargs {'d': 40, 'c': 30, 'e': 50, 'b': 20}
=====
z= 0
=====
zz= 100
-----func(1,*kargs)-----
x= 1
=====
a= e
=====
args= ('a', 'd')
=====
kwargs {}
=====
z= c
=====
zz= b

実引数の辞書に*を付けて関数に渡すと、辞書のキーが仮引数に渡される。仮引数のデフォルト引数への割り当ては、仮引数の宣言順序とは関係なく実施される。以下のように、実行するたびに割り当てが変わる。

-----func(1,*kargs)-----
x= 1
=====
a= b
=====
args= ('c', 'd')
=====
kwargs {}
=====
z= e
=====
zz= a

splitlines()

splitlines()は最後の空白要素を削除するが、split("\n")は削除しない。Trueを与えると改行を付けたまま分割して返す。

>>> a="aaa\nbbb\nccc\n"
>>> a.splitlines()
['aaa', 'bbb', 'ccc']
>>> a.split("\n")
['aaa', 'bbb', 'ccc', '']
>>> a="aaa\nbbb\nccc\n\n\n\n"
>>> a.splitlines()
['aaa', 'bbb', 'ccc', '', '', '']
>>> a.split("\n")
['aaa', 'bbb', 'ccc', '', '', '', '']
>>> a="aaa\nbbb\nccc"
>>> a.splitlines()
['aaa', 'bbb', 'ccc']
>>> a.split("\n")
['aaa', 'bbb', 'ccc']
>>> a.splitlines(True)
['aaa\n', 'bbb\n', 'ccc']