相関係数について
相関係数について
下記相関係数の導出までの説明
正の相関と負の相関
- データ列1のように、年月日に対して値が増加している場合、これを「正の相関がある」という
- データ列2のように、年月日に対して値が増加している場合、これを「負の相関がある」という
年月日 | 20140101 | 20140102 | 20140103 | 20140104 | 20140105 | 20140106 | 20140107 | 20140108 | 20140109 | 20140110 | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
データ列1 | 正の相関 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | |
データ列2 | 負の相関 | 110 | 109 | 108 | 107 | 106 | 105 | 104 | 103 | 102 | 101 |
相関を数値で表す
参考:http://mcn-www.jwu.ac.jp/~yokamoto/ccc/stat/p22covcor/
年月日を変数x、相関関係を考えたい数値を変数yとする 変数xの平均をm_x、変数yの平均をm_yとする 変数の数をNとする
- Σ(x-m_x)(y-m_y)/N を計算すると、xとyに正の相関があるか、負の相関があるか分かる ⇒ 上記をxとyの 共分散 という
- 例えばデータ列1の例では、年月日をx、データ列1の数値をyとすると下記のような計算になる
xの平均 = (20140101 + 20140102 + ・・・ + 20140110)/10 = 20140105.5 yの平均 = (101 + 102 + ・・・ + 110)/10 = 105.5 xとyの共分散 = Σ(x-m_x)(y-m_y)/N = { (20140101-20140105.5) × (101-105.5) + ・・・ +(20140110-20140105.5) × (110-105.5) }/10 = 8.25
データ列1とデータ列2の共分散を計算すると下記の通り(エクセルではCOVAR関数を使用)
テータ列 | 相関 | 共分散 |
---|---|---|
データ列1 | 正の相関 | 8.25 |
データ列2 | 負の相関 | -8.25 |
共分散で相関係数を考える場合の問題点
- 共分散は変数が等倍されるだけで値が変わってしまうため、
- 複数の単位が異なったデータ列について相関の大きさを比較するには向いていない
- データ列1やデータ列2の数値を100倍したものをそれぞれデータ列3、データ列4とする
年月日 | 20140101 | 20140102 | 20140103 | 20140104 | 20140105 | 20140106 | 20140107 | 20140108 | 20140109 | 20140110 | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
データ列3 | 正の相関(数値が100倍) | 10100 | 10200 | 10300 | 10400 | 10500 | 10600 | 10700 | 10800 | 10900 | 11000 | |
データ列4 | 負の相関(数値が100倍) | 11000 | 10900 | 10800 | 10700 | 10600 | 10500 | 10400 | 10300 | 10200 | 10100 |
- データ列3とデータ列4の共分散
テータ列 | 相関 | 共分散 |
---|---|---|
データ列3 | 正の相関(数値が100倍) | 825 |
データ列4 | 負の相関(数値が100倍) | -825 |
⇒ 数値が100倍だと共分散も100倍になってしまう
共分散を相関の大きさだと考えた時の問題点として データ列1、2の数値の単位がメートルだとしたとき、 それをセンチメートル単位で見たものをデータ列3、4とすると、 同じ数字を見ているにもかかわらず、2つの場合で相関の大きさが変化するということになってしまう
相関係数
- 共分散を規格化して、単位をによらない無次元の量としたものを相関係数といい、下記の通り定義する
相関係数 = Σ(x-m_x)(y-m_y)/N ÷ (√(Σ(x-m_x)^2) ÷ (√(Σ(y-m_y)^2) ※相関係数はxとyの共分散をそれぞれの標準偏差で割ったもの
参考:http://mcn-www.jwu.ac.jp/~yokamoto/ccc/stat/p22covcor/
この相関係数でデータ列1~4を計算すると下記の通り(エクセルではCORREL関数を使用)
テータ列 | 相関 | 相関係数 |
---|---|---|
データ列1 | 正の相関 | 1 |
データ列2 | 負の相関 | -1 |
データ列3 | 正の相関(数値が100倍) | 1 |
データ列4 | 負の相関(数値が100倍) | -1 |
⇒ 相関係数は単位によらない値になっている。また、最大値が1、最小値が-1となるよう規格化されている
数関係数=Rとした時の表
範囲 | いえること |
---|---|
1.0≧|R|≧0.7 | 高い相関がある |
0.7≧|R|≧0.5 | かなり高い相関がある |
0.5≧|R|≧0.4 | 中程度の相関がある |
0.4≧|R|≧0.3 | ある程度の相関がある |
0.3≧|R|≧0.2 | 弱い相関がある |
0.2≧|R|≧0.0 | ほとんど相関がない |
相関がない場合
下記のようなデータ列5は年月日と全く関係のない値をとっているため、相関係数は0に近くなる
年月日 | 20140101 | 20140102 | 20140103 | 20140104 | 20140105 | 20140106 | 20140107 | 20140108 | 20140109 | 20140110 | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
データ列5 | 相関なし | 101 | 12 | 390 | -236 | 1 | 12 | 342 | -80 | 100 | 120 |
テータ列 | 相関 | 相関係数 |
---|---|---|
データ列5 | 相関なし | 0.018052993 |
相関が発見できない場合(シンプソンのパラドックス)
相関係数は変数同士の比例、反比例の関係のみ判断できるので、 下記のデータ列6のように半分に区間を区切れば相関があるデータについて見過ごしてしまう危険性がある
年月日 | 20140101 | 20140102 | 20140103 | 20140104 | 20140105 | 20140106 | 20140107 | 20140108 | 20140109 | 20140110 | |
---|---|---|---|---|---|---|---|---|---|---|---|
データ列6 | 相関が発見できない | 101 | 102 | 103 | 104 | 105 | 105 | 104 | 103 | 102 | 101 |
テータ列 | 相関 | 相関係数 |
---|---|---|
データ列6 | 相関があるのに発見できない | 0 |
集団を2つに分けた場合にある仮説が成立しても、集団全体では正反対の仮説が成立することがある ⇒ シンプソンのパラドックス 単純にデータを切り出してその相関係数のみを考えることは危険!
【付録】相関係数はcosθを意味する 下記のようなベクトルを定義すると相関係数はcosθと同じことがわかる
- x = (x1-m_x),(x2-m_x)・・・(xN-m_x)
- y = (y1-m_y),(y2-m_y)・・・(yN-m_y) (以下略)
pythonアルゴリズムなどメモ
内容
自分がよく使うpython3の書き方のまとめ 大体はpython2でも使えるはず(?)
Tips
文字列を受け取って整数にする
N = int(input())
文字列を受け取って整数の配列にする
A = list(map(int, input().split()))
またはリスト内包表記を使って
A = [int(a) for a in input().split()]
文字列を受け取って整数の変数に個別に代入
以下のようにリストの左辺をカンマ区切りで別々の変数にすればそれぞれに格納される
N, A, B = [int(x) for x in input().split()] print(N) print(A) print(B)
3 53 94 ← 入力値 3 53 94
a ~ bまでfor
0~3をfor
for x in range(4): print(x)
0 1 2 3
1から始めて4まで出したい場合は 「1, 4+1」を指定する
for x in range(1, 5): print(x)
1 2 3 4
listに対してfor
l=[1, 2, 3] for i in l: print(i)
結果
1 2 3
数字の各桁をすべて足す
考えた方法1.
以下ではlist(map(int, list(str(n)))
で、
いったん数字nを文字列にしてからそれをlistで一文字ずつ区切ったリストにして、それらをintに戻している
n=12345 summary = 0 for i in list(map(int, list(str(n)))): summary += i
summary : 15
考えた方法2.こちらのほうがわかりやすい
s = 0 while n > 0: s += n % 10 n //= 10
s: 15
ファイル読み込み
以下をopen.pyの名前で保存
import sys args = sys.argv #print(args[1]) path = args[1] with open(path) as f: print(f.read())
python open.py <対象ファイル>
go言語の並行処理
概要
「Go言語による並行処理」を読んだのでメモ ※この本に書いてないことも以下では取り上げている
goroutineとsync
goroutineの書き方
一番単純なgoroutine(これは期待した通り動かない)
package main import "fmt" func main() { hello := func() { fmt.Println("hello") } go hello() }
無名関数helloを定義して、「go 関数()」で実行する しかしこれは実行しても何も出力されない
$go run ex1.go $
その理由は、main goroutineとhello goroutineはそれぞれ別個に動くので、 mainが終わる前に子の処理のhelloを待たないため。
そこで、意図したとおりにするためには、syncで待ち合わせをすることが一番簡単な実現方法となる
syncで待ち合わせをする(期待通りの挙動になる)
syncパッケージを使って待ち合わせをする場合は以下のようにする
- wg.Add(待ちたい処理の数)を定義
- hello goroutineが終わる前に、このgoroutineの終了を伝えるwg.Doneをdeferで実行
- hello goroutineが終わるまで、wg.Wait()で待つ
$cat ex2.go package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) hello := func() { defer wg.Done() fmt.Println("hello") } go hello() wg.Wait() }
これを実行すると以下の通り出力される
$go run ex2.go hello $
syncで待ち合わせをする(helloを定義しない)
上の例はより簡単に、以下のようにも書ける
- かっこの最後に 「 }()」がついていることに注意
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("hello") }() wg.Wait() }
syncで待ち合わせをする(helloを通常の関数として定義する)
helloを通常の関数として定義する場合には以下のように、WaitGroupをポインタとして渡すことが必要になる
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) go hello(&wg) wg.Wait() } func hello(wg *sync.WaitGroup) { defer wg.Done() fmt.Println("hello") }
syncで複数の待ち合わせをする
waitNum := 3 と定義して、その回数分だけ待つ
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup waitNum := 3 wg.Add(waitNum) hello := func() { defer wg.Done() fmt.Println("hello") } for i := 0; i < waitNum; i++ { go hello() } wg.Wait() }
結果
hello hello hello
goroutineに引数を渡す
通常の関数と同様に、goroutineにも引数を渡せる
$cat ex3-2.go package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup waitNum := 3 wg.Add(waitNum) hello := func(i int) { defer wg.Done() fmt.Printf("hello %d\n", i) } for i := 0; i < waitNum; i++ { go hello(i) } wg.Wait() }
結果
$go run ex3-2.go hello 2 hello 0 hello 1
channel
最も簡単なchannel
一番簡単な例
- channelは複数のgoroutine間でデータを共有するためにあるので、ふつうこんな使い方はしない
package main import "fmt" func main() { ch := make(chan int, 1) // channelに5を送信 ch <- 5 // channelから整数値を受信 i := <-ch fmt.Println(i) }
実行結果
$go run chan.go 5
goroutineでのchannel
goroutineでchanelをやり取りする例
package main import "fmt" func main() { ch := make(chan int) go func() { // channelに5を送信 ch <- 5 }() // channelから5を受信 fmt.Println(<-ch) }
実行結果
5
複数のchannelを送受信することもできる
package main import "fmt" func main() { ch := make(chan int) n := 3 go func() { for i := 0; i < n; i++ { ch <- i } }() for i := 0; i < n; i++ { fmt.Println(<-ch) } }
実行結果
0 1 2
for rangeを使ったchannelの受信
for rangeを使ってchannelを受信する書き方もできる
ただし、for rangeを使って受信する場合、channelの終了がわからず永遠に待ち続けるので、goroutine側がこれ以上channelに送信しなくなった時点でchannelをcloseすることが必要
package main import "fmt" func main() { ch := make(chan int) n := 3 go func() { defer close(ch) // channelの終了を送信 for i := 0; i < n; i++ { ch <- i } }() for i := range ch { fmt.Println(i) } }
実行結果
0 1 2
deferを忘れると、待ち続けてdeadlockになる
deferをつけない場合の実行結果
0 1 2 fatal error: all goroutines are asleep - deadlock!
channelのcloseを確認する
channelからは、2番目の返り値としてchannelがclose済みかそうでないかを表すbool値が取得できる 慣習的にこの2番目の返り値はokとする
上の例で言えば、chから得られた値がi、chがclose済みかどうかがok(closeされていればfalse)となる。このokはとっても取らなくてもいい
i, ok := <-ch
参考 - A Tour of Go
上の例を、okがfalseとなるまでchの中身を出力し続けるようにした場合、以下のようになる
- ※上のfor rangeを使った書き方より冗長だしミスると無限ループになるからめったに使う必要ないと思う
package main import "fmt" func main() { ch := make(chan int) n := 3 go func() { defer close(ch) // channelの終了を送信 for i := 0; i < n; i++ { ch <- i } }() for { i, ok := <-ch if !ok { fmt.Println("ch closed") return // このreturnを忘れると無限ループになる } fmt.Println(i) } }
実行結果
0 1 2 ch closed
bufferなしchannel
bufferなしchannelとbufferつきchannelの挙動の違いについてシンプルにr例示している資料が意外とすぐに見つからなかった
まずbufferなしchannelの例
package main import ( "fmt" "time" ) func main() { ch := make(chan int) go func() { defer close(ch) defer fmt.Println("Producer Done") for i := 0; i < 5; i++ { ch <- i fmt.Printf("Sending: %d\n", i) } }() for i := range ch { fmt.Printf("Received %v\n", i) time.Sleep(1 * time.Second) } }
実行結果
Sending: 0 Received 0 Received 1 Sending: 1 Received 2 Sending: 2 Received 3 Sending: 3 Received 4 Sending: 4 Producer Done
- 1秒おきにSendingとReceivedが交互に出力されている
channelが満杯のときはchannelへの書き込みは(読み込みされるまで)ブロックされるので、bufferなしchannelは毎回読み込みをした後で追加の書き込みをしている
bufferつきchannel
bufferつきchannelは一度に送信できる件数を指定できる
package main import ( "fmt" "time" ) func main() { ch := make(chan int, 4) go func() { defer close(ch) defer fmt.Println("Producer Done") for i := 0; i < 5; i++ { ch <- i fmt.Printf("Sending: %d\n", i) } }() for i := range ch { fmt.Printf("Received %v\n", i) time.Sleep(1 * time.Second) } }
実行結果
Sending: 0 Sending: 1 Sending: 2 Sending: 3 Sending: 4 Producer Done Received 0 Received 1 Received 2 Received 3 Received 4
- buffer4に指定した場合、channelに対して先に4つ書き込みが終了し、あとからReceivedされていることがわかる
- このように、bufferつきchannelは読み込みを待つことなく何件まで書き込めるかを指定できる
select
selectは複数のcaseに対して、それぞれの条件が等しく選択されるように、疑似乱数を使って配分している。
例えば、以下のようにc1, c2という2つのチャネルから合計1000回読み込むとする。
- c1からのチャネルから読み込んだらc1Countを増やす
- c2からのチャネルから読み込んだらc2Countを増やす
package main import ( "fmt" ) func main() { c1 := make(chan interface{}) close(c1) c2 := make(chan interface{}) close(c2) var c1Count, c2Count int for i := 1000; i >= 0; i-- { select { case <-c1: c1Count++ case <-c2: c2Count++ } } fmt.Printf("c1Count: %d, c2Count: %d.\n", c1Count, c2Count) }
このコードを何回も実行した結果を見てみると、c1, c2がほぼ同じ回数ずつカウントされていることがわかる
$go run select2.go c1Count: 499, c2Count: 502. $go run select2.go c1Count: 498, c2Count: 503. $go run select2.go c1Count: 520, c2Count: 481. $go run select2.go c1Count: 513, c2Count: 488. $go run select2.go c1Count: 505, c2Count: 496. $go run select2.go c1Count: 502, c2Count: 499. $go run select2.go c1Count: 509, c2Count: 492. $
for-selectでdefault処理
以下のように、チャネルを待っている間やりたい処理をdefaultに書くことでgoroutineの結果を待っている間他の処理ができる
for { select { case <-チャネル: <チャネルを受け取ったあとの処理> case <-チャネル2: <チャネル2を受け取ったあとの処理> default: } <チャネル1とチャネル2を受け取る前に行いたい処理> }
for で無限ループをしながらselectでdoneが来るのを待ち続ける例
goroutineが5秒後にdoneにcloseを送るので、処理が終わる
それまではWaitを繰り返す
package main import ( "fmt" "time" ) func main() { done := make(chan interface{}) go func() { defer close(done) fmt.Println("Goroutine start") time.Sleep(5 * time.Second) fmt.Println("Goroutine end. send done") }() i := 0 for { select { case <-done: fmt.Println("Receive done") return default: } i++ fmt.Printf("Wait %d\n", i) time.Sleep(1 * time.Second) } }
これを実行すると以下のようになる
$go run sample.go Wait 1 Goroutine start Wait 2 Wait 3 Wait 4 Wait 5 Goroutine end. send done Receive done
ちなみに、以下のようにdefautlとselectの終端の}の間に処理を入れても同様の結果になるが、この処理が往々にして長くなることがあってselectの終端がわかりにくくなるので、自分はあまり好んで使う人はいないようだ
default: <チャネル1とチャネル2を受け取る前に行いたい処理> }
timeout
Go by Exampleに書いてあるtimeoutの方法
Go by Example: Timeouts に書いてある方法
time.After(タイムアウトする時間) を設定してselect文で受け取ることで、時間がかかるgoroutineのタイムアウト処理ができる
以下の例では、 - 2秒かかるgoroutineの結果c1が1秒のtimeout制限に引っかかってtimeout 1が出力され、 - 同じく2秒かかるgoroutineの結果c2が3秒のtimeout制限以内に終わるので、result 2が出力される
package main import ( "fmt" "time" ) func main() { c1 := make(chan string, 1) go func() { time.Sleep(2 * time.Second) c1 <- "result 1" }() select { case res := <-c1: fmt.Println(res) case <-time.After(1 * time.Second): fmt.Println("timeout 1") } c2 := make(chan string, 1) go func() { time.Sleep(2 * time.Second) c2 <- "result 2" }() select { case res := <-c2: fmt.Println(res) case <-time.After(3 * time.Second): fmt.Println("timeout 2") } }
実行結果
timeout 1 result 2
上のtimeout方法の問題点
上の方法には一つ問題があって、timeout 1と出てもgoroutineに切り出した処理は自動で止まらないということがある
ためしに以下のようにc1で受け取っていた部分を以下のようにtaskというgoroutineにして、task関数の中では0から999999まで数字を出力し続けるようにしてみる - また、最初と最後の時間差をとって、処理時間を計測してみる (結果は長くなるので注意)
package main import ( "fmt" "log" "time" ) func main() { start := time.Now() select { case res := <-task(): fmt.Println(res) case <-time.After(1 * time.Second): fmt.Println("timeout 1") } log.Printf("Total time: %fs", time.Since(start).Seconds()) } func task() <-chan string { res := make(chan string) go func() { defer close(res) for i := 0; i < 1000000; i++ { fmt.Println(i) time.Sleep(1 * time.Nanosecond) } res <- "done!" }() return res }
実行結果
0 1 2 (省略) 475135 475136 475137 timeout 1 2019/09/29 06:11:19 Total time: 12.296678s
おかしなことが起きた! 実行結果はマシンの性能にもよると思うが、「timeout 1」まで出した後でtimeoutが出力されて、「 Total time: 12.296678s」となっている。
しかも恐ろしいことに2回目に実行すると、今度は「timeout 1」すら出ずに12秒かかっている。
489587 489588 489589 2019/09/29 06:13:37 Total time: 12.209647s
3回目もまた結果が変わる
489490 489491 489492 2019/09/29 06:14:39 Total time: 12.120974s
timeoutとなっても、すでに起動してfmt.Println(i) を出力し続けるgoroutineは勝手に暴走して、それが終わるタイミングは全然予測できていない
これをtimeout時間の通り1秒で終了させたい場合は以下のようにcontextやdoneチャネルを使うといい。
timeoutでcontext cancelを行う場合
上のコードをcontextを用いて書き直したものが以下となる。
timeout時刻になるとcontextのcancel処理が飛んで、1秒で処理が終わっていることがわかる
package main import ( "context" "fmt" "log" "time" ) func main() { start := time.Now() ctx, cancel := context.WithCancel(context.Background()) defer cancel() res := task(ctx) select { case <-res: fmt.Println(res) case <-time.After(1 * time.Second): log.Println("timeout after 1s") cancel() log.Println(<-res) } log.Printf("Total time: %fs", time.Since(start).Seconds()) } func task(ctx context.Context) <-chan string { res := make(chan string) go func() { defer close(res) for i := 0; i < 1000000; i++ { select { case <-ctx.Done(): log.Printf("context cancelled. count: %d", i) res <- fmt.Sprintf("error: %s", ctx.Err()) return default: } fmt.Println(i) time.Sleep(1 * time.Nanosecond) } res <- "done!" }() return res }
実行結果
(省略) 38503 38504 38505 38506 38507 2019/09/29 06:30:35 timeout after 1s 2019/09/29 06:30:35 context cancelled. count: 37841 2019/09/29 06:30:35 error: context canceled 2019/09/29 06:30:35 Total time: 1.000472s
contextの使い方は公式は以下。ほかにもネット上にたくさんあるので、以下で簡単な説明だけ記載しておく。 Go Concurrency Patterns: Context - The Go Blog
簡単に説明
- コードの最初でcancel 機能付きのcontextを宣言する
- 「defer cancel()」によって、プログラム終了時に確実にすべてのcontextがキャンセルされるようにする
ctx, cancel := context.WithCancel(context.Background()) defer cancel()
- task関数にこのcontextを渡して(第一引数として渡すのがセオリー)、for 文の1ループごとに、「ctx.Done()」が来ていないかチェックしている
- このプログラムでは 「ctx.Done()」を受け取ったらlogに「context cancelled. count」とその時のループ番号を出力している
- さらにresにエラー原因として「 ctx.Err()」を返してreturnでgoroutineを終える
- このreturnがないとDoneを受け取ってもgoroutineが終わらないので必要
for i := 0; i < 1000000; i++ { select { case <-ctx.Done(): log.Printf("context cancelled. count: %d", i) res <- fmt.Sprintf("error: %s", ctx.Err()) return default: } fmt.Println(i) time.Sleep(1 * time.Nanosecond) }
- 呼び出し側のmain内では、「res := task(ctx)」としてtaskの戻り値(channel)をいったんres変数に定義
- time.Afterの制限時間内に処理が終われば、「case <-res」に入る
- time.Afterの制限時間を過ぎたら、「timeout after 1s」をlogに表示させて、contextのcancel関数を呼び出す(重要)
- 「log.Println(<-res)」には、task関数内で詰めたctx.Err()の内容が出力される「error: context canceled」
- 「<-res」とすることで、cancelをした後のtask関数が返すエラーをきちんと受け取ってから終わるように待ち合わせをしている
res := task(ctx) select { case <-res: fmt.Println(res) case <-time.After(1 * time.Second): log.Println("timeout after 1s") cancel() log.Println(<-res) }
contextを使うことで、goroutineを安全に終了させることができる
pipeline、fan-in、fan-out
go言語のpipeline、fan-in、fan-out - ludwig125のブログ
複数のgoroutineのエラーハンドリングをする
同時並列数を制御する
シグナル処理
GoogleAppEngineでgoroutineを使う
GoogleAppEngineでgoroutineを使った例 ludwig125.hatenablog.com
vmwareで「パワーオン中にエラーが発生しました」が出た(Windows10)
起きたこと
Windows10 で、 VMware Playerで以下のエラーが発生して起動しない
パワーオン中にエラーが発生しました:Transport(VMDB)error -44 Message. The VMware Authorization Service is not running.
やったこと
Windows10の場合、左下のWindowsボタンから「PC」を検索
PCを右クリックして「管理」を開く
「サービスとアプリケーション」を選択
「サービス」を選択
「VMware Authorization Service」をダブルクリック
「サービスの状態」の「開始」をクリック
OKを押して閉じると「VMware Authorization Service」が「実行中」になっていることが確認できる
go言語で呼び出す関数を動的に変更する
package main import ( "fmt" ) func main() { funcList := []func(){function1, function2, function3} for i:=0; i < 3; i++{ funcList[i]() } } func function1() { fmt.Println("print function1") } func function2() { fmt.Println("print function2") } func function3() { fmt.Println("print function3") }
実行結果
print function1 print function2 print function3
実行例 https://play.golang.org/p/MQnTJWIu7P1
参考にしたページ C# - 関数名call1, call2, call3...の連続実行をまとめる方法はありますか?|teratail
応用
上のを応用して自前のretry関数の挙動を確かめたもの
スプレッドシートへの書き込みを3回失敗するまでretryする関数を作りたかった
https://play.golang.org/p/HV0uqiVphap
以下は、3回失敗するまでretryをする関数とその実行例
- test1は、3回とも失敗するのでinsertedは0
- test1は、3回目で成功するのでinsertedは10
package main import ( "errors" "fmt" "time" ) type Resp struct { status int } func main() { target, inserted := retry([]func()(Resp, error){returnStatus1, returnStatus2, returnStatus3}) fmt.Printf("test1. target %d, inserted: %d", target, inserted) fmt.Printf("\n----------------------\n") target, inserted = retry([]func()(Resp, error){returnStatus1, returnStatus2, returnStatus4}) fmt.Printf("test2. target %d, inserted: %d", target, inserted) } func retry(returnStatusList []func()(Resp, error)) (int, int) { target_num := 10 fmt.Printf("insert target num: %v\n", target_num) var MaxRetries = 3 for attempt := 0; attempt < MaxRetries; attempt++ { resp, err := returnStatusList[attempt]() if err != nil { fmt.Printf("Unable to write value. %v. attempt: %d\n", err, attempt+1) time.Sleep(3 * time.Second) // 3秒待つ continue } status := resp.status if status != 200 { fmt.Printf("HTTPstatus error. %v. attempt: %d\n", status, attempt+1) time.Sleep(3 * time.Second) // 3秒待つ continue } // 書き込み対象の件数と成功した件数 fmt.Printf("succeded to write value.\n") return target_num, target_num } fmt.Printf("Failed to write data to sheet. reached to MaxRetries: %d\n", MaxRetries ) // 書き込み対象の件数と成功した件数 return target_num, 0 } func returnStatus1() (Resp, error) { resp := Resp{} resp.status = 200 err := errors.New("error! happend") return resp, err } func returnStatus2() (Resp, error) { resp := Resp{} resp.status = 404 return resp, nil } func returnStatus3() (Resp, error) { resp := Resp{} resp.status = 201 return resp, nil } func returnStatus4() (Resp, error) { resp := Resp{} resp.status = 200 return resp, nil }
結果
insert target num: 10 Unable to write value. error! happend. attempt: 1 HTTPstatus error. 404. attempt: 2 HTTPstatus error. 201. attempt: 3 Failed to write data to sheet. reached to MaxRetries: 3 test1. target 10, inserted: 0 ---------------------- insert target num: 10 Unable to write value. error! happend. attempt: 1 HTTPstatus error. 404. attempt: 2 succeded to write value. test2. target 10, inserted: 10
少し書き換え
上のはretryの中で回数を指定しているのが気に食わなかったのでちょっと修正。 getReturnStatusというラップ関数を用意
https://play.golang.org/p/hQK-TunSAWE
package main import ( "errors" "fmt" "time" ) type Resp struct { status int } func main() { target, inserted := retry() fmt.Printf("test1. target %d, inserted: %d", target, inserted) } func retry() (int, int) { target_num := 10 fmt.Printf("insert target num: %v\n", target_num) var MaxRetries = 3 for attempt := 0; attempt < MaxRetries; attempt++ { resp, err := getReturnStatus() if err != nil { fmt.Printf("Unable to write value. %v. attempt: %d\n", err, attempt+1) time.Sleep(3 * time.Second) // 3秒待つ continue } status := resp.status if status != 200 { fmt.Printf("HTTPstatus error. %v. attempt: %d\n", status, attempt+1) time.Sleep(3 * time.Second) // 3秒待つ continue } // 書き込み対象の件数と成功した件数 fmt.Printf("succeded to write value.\n") return target_num, target_num } fmt.Printf("Failed to write data to sheet. reached to MaxRetries: %d\n", MaxRetries ) // 書き込み対象の件数と成功した件数 return target_num, 0 } var count = 0 func getReturnStatus() (Resp, error) { funcList := []func()(Resp, error){returnStatus1, returnStatus2, returnStatus3} count++ return funcList[count-1]() } func returnStatus1() (Resp, error) { resp := Resp{} resp.status = 200 err := errors.New("error! happend") return resp, err } func returnStatus2() (Resp, error) { resp := Resp{} resp.status = 404 return resp, nil } func returnStatus3() (Resp, error) { resp := Resp{} resp.status = 201 return resp, nil } func returnStatus4() (Resp, error) { resp := Resp{} resp.status = 200 return resp, nil }
GoogleAppEngine(GAE)メモ
Tips
githubに上げたくない環境変数をどうやって管理するか
以下を参考に、別のyamlをincludesして、秘密にしたいinclude先のファイルは.gitignoreに入れてgithubには上げないようにする
app.yaml リファレンス | Python の App Engine スタンダード環境 | Google Cloud
こんな感じ app.yamlの中身
env_variables: HOGE: "hoge" FUGA: "fuga" includes: - secret_piyo.yaml
secret_piyo.yamlの中身
env_variables: PIYO: "piyo"
secretの方はgitignoreする
$cat .gitignore secret_piyo.yaml
トラブル対応
INVALID_ARGUMENT: Your app may not have more than 15 versions.
$gcloud app deploy app.yaml 略 ERROR: (gcloud.app.deploy) INVALID_ARGUMENT: Your app may not have more than 15 versions. Please delete one of the existing versions before trying to create a new version.
対応方法
$ gcloud app versions list
でバージョンのリストを表示
こんな感じに表示されるので、不要なVERSIONを選んで消す
$ gcloud app versions list SERVICE VERSION TRAFFIC_SPLIT LAST_DEPLOYED SERVING_STATUS default 20180811t000507 0.00 2018-08-11T00:05:29+09:00 SERVING default 20180812t234454 0.00 2018-08-12T23:47:48+09:00 STOPPED default 20180814t002149 0.00 2018-08-14T00:22:47+09:00 STOPPED default 20180814t004044 0.00 2018-08-14T00:41:48+09:00 STOPPED
上の2列目がバージョンID
以下のように消す
$gcloud app versions delete 20190224t071430 20190226t055739 ...以下複数まとめて消せる
deleteには結構時間がかかる
go言語アルゴリズムやTipsなどメモ
内容
go言語で自分がよく使う書き方やTipsのまとめ
並行処理関係は以下
BenchmarkTestは以下
Tips
入力値
入力値を受け取る
文字列で受け取る
var s string fmt.Scan(&s)
整数で受け取る
var N int fmt.Scan(&N)
複数の入力をまとめて受け取ることもできる
var a, b, c int fmt.Scan(&a, &b, &c)
空白区切りの入力値を受け取ってスライスにする
N文字の空白区切りの入力された数値をスライスにする
- fmt.Scanは、改行もしくは空白を区切ってしまうので、N回ループする
l := make([]int, N) for i := range l { fmt.Scan(&l[i]) }
20 24 59 ← 入力値 [20 24 59]
空白区切りの入力値を一行受け取ってスライスにする
参考: Goで標準入力から文字列や数値列を取得する - Qiita
package main import ( "bufio" "fmt" "os" ) func main() { s := bufio.NewScanner(os.Stdin) s.Scan() input := s.Text() l := strings.Split(input, " ") fmt.Println(l) }
3 42 25 ← 入力 [3 42 25] ← 全部stringなので注意
全部intのスライスにしたい場合はこんな感じかな
- この方法よりは上記のfmt.Scanを使ったほうがよっぼど簡単だと思う
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { s := bufio.NewScanner(os.Stdin) s.Scan() input := s.Text() l := strings.Split(input, " ") toIntL := func(l []string) []int { var intL []int for _, v := range l { i, _ := strconv.Atoi(v) intL = append(intL, i) } return intL } fmt.Println(toIntL(l)) }
実行結果
431 12 64 ←入力値 [431 12 64]
整数の数列をソート
package main import ( "fmt" "sort" ) func main() { a := []int{10, 3, 4, 1} A := a sort.Ints(A) fmt.Println(A) B := a fmt.Println(sort.IntSlice(B)) // 上と同じ出力 C := a sort.Sort(sort.IntSlice(C)) fmt.Println(C) // 上と同じ出力 D := a sort.Sort(sort.Reverse(sort.IntSlice(D))) fmt.Println(D) // 逆順に出力 }
結果 [1 3 4 10] [1 3 4 10] [1 3 4 10] [10 4 3 1]
文字列を辞書順にソート
いったんスライスにしてソートしてからjoinで戻す方法
参考
package main import ( "fmt" "sort" "strings" ) func main() { var s string fmt.Scan(&s) sl := strings.Split(s, "") sort.Strings(sl) sj := strings.Join(sl, "") fmt.Println(sj) }
実行
gasdbfgaa ← 入力 aaabdfggs ← 出力
逆順にソートしたいときは以下を使う
- sort.Sort(sort.Reverse(sort.StringSlice(s)))
関数化したもの
複数条件で優先順位をつけたソート
安定ソートを数回するといい - 安定ソート(SliceStable)を使うことで、最初の並びをなるべくそのままにしつつ並べ替える
参考 - GoのSliceをSortする(sort.Sliceとsort.SliceStable) - Qiita
package main import ( "fmt" "sort" ) type Person struct { ID int Name string } func main() { group := []Person{ {ID: 2, Name: "A"}, {ID: 1, Name: "A"}, {ID: 3, Name: "A"}, {ID: 2, Name: "C"}, {ID: 1, Name: "B"}, {ID: 1, Name: "C"}, {ID: 3, Name: "C"}, {ID: 3, Name: "B"}, {ID: 2, Name: "B"}, } // groupをgroup1, group2に代入してそれぞれの変化を見る group1 := group // 最初にID順にソート sort.SliceStable(group1, func(i, j int) bool { return group1[i].ID < group1[j].ID }) // 次にName順にソート。この時、Nameが同じであれば上でソートされたID順の並びを崩さない(安定ソート) sort.SliceStable(group1, func(i, j int) bool { return group1[i].Name < group1[j].Name }) fmt.Printf("IDで安定SortしてからNameで安定ソート:%+v\n", group1) group2 := group // 最初にName順にソート sort.SliceStable(group2, func(i, j int) bool { return group2[i].Name < group2[j].Name }) // 次にID順にソート。この時、IDが同じであれば上でソートされたName順の並びを崩さない(安定ソート) sort.SliceStable(group2, func(i, j int) bool { return group2[i].ID < group2[j].ID }) fmt.Printf("Nameで安定SortしてからIDで安定ソート:%+v\n", group2) }
実行結果
IDで安定SortしてからNameで安定ソート:[{ID:1 Name:A} {ID:2 Name:A} {ID:3 Name:A} {ID:1 Name:B} {ID:2 Name:B} {ID:3 Name:B} {ID:1 Name:C} {ID:2 Name:C} {ID:3 Name:C}] Nameで安定SortしてからIDで安定ソート:[{ID:1 Name:A} {ID:1 Name:B} {ID:1 Name:C} {ID:2 Name:A} {ID:2 Name:B} {ID:2 Name:C} {ID:3 Name:A} {ID:3 Name:B} {ID:3 Name:C}]
数字の各桁の数値をすべて足す
方法1.
sum := 0 for n > 0 { sum += n % 10 n /= 10 }
⇒ n が 12345なら sは15
方法2.
sum := 0 for i := n; i > 0; i /= 10 { sum += i % 10 }
ポインタ
変数のポインタ
package main import ( "fmt" ) func main() { t := 10 fmt.Println(t) change := func(t *int) { *t = 11 } change(&t) fmt.Println(t) }
実行結果
10 11
グリッド
二次元配列を組み立て
package main import ( "fmt" "strings" ) func main() { var h, w int fmt.Scan(&h, &w) // high, width var p [][]string for i := 0; i < h; i++ { var l string fmt.Scan(&l) p = append(p, strings.Split(l, "")) } fmt.Println(p) fmt.Println(p[0][1]) }
実行結果
3 5 12345 67891 23456 [[1 2 3 4 5] [6 7 8 9 1] [2 3 4 5 6]] ← 出力 2 ← 出力
行列計算
n * m
の二次元配列を入力から受け取り
var n, m int fmt.Scan(&n) fmt.Scan(&m) a := make([][]int, n) for i := 0; i < n; i++ { a[i] = make([]int, m) for j := 0; j < m; j++ { fmt.Scan(&a[i][j]) } }
n * mの行列Aと m * l の行列Bの積としてn * lの行列Cを作成
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_7_D
var n, m, l int fmt.Scan(&n) fmt.Scan(&m) fmt.Scan(&l) a := make([][]int, n) for i := 0; i < n; i++ { a[i] = make([]int, m) for j := 0; j < m; j++ { fmt.Scan(&a[i][j]) } } b := make([][]int, m) for i := 0; i < m; i++ { b[i] = make([]int, l) for j := 0; j < l; j++ { fmt.Scan(&b[i][j]) } } c := make([][]int, n) for i := 0; i < n; i++ { c[i] = make([]int, l) } for k := 0; k < n; k++ { for j := 0; j < l; j++ { for i := 0; i < m; i++ { c[k][j] += a[k][i] * b[i][j] } } } fmt.Println(c)
構造体とメソッド
構造体
例
こんな感じにstructを宣言して初期化できる
package main import "fmt" type Person struct { name string age int weight float64 } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} // フィールド名を指定するとわかりやすい fmt.Println(person1) person2 := Person{"Akira", 22, 61.12} // 順番通りなら指定しなくてもいい fmt.Println(person2) }
実行結果
{Taro 21 60.12} {Akira 22 61.12}
メソッド
goにはクラスはないが、構造体にメソッドを追加することでクラスメソッドのように使える
例
上で定義したPerson構造体の要素をすべてstringにしたスライスを返すメソッドを追加した例
func (p *Person) toString() []string { var str []string str = append(str, fmt.Sprintf("%s", p.name)) str = append(str, fmt.Sprintf("%d", p.age)) str = append(str, fmt.Sprintf("%f", p.weight)) return str } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} fmt.Println(person1.toString()) }
実行結果
[Taro 21 60.120000]
例2
Person構造体のスライスPersonsを定義して、要素をすべてinterfaceの二次元配列にして返す関数を使ってみた例
type Person struct { name string age int weight float64 } type Persons []Person func (p *Person) toString() []string { var str []string str = append(str, fmt.Sprintf("%s", p.name)) str = append(str, fmt.Sprintf("%d", p.age)) str = append(str, fmt.Sprintf("%f", p.weight)) return str } func (p *Person) toInterfaceSlice() []interface{} { var is []interface{} is = append(is, p.name) is = append(is, p.age) is = append(is, p.weight) return is } func (ps *Persons) toInterfaceSlices() [][]interface{} { var iss [][]interface{} for _, p := range *ps { iss = append(iss, p.toInterfaceSlice()) } return iss } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} fmt.Printf("要素が全部stringのスライスに変換 %v\n", person1.toString()) fmt.Printf("要素が全部interfaceのスライスに変換 %v\n", person1.toInterfaceSlice()) persons := Persons{} persons = append(persons, person1) person2 := Person{name: "Akira", age: 22, weight: 61.12} persons = append(persons, person2) fmt.Printf("要素が全部interfaceの二次元スライスに変換 %v\n", persons.toInterfaceSlices()) }
実行結果
要素が全部stringのスライスに変換 [Taro 21 60.120000] 要素が全部interfaceのスライスに変換 [Taro 21 60.12] 要素が全部interfaceの二次元スライスに変換 [[Taro 21 60.12] [Akira 22 61.12]]
コード https://play.golang.org/p/FeJx-_wXv68
Enum
参考
4 iota enum examples · YourBasic Go
エラーハンドリング
参考
【go】golangのエラー処理メモ - ②. 例外はないがエラーハンドリングはできるよ(インスタンスや型でハンドリング) - tweeeetyのぶろぐ的めも
エラー・ハンドリングについて(追記あり) — プログラミング言語 Go | text.Baldanders.info
Golangのエラー処理とpkg/errors | SOTA
Errors are values - The Go Blog ← 同じ関数を複数回呼ぶ時のエラーをまとめて扱う
reflect
こちらにまとめた ludwig125.hatenablog.com
アルゴリズム
全N個のものをM個単位で処理する
以下のようなlistを指定の件数ずつ分割して処理したいとき
package main import ( "fmt" ) func main() { l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10} for _, i := range l { fmt.Println(i) } } // 結果 /* 1 2 3 4 5 6 6 7 8 9 10 */
以下のようにしてみた
package main import ( "fmt" ) func main() { l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10} chunkSize := 3 for start := 0; start < len(l); start += chunkSize { end := start + chunkSize if end > len(l) { end = len(l) } fmt.Println(l[start:end]) fmt.Println("---") } } // 結果 /* [1 2 3] --- [4 5 6] --- [6 7 8] --- [9 10] --- */