Dictionary
Dictionaryの基本 いわゆるKey-Valueのことを\"Mapping\"と呼ぶ。 PythonのDictionaryはハッシュで実現されている。 キーにはハッシュ可能オブジェクトを指定可能。ハッシュ可能=更新不可、という意味ではない。 例えばオブジェクトのインスタンスのように中身が変わっても枠が変わらなければOK。 rgbs = { (255,0,0): \'red\', (0,255,0): \'green\', (0,0,255): \'blue\' キーは重複不可。同じキーを登録すると前のKeyValueが上書きされる。 kv = { \'hoge\': 100, \'fuga\': 200 } print(kv) # {\'hoge\': 100, \'fuga\': 200} kv[\'hoge\'] = 300 print(kv) # {\'hoge\': 300, \'fuga\': 200} DictionaryのCRUD d = {\'hoge\':100,\'fuga\':200} # Create print(d[\'hoge\']) # Read d[\'hoge\'] = 300 # Update del d[\'hoge\'] # Delete d2 = dict([(1,\'hoge\'),(2,\'fuga\')]) print(d2) # {1: \'hoge\', 2: \'fuga\'} d3 = dict(hoge1=\'hoge1\',hoge2=\'hoge2\',hoge3=\'hoge3\') print(d3) # {\'hoge1\': \'hoge1\', \'hoge2\': \'hoge2\', \'hoge3\': \'hoge3\'} 辞書内包 List,Tupleと同様に内包式を書ける。 s = [\"Hoge1\",\"Hoge2\",\"Hoge3\"] k1 = {} for v in s: k1[v] = v.lower() k2 ={v.lower() for v in s} Operator Dictionaryに対する演算。 d = {\"hoge\":100} print(\'hoge\' in d) # True print(\'fuga\' not in d) # True print(len(d)) # 1 iteration中に別のkeyへのアクセスは不可 こういうことは避けるべきだけれども、実際にやるとRuntimeエラー。 d = {\'hoge1\':100,\'hoge2\':200,\'hoge3\':300} for key in d: d[\'hoge4\'] = 500 # RuntimeError: dictionary changed size during iteration ViewObject キーの集合、値の集合を参照するためのデータ構造。 Viewというからには、Dictionary内に含まれる実体を参照している。 ViewObjectは集合演算をサポートしている。 d = {\'hoge1\':100,\'hoge2\':200,\'hoge3\':300} view1 = d.keys() for key in view1: print(key) # hoge1 hoge2 hoge3 del d[\'hoge2\'] for key in view1: print(key) # hoge1 hoge3 view_x = view1 - [\'hoge1\',\'hoge2\'] print(view_x) # {\'hoge3\'} view2 = d.values() for value in view2: print(value) # 100 200 300
String
Stringの基本 他の型をStringに変換する。 a = 100 b = 3.14 c = str(a) + \'/\' + str(b) print(c) # 100/3.14 スライスで参照できる。 a = \"hogehoge\" print(a[3:5]) # eh Stringのmethod達 Stringは更新不能。method達は自分自身を破壊しない。 戻り値として新しいStringオブジェクトが戻るのみ。 大文字小文字,数値アルファベットの判定はUnicode標準に従う。 # coding: utf-8 # Your code here! print(\"Hogehoge\".capitalize()) # Hogehoge print(\"AiUeO\".casefold()) # aiueo print(\"hoge\".center(10,\'-\')) # ---hoge--- print(\"hogehoge\".count(\'h\')) # 2 print(\"hogehoge\".encode()) # b\'hogehoge\' print(\"hogehoge\".endswith(\'hoge\')) # True print(\"hoge\".find(\"ge\")) # 2 print(\"{0} is {1}\".format(\"hoge\",\"fuga\")) # hoge is fuga print(\"hogehoge\".index(\"geh\")) print(\"hoge\".isalnum()) # True print(\"1234\".isalnum()) # True print(\"()\".isalnum()) # False print(\"hoge\".isalpha()) # True print(\"1234\".isalpha()) # False print(\"1234\".isdecimal()) # True print(\"12ab\".isdecimal()) # False print(\"hoge\".isidentifier()) # True print(\"3ab\".isidentifier()) # Flase print(\"Hoge\".islower()) # False print(\"hoge\".islower()) # True print(\"1234\".isnumeric()) # True print(\"12ab\".isnumeric()) # False print(\"t\".isprintable()) # False print(\"hoge \".isspace()) # False print(\" \".isspace()) # True print(\"hoge\".istitle()) # False print(\"This Is My Hoge\".istitle()) # True print(\"--\".join([\'1\',\'2\',\'3\',\'4\'])) # 1--2--3--4 print(\"\".join([\'spam\',\'ham\',\'egg\'])) # spamhamegg print(\"hoge\".ljust(10,\'*\')) # hoge****** print(\"This is hoge\".lower()) # this is hoge print(\" hoge \".lstrip()) # hoge print(\"hoge1 hoge2 hoge3\".replace(\"hoge1\",\"fuga1\")) # fuga1 hoge2 hoge3 print(\"12345678\".rfind(\'6\')) # 5 print(\"12345678\".rindex(\'6\')) # 5 print(\"hoge\".rjust(10,\'*\')) # ******hoge print(\"hoge/fuga\".rpartition(\'/\')) # (\'hoge\', \'/\', \'fuga\') print(\"hoge*fuga\".rpartition(\'/\')) # (\'\', \'\', \'hoge*fuga\') print(\"hoge fuga hoge\".rsplit()) # [\'hoge\', \'fuga\', \'hoge\'] print(\"hoge \".rstrip()) # hoge print(\"hoget\".rstrip()) # hoge print(\"hoge hoge hoge\".split()) # [\'hoge\', \'hoge\', \'hoge\'] print(\"hoge1rhoge2nhoge3rn\".splitlines()) # [\'hoge1\', \'hoge2\', \'hoge3\'] print(\"hoge1rhoge2nhoge3rn\".splitlines(keepends=True)) # [\'hoge1r\', \'hoge2n\', \'hoge3rn\'] print(\"aiueokakikukeko\".startswith(\"aiueo\")) # True print(\" hoge \".strip()) # hoge print(\"AiUeO\".swapcase()) # aIuEo print(\"hoge hoge hoge\".title()) # Hoge Hoge Hoge print(\"hogehgoehgoe\".upper()) # HOGEHGOEHGOE print(\"hoge\".zfill(10)) # 000000hoge print(\"-foo\".zfill(10)) # -000000foo
Collection…Sequence,Slice,List,Tupple
Sequence,Slice 初めてPythonを触ると地味に取っつきにくい構文が現れるけども。 序数によるアクセスの他、範囲を指定してリストを得られる。 負の序数を指定すると後ろから数える。負のスライスも可 スライスの開始位置、終了位置はSequenceの前の方->後の方の順に書く。 長さがゼロ、またはマイナスの場合、空のリストを返す。 L = [0,1,2,3,4] a = L[1:3] print(a) # [1,2] b = L[-1:-3] print(b) # [2,3] k = K[3:1] print(k) # [] 増分値を指定することもできる。 L = [0,1,2,3,4] c = L[0:5:2] print(c) # [0,2] スライス自体もオブジェクト L = [0,1,2,3,4] s = slice(0,3) d = L[s] print(d) # [0,1,2] s2 = slice(0,None,1) print(L[s2]) # [0,1,2,3,4] スライスを構成する3要素は省略できる。 増分値に負を与えると逆順になるのを利用して要素を反転できる。 L = [0,1,2,3,4] print(L[::]) # = L[0:4:1] = [0,1,2,3,4] print(L[:]) # = L[0:4:1] = [0,1,2,3,4] print(L[::2]) # = L[0:4:2] = [0,2] print(L[::-1]) # = [4,3,2,1,0] スライスを指定して代入できる。 L = [0,1,2,3,4] L[1,4] = [\'hoge\',\'fuga\'] print(L) # [0,\'hoge\',\'fuga\',4] スライスを指定して削除できる。リストから削除するだけで要素自体を破棄しない。 L = [0,1,2,3,4] del L[0,2] print(L) # [3,4] 算術演算子、比較演算子、メンバーシップ演算子が用意されている。 Q = [1,2] * 3 print(Q) # [1,2,1,2,1,2] R1 = [1,2] R2 = [3,4] RX = R1 < R2 print(RX) # True P = [0,1,2,3,4] PX = 7 in P print(PX) # False PXX = 15 not in P print(PXX) # True Sequenceの長さはlen()で取得。 L = [0,1,2,3,4] print(len(L)) # 5 List リストの基本的な使い方は以下。 L1 = [0,1,2,3,4,5] L2 = [] for E in L1: L2.append(E) print(L2) # [0,1,2,3,4,5] 上記は以下のように1回で書ける。リスト内包とか言うらしい。 L1 = [0,1,2,3,4,5] L2 = [value for value in L1] print(L2) # [0,1,2,3,4,5] リスト内包には条件をつけられる。例えば偶数だけを集めるのは以下。 L1 = [0,1,2,3,4,5] L2 = [value for value in L1 if value % 2 == 0] # [0,2,4] iteratableを分解できる。 L = [(\'a\',1),(\'b\',2),(\'c\',3)] L1 = [c*i for c,i in L] print(L1) # [\'a\', \'bb\', \'ccc\'] iteratableの分解時に要素をリストで受けられる。 以下、タプルの第2要素以降をリストで受けて総和を求めて第1要素に掛けている。 L = [(1,2),(3,4,5),(6,7,8,9)] L1 = [car*sum(cdr) for car,*cdr in L] print(L1) # [2, 27, 144] リスト内包を入れ子にすることもできる。 years = [2013,2014] months = [1,2,3,4,5,6,7,8,9,10,11,12] dest1 = [] for year in years: for month in months: dest1.append((year,month)) print(dest1) dest2 = [(year,month) for year in years for month in months] print(dest2) Listオブジェクトの代表的なメソッド達。 L1 = [0,1,2,3,4] L1.append(5) print(L1) # [0,1,2,3,4,5] L2 = L1.copy() print(id(L1)) # 140415098962824 print(id(L2)) # 140415098984840 L2.clear() print(L2) # [] print(L1.count(2)) # 1 L3 = [\'a\',\'b\',\'c\'] L1.extend(L3) print(L1) # [0, 1, 2, 3, 4, 5, \'a\', \'b\', \'c\'] L4 = L1.index(0) print(L4) # 0 L1.insert(4,\'hoge\') print(L1) # [0, 1, 2, 3, \'hoge\', 4, 5, \'a\', \'b\', \'c\'] p = L1.pop(3) print(p) # 3 print(L1) # [0, 1, 2, \'hoge\', 4, 5, \'a\', \'b\', \'c\'] L1.remove(4) print(L1) # [0, 1, 2, \'hoge\', 5, \'a\', \'b\', \'c\'] L1.reverse() print(L1) # [\'c\', \'b\', \'a\', 5, \'hoge\', 2, 1, 0] Tuple 更新不能なリスト。要素を列挙するだけでTupleを作れる。更新できない以外はListと変わらない感じ。 \"更新できない\"という特徴があるため、辞書のキーとして使うことができる。 リストは要素としてそれぞれ独立した値が設定されることを想定。 タプルは全体で1つの要素になることを想定。タプルは「複数の要素から構成される値」。 \"更新できない\"という特徴は、メモリ割り当て時に動的確保を想定しなくて良いため、 リストよりもタプルの方がメモリ使用量を削減できるし、GCの負荷軽減に寄与する。 t1 = (1,2,3) print(t1) # (1,2,3) t2 = 3,4,5 print(t2) # (3, 4, 5) t3 = 1, print(t3) # (1,)
NP困難な分類問題を代理損失の最小化に帰着させる話
[mathjax] 機械学習の分類問題の中心にある決定境界の決定方法について かなり要領を得た説明を聞いて理解が2段階くらい先に進んだのでまとめてみます。 データが与えられただけの状態から決定境界を決める問題はNP困難ですが 別の問題に帰着させることで解を得る、というのが基本的なアイデアです。 分類の正誤とその度合いを一度に表現できるマージンを定義し、 マージンを使って与えた代理損失を最小にする問題にします。 分類問題を代理損失の最小化に帰着させるのですね。 任意の決定境界を決める問題は線形分類であってもNP困難 2値のラベルA,B付きの2次のデータポイントが与えられたとして、 入力空間(X1-X2)におけるA,Bの分離境界(decision boundary)を求める問題が\"分類\"。 直線で分離境界を書くとして、それを求めるための最も愚直な方法は以下のようなもの。 その分離境界によりデータポイントが正しく分類出来ていれば1をカウントする。 正しく分類出来ていなければ0をカウントする。 全データポイントにおける正答率を求める。 正答率が最大になるような決定境界を求める。 そもそも分離境界は直線でなくても良いのに、あえて直線ですよ、と仮定をしたとしても、 分離境界が完全に自由で、全データに対して正答率を求めないといけない。 上記の問題の計算量は(mathcal{O}(n^3))では済まない。NP困難。 計算できるように改善 分離境界の初期値を決めて、そこから正答率が良くなる方向に少しずつずらしていこうにも、 \"正しく分類されている\"=1,\"分類されていない\" =0 は、少しの変化に影響されない。 正しい=1/正しくない=0、という損失とは別の損失を作って、 その損失を使った別の問題を解くことを、上記を問題を解くことに帰着させる。 決定境界の変化に敏感な損失を作る サンプルサイズが十分大きいとき、1.で作った損失による学習結果が、「正しく分類」「正しくない分類」という損失の学習結果と一致する margin 線形分類において、分離境界(f(x_1,x_2,cdots,x_n)=w_0+w_1x_1+w_2x_2+cdots+w_nx_n)とする。 この多項式と分離の正誤、正誤の度合いは以下のように決まる。 分類の正誤は(f(x_1,x_2,cdots,x_n))の符号が決める。 分類の正誤の度合いは(f(x_1,x_2,cdots,x_n))の絶対値が決める。 (f(x_1,x_2,cdots,x_n))が正の場合、決定境界から近い場所にあるデータポイントは もしかしたら誤って分類してしまったものかもしれない。 決定境界から遠い場所にあるデータポイントは近いものよりは正しく分類しているかもしれない。 同様に(f(x_1,x_2,cdots,x_n))が負の場合、決定境界から近い場所にあるデータポイントは もしかしたら正しい分類かもしれないし、遠いデータポイントはより近いものより間違っている可能性が高い。 この事実を1つの式で表す。 データポイントには出力ラベル(y=pm 1)が付いているものとする。 判別関数を(f(x_1,x_2,cdots,x_n))とする。決定境界は(f(x_1,x_2,cdots,x_n)=0) begin{eqnarray} m = yf(x_1,x_2,cdots,x_n) end{eqnarray} ラベル1を-1と分類した場合(f(x_1,x_2,cdots,x_n)<0)。 同様に-1を1と分類した場合も(f(x_1,x_2,cdots,x_n)<0)。 つまり、誤分類したときにラベルと判別関数の符号が異なり(m0)となる。 ということで、(m)をマージン(margin)と呼ぶ。 サポートベクトル marginが最大になるように各データポイントの中にある決定境界を決めていく。 全てのデータポイントについて距離を計算する必要はなく、決定境界と距離が一番近いデータポイントとの 距離を最大化すれば良いらしい。(それが一番近いかどうかはいずれにせよ距離を求める必要がありそうだけど..) marginが最大になるように決めた決定境界と距離が最も近いデータポイントをサポートベクトルと言うらしい。 マージンを使った損失 最初に戻ると、決定境界の変化に敏感な損失を作ることが目的だった。 マージンが正の方向に大きいほど正しい分類であると言えるし、 マージンが負の方向に大きいほど誤った分類であると言えるけれども、 正しい度合いが高ければ小、誤りの度合いが高ければ大、となる損失を考えることで、 誤った方向に決定境界を修正すれば敏感に値が上昇する損失にすることができる。 (正しい方向に移動しても変わらない。) 横軸にマージン、縦軸に損失を取ったとして、以下のような損失(h(m))を考える。 もちろん、(m = yf(x_1,x_2,cdots,x_n))。 (m=1)より大きいマージンについては損失が0。(m=1)より小さいマージンについて線形に増加する。 (m=1)を境にヒンジの形をしているのでhinge損失という名前が付いてる。 begin{eqnarray} h(m) = max(0,1-m) end{eqnarray}
Reference,unpack,同時代入,変数の削除,3項演算子
Reference 変数はオブジェクトの参照。ということなので、オブジェクトのidを確認してみる。 hoge = [1,2,3,4] fuga = hoge print(hoge) # [1,2,3,4] print(fuga) # [1,2,3,4] print(id(hoge)) # 4359573320 print(id(fuga)) # 4359573320 hoge.append(5) print(hoge) # [1,2,3,4,5] print(fuga) # [1,2,3,4,5] 代入したときにどうなるか。新しい箱が作られてそちらを指すようになる。 foo = 1 bar = foo print(foo) # 1 print(bar) # 1 print(id(foo)) # 4359573320 print(id(bar)) # 4359573320 foo = 2 print(foo) # 2 print(bar) # 1 print(id(foo)) # 4476302432 print(id(bar)) # 4359573320 Unpack,同時代入 代入の右辺にiterableを置き、左辺に複数の変数を置くと、 左辺の各変数にiterableの各要素が代入される。 数が合わないとエラー。 foo = [1,2,3,4,5] a1,a2,a3,a4,a5 = foo print(a1) # 1 print(a2) # 2 print(a3) # 3 print(a4) # 4 print(a5) # 5 左辺にリストを置けるが、Unpackの評価においてリストも分解される。 c1,[c2,c3],c4 = [1,[2,3],4] print(c1) # 1 print(c2) # 2 print(c3) # 3 print(c4) # 4 リストでなくても同時代入は可能。 d1,d2 = 100,200 print(d1) # 100 print(d2) # 200 左辺と右辺の個数が合わないケース。通常は下記の通りエラーになる。 ただし左辺に*があるとよしなにやってくれる。 bar = [1,2,3] b1,b2 = bar # ValueError: too many values to unpack (expected 2) e1,*e2 = bar print(e1) # 1 print(e2) # [2,3] *f1,f2 = bar print(f1) # [1,2] print(f2) # 3 オブジェクトの削除 オブジェクトは明示的に削除できる。 foobar = 1024 print(id(foobar)) # 4348092368 del(foobar) print(id(foobar)) # NameError: name \'foobar\' is not defined 3項演算子 ?ではなくif..elseで書く。 a if b else c の場合、bが真ならa,偽ならc。 片方だけ評価される(short circuit)。 a = 100 b = 200 y = True x = a if y else b z = a if not y else b print(x) # 100 print(z) # 200
PythonでUpcast/Downcast出来ない件と多態性のサンプル
継承・カプセル化・多態性 オブジェクト指向の必須要素はこれ。継承、カプセル化・多態性。 異種リストを書けば全部試せるので試してみる。 多態性実現のためにオブジェクトのUpcast/Downcastを調べようとしたら存在しない!。 結論として、PythonにおいてはオブジェクトのUpcast/Downcastは出来ない。 動的型付けなんで、Upcastは勝手に上位クラスだと思って触れば良いのだけど、 形だけでも上位クラスにキャストしておいた方が可読性は上がると思うので残念。 abstractメソッドを定義するだけでパッケージを要求するとか切ない。 from abc import ABCMeta, abstractmethod # 抽象基底クラス class Base: __attr = 100 @abstractmethod def hoge(self): pass class Derived1(Base): __attr = 200 def hoge(self): print(self.__attr) class Derived2(Base): __attr = 300 def hoge(self): print(self.__attr) vals = [Derived1(), Derived2()] for val in vals: val.hoge() なお、Downcastについては、以下のようにしている例がある。 class SubDerived2(Derived1): __attr = 500 def hoge(self): print(self.__attr) def myfunc(self): print(\"myfunc is called\") derived = Derived2() derived.__class__ = SubDerived2 derived.myfunc() 確かに、Upcastは動的型付けの範囲内だが、実現可能でDowncastは何らかの指示が必要で、 必要な方にだけ手段が用意されているのは合理的な気もする。 関数 defで定義する。最初の行に書いた文字列リテラルはdocstring。PHPDoc的なやつだろうか。 関数のスコープからローカル->グローバル->ビルトインの順にシンボルを参照できる。 関数スコープから外のスコープのシンボルは書けない。(global指定して初めて書ける)。 フィボナッチ数列。 def fib(n): \"\"\"Print a Fibonacci series up to n.\"\"\" a,b = 0,1 while a < n: print(a,end=' ') a,b=b,a+b print() fib(100) # 0 1 1 2 3 5 8 13 21 34 55 89 キーワード引数も使える。 def myfunc(hoge,fuga=1): print(hoge) print(fuga)
Laravel Accessor/Mutatorを使って透過的にフィールドを暗号化/復号するサンプル
DBに入っているデータを決まった書式/形式に変換して表示したり、 逆に逆変換して保存する例は多いかと思います。 変換,逆変換の実装方法は以下みたいな感じかと..。 いずれも変換/逆変換の存在を忘れて仕様が抜けたり、 同じことを他でも書くコードクローンが発生する原因になる。 Controllerにダラダラと変換/逆変換を書く EloquentにオレオレSetter/Getterを書く Accessor/Mutatorを使うことで上記の原因を無くすことができます。 Accessor/Mutator Eloquentのメンバ変数(つまり、テーブルのフィールド)へのアクセスを ある規則をもってEloquentに定義したSetter/Getterを仲介するように強制できます。 [clink implicit=\"false\" url=\"https://laravel.com/docs/5.8/eloquent-mutators\" imgurl=\"http://laravel.jp/assets/img/logo-head.png\" title=\"Eloquent: Mutators Introduction\" excerpt=\"Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model.\"] Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model. In addition to custom accessors and mutators, Eloquent can also automatically cast date fields to Carbon instances or even cast text fields to JSON. 暗号化/復号 サンプル 標題の通りですが、Accessor/Mutatorを使ってフィールドを暗号化/復号してみます。 Cryptファサードを使ってAES-256-CBCの暗号化/復号を行う対です。 secretvalueというフィールドにAES256CBCで暗号化して書き込み、復号して読み込みます。 class User extends Authenticatable { use Notifiable; /** * Get the user\'s secretvalue. * * @param string $value * @return string */ public function getSecretvalueAttribute($value) { return decrypt($value); } /** * Set the user\'s secretvalue. * * @param string $value * @return string */ public function setSecretvalueAttribute($value) { $this->attributes[\'secretvalue\'] = encrypt($value); $this->save(); } } 透過的に呼び出す例です。 Userのsecretvalueフィールドに\"hogehoge\"という値を設定しています。 hogehogeという平文を暗号化してsecretvalueフィールドに書き込む処理は使う側には見えません。 Route::get(\'/sample/setvalue\',function(){ AppUser::find(1)->secretvalue = \'hogehoge\'; }); Userのsecretvalueフィールドを読み込んで出力しています。 暗号化済み文字列を復号する処理は使う側には見えません。 Route::get(\'/sample/getvalue\',function(){ echo AppUser::find(1)->secretvalue; }); より広い用途で使える 暗号化/復号はかなり直球な使い方ですが、ビジネスロジック内の定型処理など 積極的に使おうとするとAccessor/Mutatorに掃き出せるケースがありそうです。
インデントとブロック,比較と、比較の連接,辞書オブジェクト,リストとタプル,if … elif … else,for,while,iteratableオブジェクト
他言語からの流入組が最小工数でPythonを学ぶシリーズです。 Python文法詳解を読みながらアウトプットしていきます。 [arst_toc tag=\"h4\"] いくら何でも公式を上から順番に読むのは時間がかかり過ぎるし、 550ページとかある本なんて読みたくないので、なるべく薄くて枠だけ書いてある本が良いなと。 Python文法詳解posted with amazlet at 19.05.25石本 敦夫 オライリージャパン 売り上げランキング: 432,601Amazon.co.jpで詳細を見る インデントとブロック ブロックはbeginや { などの予約語ではなく、インデントを使う。 公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/2.0/ref/indentation.html\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" excerpt=\"Leading whitespace (spaces and tabs) at the beginning of a logical line is used to compute the indentation level of the line, which in turn is used to determine the grouping of statements.\"] 公式に面白い例が載ってた。 見た目上、インデントが無茶苦茶だけれども文法的には合理性がある例。 要するにインデントとインデント解除のconsistencyがあればOK。 def perm(l): # Compute the list of all permutations of l if len(l) <= 1: return [l] r = [] for i in range(len(l)): s = l[:i] + l[i+1:] p = perm(s) for x in p: r.append(l[i:i+1] + x) return r コーデイング規約によると、お作法としてスペースの個数は4個。だそうだ。 タブではなくスペースでインデントするなんて信じられない...。 エディタにタブ=スペース4個の設定を追加しないと。 言語仕様上の制約はインデントのconsistencyだけなので、 実際のインデント数はプロジェクトによるとのこと。ほうほう。 ただ、スペースとタブの混在禁止はこの際ハッキリさせましょう、という流れ。 比較と、比較の連接 比較演算子について公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/library/stdtypes.html#comparisons\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" title=\"比較\" excerpt=\"Python には 8 種の比較演算があります。比較演算の優先順位は全て同じです (ブール演算より高い優先順位です)。比較は任意に連鎖できます; \"] 比較演算子を連接できる。PHPとRubyには無い感じ。 a=50 # 通常 if (0 < a) and (a < 100): print("OK") else: print("NG") # 連接 if 0 < a < 100: print("OK") else: print("NG") 辞書オブジェクト KeyValue型。本家は以下。Keyは変更不能で一意でなければならない、と書いてある。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"5.5. 辞書型 (dictionary)\" excerpt=\"もう一つ、有用な型が Python に組み込まれています。それは 辞書 (dictionary)です。辞書は他の言語にも 連想記憶 (associated memory) や 連想配列 (associative array) という名前で存在することがあります。ある範囲の数でインデクス化されているシーケンスと異なり、辞書は キー (key) でインデクス化されています。\"] a = {1:\'one\',2:\'two\'} print(a) # {1:\'one\',2:\'two\'} # 代入 print(a[1]) # \'one\' a[1] = \'壱\' print(a[1]) # \'壱\' # 削除 del a[1] print(a[1]) # エラー キーを文字列にして連想配列みたくできる。 連想配列のキーにできるかどうかは、その型が更新可能であるかどうか? b = {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} print(b) # {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} リストとタプル 似て非なる配列的データ構造、リストとタプル。 リストの方は更新可能。タプルは更新不能。 任意の型を要素としてとれるので、配列ではなくリスト。 L1 = [1,2,3,4,5] print(L1) # [1,2,3,4,5] L2 = (1,2,3,4,5) print(L2) # (1,2,3,4,5) L1[0] = \'hoge\' print(L1) # [\'hoge\', 2, 3, 4, 5] L2[0] = 100 # TypeError: \'tuple\' object does not support item assignment 序数を使ってシーケンシャルアクセスできる。 ary = [1,5,3,4,9] print(ary[3]) # 4 str = \'abcdef\' print(str[1]) # b 負の序数を使うと後ろから数える。 ary = [1,5,3,4,9] print(ary[-1]) # 4 レンジも使える。 ary = [1,5,3,4,9] print(ary[1:3]) # [5,3,4] print(ary[-3:-1]) # [5,3,4] print(ary[:3]) # [1,5,3] レンジ指定で要素を更新できる。 ary = [1,5,3,4,9] ary[2:3] = [100,200] print(ary)# [1, 5, 100, 200, 4, 9] if ... elif ... else elseif が elif。 論理式が偽になるのは\"数値の0\"、空のコレクション、Noneオブジェクト。それ以外は真。 a = 50 if 0 < a < 35: print("OK") elif 35 <= a < 100: print("So much") else: print("NG") for,while forループとwhileループがある。iteratableオブジェクトを指定できる。 whileループについて。 str = \"aiueo\" stop = len(str) i=0 while i < stop: print(str[i]) i += 1 # a # i # u # e # o forループについて。 str = \'abcde\' for c in str: print(c) # a # b # c # d # e # continue, break str = \'abcde\' for c in str: if c == \'c\': continue if c == \'e\': break print(c) # a # b # d whileループ、forループの終了時に実行するブロックをelseで定義できる。 breakで中断した場合は実行されない。 str = \'abcde\' for c in str: print(c) else: print(\'ループ終了\') # a # b # c # d # e # ループ終了 iteratableオブジェクト よくあるパターン。要素の順序を走査するためのインターフェースを実装したオブジェクト。 本家によるとこんな感じ。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/glossary.html#term-iterable\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"iterable\" excerpt=\"(反復可能オブジェクト) 要素を一度に 1 つずつ返せるオブジェクトです。 反復可能オブジェクトの例には、(list, str, tuple といった) 全てのシーケンス型や、 dict や ファイルオブジェクト といった幾つかの非シーケンス型、 あるいは Sequence 意味論を実装した __iter__() メソッドか __getitem__() メソッドを持つ任意のクラスのインスタンスが含まれます。\"]
回帰直線の当てはまりの指標
[mathjax] 前の記事で線形単回帰において訓練データから回帰係数を求める方法を書いてみた。 標本平均を使って母平均を推測する話とリンクさせることで、 回帰係数の95%信頼区間を求めることができた。 回帰係数(hat{beta_0},hat{beta_1})と真の回帰係数(beta_0,beta_1)の関係がこれ。 [clink url=\"https://ikuty.com/2019/05/15/linear_regression_evaluate/\"] RSE,真の回帰直線と観測データがどれくらい離れているか 真の回帰直線がわかったとしても、全てのデータが回帰直線の上に乗っているのでなければ、 回帰直線を使って値を予測したときに誤差が出てくる。 残差平方和(Residual sum of square)。WikipediaにもRSS。 (hat{y_i})は訓練データを使って得られた回帰係数で作った回帰直線で予測した値。 だから、RSS自体も訓練データに対応して変動する。 begin{eqnarray} RSS=sum_{i=1}^n (y_i-hat{y_i})^2 end{eqnarray} で、知りたいのはRSSが訓練データに対してどの程度変動するかだから標準偏差。 標本分散は不偏推定量ではなくて分布の自由度で割る必要がある...という話があって、 不偏推定量を求める段取りが必要。(n-1)ではなく(n-2)で割る!。詳しくは以下。 カイ2乗分布になりそうだけれども、自由度が何故(n-2)なのだろうか...。 begin{eqnarray} RSE= sqrt{frac{1}{n-2}sum_{i=1}^{n}(y_i-hat{y_i}^2)} end{eqnarray} [clink implicit=\"false\" url=\"https://stats.stackexchange.com/questions/204238/why-divide-rss-by-n-2-to-get-rse/377869\" imgurl=\"https://cdn.sstatic.net/Sites/stats/img/logo.svg?v=60d6be2c448d\" title=\"Why divide RSS by n-2 to get RSE?\" excerpt=\"The reason is based on trying to get an unbiased estimator of the underlying error variance in the regression. In a simple linear regression with normal error terms it can be shown that:That is, under the standard assumption of normally distributed errors, the residual sum-of-squares has a chi-squared distribution with ?−2 degrees of freedom. \"] 決定係数(R^2) RSS,RSEは(Y)の単位で値が決まる。(y_i)が無茶苦茶大きいとRSEは大きくなる。 RSEだけ見て回帰直線がどれだけ当てはまっているか言えない様子。 当てはまりの良さを(0)から(1)の範囲におさめる別の指標もある。 TSS (Total sum of square)として以下。 begin{eqnarray} TSS = sum_{i-1}^{n}(y_i-bar{y_i})^2 end{eqnarray} (R^2)として以下。 begin{eqnarray} R^2 &=& frac{TSS-RSS}{TSS} \\ &=& 1-frac{RSS}{TSS} \\ &=& 1 - frac{sum_{i=1}^n (y_i-hat{y_i})^2}{sum_{i-1}^{n}(y_i-bar{y_i})^2} end{eqnarray}
Model Binding と 1枚のBladeで CRUD する
1枚のBladeで確認画面付きCRUDを実現できると、Bladeの枚数が格段に少なくなって良さそう。 その前にまずModelBindingで単なるUserを1枚のBladeでCRUDしてみる。 1枚のBladeが複数の機能で使われることになり、Bladeの中に要素と制御が増えていくため、 実は、Bladeの枚数が増えたとしても1つのBladeを単純にした方が良いのかもしれないが、 1度作っておくとずっと使えるかもしれないので、そこまでやってみる。 やること Laravelに最初から付いてくるUserを使って、name,email,passwordのCRUDをする。 URL(route)は以下。showのパラメタをOptionalにして、あればUpdate、なければCreateする。 Update、Createは、本質的に分けるべきと考えてURLを別にしてある。 firstOrNew()を使うと、あればUserインスタンスを読み込んでくれる。 なければインスタンスを作る。ただしレコードは作らない。新規作成操作時にレコードを作成する。 <?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the \"web\" middleware group. Now create something great! | */ Route::get(\'/user/{user?}\',\'UserController@show\'); Route::post(\'/user/\',\'UserController@add\')->name(\'postAddUser\'); Route::post(\'/user/{user}\',\'UserController@edit\')->name(\'postEditUser\'); コントローラ コントローラは以下。無条件に保存するだけなのでほとんど何も書いてない。 条件が増えてくるとそれなりに行数が増える。 ModelBindingの良さは、タイプヒンティングでEloquentのインスタンスを受けられること。 変数を受けてEloquentインスタンスを探す手間がバッサリ無い。 RequestValidatorは載せていません。 <?php namespace AppHttpControllers; use AppHttpRequestsAddUserRequest; use AppHttpRequestsEditUserRequest; use AppUser; class UserController extends Controller { public function show($id=null) { $user = User::firstOrNew([\'id\'=>$id]); return view(\'user\',compact(\'user\')); } public function add(User $user,AddUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } public function edit(User $user,EditUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } } Blade 肝心のBladeは以下。これだけなのに結構書かないといけない。 laravelcollective/htmlは大分前にLaravelから外れていて、使わない方が良いのかも。 自力でHTMLを書くのと大して労力が変わらない可能性がある。 結構書かないといけないから1枚にしたいのか、複数枚でよければあまり書かなくて良いのか、 微妙なところ。Laravel5.7なのでBootstrap4。validation用のクラスが全然違う。 親Blade(layouts.app)は何でも良いので載せていません。 @extends(\'layouts.app\') @section(\'content\') @if (isset($user->id))編集 @else 追加 @endif @if ($user->wasRecentlyCreated) {!! Form::model($user,[\'route\'=>[\'postEditUser\',$user->id],\'class\'=>\'form-horizontal\'])!!} @else {!! Form::model($user,[\'route\'=>[\'postAddUser\'],\'class\'=>\'form-horizontal\'])!!} @endif {!! Form::label(\'name\', \'名前 :\') !!} @if($errors->has(\'name\')) {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'name\') !!} {!! Form::label(\'email\', \'email :\') !!} @if($errors->has(\'email\')) {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'email\') !!} {!! Form::label(\'password\', \'password :\') !!} @if($errors->has(\'password\')) {!! Form::password(\'password\',[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::password(\'password\',[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'password\') !!} @if($user->wasRecentlyCreated) {!! Form::submit(\'保存\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @else {!! Form::submit(\'新規作成\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @endif {!! Form::close() !!} @endsection まとめ relationもないし懸案の確認画面もないので、単純。 次回、has a、has many relation版と、確認画面付きの版を試します。
