Deep Learning勉強会に参加しました
Deep learningの勉強会に参加して、TensorFlowのBeginnersチュートリアルと、多層パーセプトロンの実装をPythonでやりました。基本的なところは青本やCouseraなどでカバーしてありましたので導入部分は特に問題無く進めましたが、ニューラルネットワークでも理解の難しい誤差逆伝播法の導出や確率的勾配降下法の利点の根拠、などついて説明の仕方が非常にうまいと感じましたし、自分の理解も進んだ気がしました。
また本勉強会の本筋であるDeep learningの内容としては、CNN(畳み込みニューラルネットワーク)の内容を分かりやすく説明してもらえました。 これも青本などでカバーできていたものの、数式の波の前に倒れそうになっていたので、初心者向けの説明をしてもらえたおかげでその内容は理解することができたと思います。
TensorFlowのBeginnersチュートリアルについては、基本的には以下のチュートリアルに貼り付けてあるコードを集めてコピーするだけで、MNISTを使った手書き文字認識のパーセプトロンの実行が試せるようになっています。
ただし上記は(単層の)パーセプトロンによる解法なので、これを多層パーセプトロンを用いたモデルに拡張しましたが、チュートリアルをそのままに隠れ層を増やすだけではうまく学習できない問題があり、勉強会ではこの解決方法を教えてもらうことができました。 直感的には、ハイパーパラメータである学習係数かSGDを繰り返す回数に改善点があるかなと思いましたが、これらを減らす・増やすしてみても精度はほとんど変わらなかったため、あとはパラメータの初期値がzerosを用いて初期化されているので問題がありそうと思いましたが、実際これが学習を妨げる要素になっていました。 0初期化ではなく、tf.truncated_normal()を用いて正規分布でランダムに初期化することで、96%程度まで精度を上げることが可能になりました。 以下は単層から隠れ層を2つ増やした多層パーセプトロンを実装した例です。
$ 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モナド(状態モナド遊びについての理解)
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (70件) を見る
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 修了
CouseraのMachine Learningコースを修了した。iPhoneのアプリもフルに活用していたので、修了までの期間は始めてから1ヶ月ちょっとくらい。このコースにはQuizとAssignmentがあって必要な点数を取らないと修了とみなされないのだが、Octaveを使った行列計算が必須になるため、それに慣れてなくて思わぬところで時間を食うことになったり。自信が無いところがあるとそこにばかり気を使ってなかなか正解に至らないのだが、結局間違っていたのはそんなところではなくいつも通りやっていればそれほど時間をかけずに解答できただろうという問題もいくつかあった。機械学習の基礎的な知識が身に付いたのはもちろん、行列計算における大事な要素を手が覚えるくらいしっかり理解することができた。内容はあくまでも実践に重きを置いているので、誤差逆伝搬法の導出や、SVMの双対問題、主成分分析の特異値分解、などの詳細に踏み入れることなく比較的カジュアルにコースを進めていける。
- 作者: 岡谷貴之
- 出版社/メーカー: 講談社
- 発売日: 2015/04/08
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (9件) を見る
ズンドコキヨシをErlangのgen_fsmを使って書いた
- FSMが保持するStateDataを使わないバージョンを追加
そういえばズンドコキヨシは gen_fsm のイイサンプルになるとおもうので、誰か書きませんか。
— V (@voluntas) March 18, 2016
ということで書いてみた。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を使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (12件) を見る
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
$ 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]
PythonでPandasのPlotを試す
こちらの記事のオマージュです。環境は、Windows/Python3.4です。pandasなどはpipでインストールします。groupbyによる集計や、日本語表記などに対応しました。
補記:可変長引数・キーワード可変長引数
$ 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']