GKEのチュートリアルでkubectlをインストールできなかった
こちらの方法に従ってkubectlをインストールしようとしたらエラーが出て失敗
gcloud components install kubectl
$ gcloud components install kubectl ERROR: (gcloud.components.install) You cannot perform this action because the Cloud SDK component manager is disabled for this installation. You can run the following command to achieve the same result for this installation: sudo apt-get install kubectl
sudo apt-get install kubectl も失敗した
$ sudo apt-get install kubectl E: パッケージ 'kubectl ' にはインストール候補がありません
Kubernetes set-up on ubuntu on Google compute - Stack Overflow
ERROR: (gcloud.components.update) The component manager is disabled for this installation
It is a known issue on the Google Cloud SDK issue tracker : Issue 336: kubectl not installed by google-cloud-sdk debian package, and not installable
Unfortunately, it provides a poor experience for first timer testing kubernetes as it's hard to find a quick AND CLEAN step by step solution.
Here is one:
とあったので、以下のコマンドを逐次実行したことで解決した
sudo apt-get update sudo apt-get remove google-cloud-sdk curl https://sdk.cloud.google.com | bash exec -l $SHELL gcloud init gcloud components list gcloud components install kubectl gcloud components list
注意:もともと入っているgcloud関連パッケージが消えてなくなるので、gcloud components listをして何が入っていたのかなど確認してから実行したほうがいい
go言語reflectメモ
reflect パッケージについてメモ
他記事:
reflectとは
- reflectはGoでReflection機能を提供するためのパッケージ
- このパッケージの関数を使うことで、引数の型や値を動的に取得できる
使い方1. (reflectで参照した変数の値を変更しないとき)
package main import ( "fmt" "reflect" ) type Point struct { X int Y int } func main() { p := Point{10, 5} // 変数を定義 rv := reflect.ValueOf(p) fmt.Printf("rv.Type = %v\n", rv.Type()) // 名前空間付きの型名 fmt.Printf("rv.Kind= %v\n", rv.Kind()) // 格納リソース種別 fmt.Printf("rv.Interface= %v\n", rv.Interface()) // interface{}としての実際の値 xv := rv.Field(0) // rv内のX要素を取り出し fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 }
実行結果
rv.Type = main.Point rv.Kind= struct rv.Interface= {10 5} xv: 10
- 変数の値を変更しないのであれば下のように変数(p)を直接reflect.ValueOfに入れると、reflect.Valueが取得できる
Typeでreflect.Valueの名前空間付きの型の名前(例ではmain.Point)を取得できる
- https://golang.org/pkg/reflect/#Value.Type
Type returns v's type.
- https://golang.org/pkg/reflect/#Value.Type
Kindでint, struct, mapなどの型種別が取得できる
- https://golang.org/pkg/reflect/#Value.Kind
Kind returns v's Kind. If v is the zero Value (IsValid returns false), Kind returns Invalid.
- https://golang.org/pkg/reflect/#Value.Kind
Fieldでstructのフィールドを取得できる
- https://golang.org/pkg/reflect/#Value.Field
Field returns the i'th field of the struct v. It panics if v's Kind is not Struct or i is out of range.
- https://golang.org/pkg/reflect/#Value.Field
使い方2. (reflectで参照した変数の値を変更するとき)
- 変数の値を変更するときは、SetInt(SetFloat, SetStringなど他にもいろいろ)を使う
- ただし、変更するためには以下のようにValueOfにポインタを渡して、そのElemを取る必要がある
Elemはinterfaceの値もしくはポインタの参照先を返す
- reflect - The Go Programming Language
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil.
- reflect - The Go Programming Language
package main import ( "fmt" "reflect" ) type Point struct { X int Y int } func main() { p := &Point{X: 10, Y: 5} // ポインタにすることによって編集可能にする rv := reflect.ValueOf(p).Elem() // Elemによってポインタが指している先の値を取得する fmt.Printf("rv.Type = %v\n", rv.Type()) // 名前空間付きの型名 fmt.Printf("rv.Kind= %v\n", rv.Kind()) // 格納リソース種別 fmt.Printf("rv.Interface= %v\n", rv.Interface()) // interface{}としての実際の値 xv := rv.Field(0) // rv内のX要素を取り出し fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 xv.SetInt(100) // intの100をX要素にセット fmt.Printf("after SetInt xv: %d\n", xv.Int()) // intに変換して表示 }
実行結果
rv.Type = main.Point rv.Kind= struct rv.Interface= {10 5} xv: 10 after SetInt xv: 100
【値を変更しようとしてpanicになる場合1】ポインタを渡さなかった場合
- ポインタではなく値を変更しようとするとpanicになる
func main() { p := Point{X: 10, Y: 5} rv := reflect.ValueOf(p) xv := rv.Field(0) // rv内のX要素を取り出し fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 xv.SetInt(100) // intの100をX要素にセット => panic: reflect: reflect.Value.SetInt using unaddressable value fmt.Printf("after SetInt xv: %d\n", xv.Int()) // intに変換して表示 }
実行結果
xv: 10 panic: reflect: reflect.Value.SetInt using unaddressable value goroutine 1 [running]: reflect.flag.mustBeAssignable(0x82, 0xf0a20) /usr/local/go/src/reflect/value.go:234 +0x180 reflect.Value.SetInt(0xf0460, 0x414020, 0x82, 0x7, 0x64, 0x0) /usr/local/go/src/reflect/value.go:1542 +0x40 main.main() /tmp/sandbox268672126/prog.go:18 +0x200
【値を変更しようとしてpanicになる場合2】Elemを使わなかった場合
- Elemでreflect.Valueの指し示す先の値を取得していないとFieldメソッドを使った時にpanicになる
- Field (再掲)
- reflect - The Go Programming Language
Field returns the i'th field of the struct v. It panics if v's Kind is not Struct or i is out of range.
- reflect - The Go Programming Language
func main() { p := &Point{X: 10, Y: 5} rv := reflect.ValueOf(p) xv := rv.Field(0) // rv内のX要素を取り出し => panic: reflect: call of reflect.Value.Field on ptr Value fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 xv.SetInt(100) // intの100をX要素にセット fmt.Printf("after SetInt xv: %d\n", xv.Int()) // intに変換して表示 }
実行結果
panic: reflect: call of reflect.Value.Field on ptr Value goroutine 1 [running]: reflect.Value.Field(0xe83c0, 0x414020, 0x16, 0x0, 0x40c120, 0x0, 0x90b80, 0x38f1) /usr/local/go/src/reflect/value.go:813 +0x160 main.main() /tmp/sandbox389676022/prog.go:16 +0xe0
値を変更できるか(SetXXXを使えるか)はCanSetを使うと判定できる
package main import ( "fmt" "reflect" ) type Point struct { X int Y int } func main() { p := Point{X: 10, Y: 5} rv := reflect.ValueOf(p) if rv.CanSet() { fmt.Println("p CanSet: true") xv := rv.Field(0) // rv内のX要素を取り出し fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 xv.SetInt(100) fmt.Printf("after SetInt xv: %d\n", xv.Int()) // intに変換して表示 } else { fmt.Println("p CanSet: false") } pPtr := &Point{X: 10, Y: 5} rv = reflect.ValueOf(pPtr).Elem() if rv.CanSet() { fmt.Println("pPtr CanSet: true") xv := rv.Field(0) // rv内のX要素を取り出し fmt.Printf("xv: %d\n", xv.Int()) // intに変換して表示 xv.SetInt(100) fmt.Printf("after SetInt xv: %d\n", xv.Int()) // intに変換して表示 } else { fmt.Println("pPtr CanSet: true") } }
実行結果
p CanSet: false pPtr CanSet: true xv: 10 after SetInt xv: 100
使えそうな使用例
1. 構造体のフィールドを順に出力
package main import ( "fmt" "reflect" ) type Point struct { X int Y int Z int } func (p Point) PrintFiled() { v := reflect.ValueOf(p) t := v.Type() for i := 0; i < t.NumField(); i++ { // 構造体のフィールド名、フィールドの値、フィールドの型を出力 fmt.Printf("%s %d %T\n", t.Field(i).Name, v.Field(i).Interface(), v.Field(i).Interface()) } } func main() { p1 := Point{X: 10, Y: 5} p1.PrintFiled() fmt.Println("----") p2 := Point{X: 100, Y: 50, Z: 7} p2.PrintFiled() }
実行結果
X 10 int Y 5 int Z 0 int ---- X 100 int Y 50 int Z 7 int
2. 構造体の型が何であってもinterface型のSliceにまとめて返す
- どんな型であってもinterfaceのスライスにしたい場面があったので
- 以下のように、toInterfaceSlice関数を定義して、PointとUserのInterfaceSliceメソッドの中でこの関数を呼ぶだけにすることで共通化できた
- toInterfaceSlice関数の引数はinterface{}なのでどの構造体でも受け付けられる
package main import ( "fmt" "reflect" ) func toInterfaceSlice(v interface{}) []interface{} { var vs []interface{} rv := reflect.ValueOf(v) rt := rv.Type() for i := 0; i < rt.NumField(); i++ { vs = append(vs, rv.Field(i).Interface()) } return vs } type Point struct { X int Y int Z int } func (p Point) InterfaceSlice() []interface{} { return toInterfaceSlice(p) } type User struct { ID int Name string Height float64 Weight float64 } func (u User) InterfaceSlice() []interface{} { return toInterfaceSlice(u) } func main() { p := Point{X: 10, Y: 5, Z: 1} for _, v := range p.InterfaceSlice() { fmt.Printf("%v %T\n", v, v) } fmt.Println("----") u := User{ID: 1001, Name: "hoge", Height: 170.1, Weight: 60.45} for _, v := range u.InterfaceSlice() { fmt.Printf("%v %T\n", v, v) } }
実行結果
10 int 5 int 1 int ---- 1001 int hoge string 170.1 float64 60.45 float64
2-2. 関数に渡すパラメータが構造体のポインタの場合でも対応できるようにする
- 上のtoInterfaceSlice関数で引数が構造体のポインタの場合には以下のようにElemを使って実体を取得するようにすれば、 引数が構造体の場合も、構造体のポインタの場合も両方対応できる
func toInterfaceSlice(v interface{}) []interface{} { var vs []interface{} rv := reflect.ValueOf(v) fmt.Printf("rv.Kind %v\n", rv.Kind()) // パラメータvが構造体のポインタのときはElemでポインタの指している先の値を取得する if rv.Kind() == reflect.Ptr { // vが構造体のポインタの時はここを通る rv = reflect.ValueOf(v).Elem() } rt := rv.Type() for i := 0; i < rt.NumField(); i++ { vs = append(vs, rv.Field(i).Interface()) } return vs } type Point struct { X int Y int Z int } func (p Point) InterfaceSlice() []interface{} { // toInterfaceSliceには構造体を渡す return toInterfaceSlice(p) } type User struct { ID int Name string Height float64 Weight float64 } func (u *User) InterfaceSlice() []interface{} { // toInterfaceSliceには構造体のポインタを渡す return toInterfaceSlice(u) }
全体のコードは以下
実行結果
rv.Kind struct 10 int 5 int 1 int ---- rv.Kind ptr 1001 int hoge string 170.1 float64 60.45 float64
2-3. 構造体のフィールドに構造体を含んでいる場合にも対応できるようにする
- 以下のように、構造体のフィールドに構造体を持つ場合(ネストしている場合)にも対応できるようにしたい
type GamePlayerInfo struct { User Point }
- 以下のように条件分岐して再帰を使ってみる
func toInterfaceSlice(v interface{}) []interface{} { var vs []interface{} rv := reflect.ValueOf(v) if rv.Kind() == reflect.Ptr { rv = reflect.ValueOf(v).Elem() } rt := rv.Type() for i := 0; i < rt.NumField(); i++ { if rv.Field(i).Kind() == reflect.Struct { // フィールドがstructの場合は再帰でinterfaceのSliceを取得して後ろにつなげる sl := toInterfaceSlice(rv.Field(i).Interface()) vs = append(vs, sl...) } else { vs = append(vs, rv.Field(i).Interface()) } } return vs }
全体のコードは以下
実行結果
10 int 5 int 1 int ---- 1001 int hoge string 170.1 float64 60.45 float64 ---- 1001 int hoge string 170.1 float64 60.45 float64 10 int 5 int 1 int
2-4. 構造体のフィールドで〇〇メソッドを持つ場合はそれを使うようにする(おまけ)
- 以下のように、Attribute変数(Enum)のように、Stringメソッドを持つ場合はそれで出力させる
- MethodByName を使う
type Attribute int const ( Fire Attribute = iota Water Earth Air ) // constのString変換メソッド func (a Attribute) String() string { return []string{"Fire", "Water", "Earth", "Air"}[a] } type SpecialGamePlayerInfo struct { User Point Attribute }
参考
Go言語におけるinterface{}とリフレクションを使ったパターン - Qiita
reflect-examples/README.md at master · a8m/reflect-examples · GitHub
CloudSQLを安くするために考えたこと
無料トライアル期間が終わったのでCloudSQLのマイグレーションをした話
安さを追求したわけではなく、自分なりの妥協点を探っただけ
事象
2019/5/22 CloudSQLに接続できなくなった
$ gcloud sql connect myfinance --user=root ERROR: (gcloud.sql.connect) HTTPError 409: The instance or operation is not in an appropriate state to handle the request.
GoogleAppEngineのログには以下が出力されていた
failed to insert table: daily, err: driver: bad connection, query:
原因
Google Cloud PlatformのUIの一番上を見ると無料トライアル期間が終わったとを通知していた
上のUIの右の「アップグレード」ボタンを押すと以下のように表示された
これを承諾するまえに、そもそも無料トライアル期間中はいくらくらい使っていたのか確認してみた
これからかかるはずの料金の確認
レポートを見てみる
レポートの見方 - 請求レポートによる費用傾向の表示 | Cloud Billing のドキュメント | Google Cloud
「お支払い」の「レポート」を見てみた
レポートの、先月分をプロダクト単位で見てみる - グループ条件を変更
SKU単位で見てみると使用しているサービスの内訳がわかる
SKU | プロダクト | SKU ID | 使用 | クレジット適用前の費用 | プロモーション | 割引 | クレジット適用後の費用 |
---|---|---|---|---|---|---|---|
DB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount) | Cloud SQL | 5356-0253-5FBD | 720 hour | ¥6,996 | ¥-6,996 | — | ¥0 |
Storage PD SSD for DB in Japan | Cloud SQL | 9D66-0506-E274 | 10 gibibyte month | ¥244 | ¥-244 | — | ¥0 |
特にこれ - DB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount) - クレジット適用前の費用:¥6,996
・・・なにこの値段
SKUって?
SKUは最小管理単位 (Stock Keeping Unit) の略。
Google Cloud PlatformにはSKUの意味について説明がなかったのでたぶんこれのことだと思う
毎月の請求について | Cloud Billing のドキュメント | Google Cloud
SKU ID サービスが使用するリソースの ID。SKU の詳細な一覧については、GCP SKU をご覧ください。
GCP SKU一覧
この単位で料金を決めているらしい
この金額が正当なのか確認
上で表示したレポートのSKUの一覧を見ると、 DB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount) に一番金がかかっているらしい
- ¥6,996
この「DB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount)」を上の公式の表「GCP SKU一覧」から探すとあった
DB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount) - 0.088 USD per hour
この0.088を4月中フル稼働した場合(720時間)で、現在の円ドルレート 約110円を当てはめると、 0.088720110=6969.6 となって上とほぼ等しい
with 30% promotional discount
この「with 30% promotional discount」というのは、 「継続利用割引」というサービスらしい
Google Compute Engine の料金 | Compute Engine ドキュメント | Google Cloud
継続利用割引は、使用する vCPU とメモリ量ごとに計算されます。Compute Engine では、1 か月の 25% を超える期間にわたり vCPU またはメモリ量がインスタンスで使用されると、そのリソースの使用が 1 秒延びるごとに割引が自動的に適用されます。 使用時間が増えるほど割引率は高くなり、1 か月ずっと稼働するインスタンスでは vCPU とメモリの費用の最大 30% の正味割引を受けることができます。
CloudSQLの料金体系について確認する
Cloud SQL の料金 | Cloud SQL ドキュメント | Google Cloud
自分が使っているのはCloudSQLのMysql 第 2 世代なのでそれを見る
MySQL 第 2 世代の料金 第 2 世代の料金は、次の料金で構成されます。 インスタンスの料金 ストレージの料金 ネットワークの料金
mysql 2ndの料金
東京(asia-northeast1)を見ると、
マシンタイプ | 仮想 CPU 数 | RAM(GB) | 最大ストレージ容量 | 最大接続数 | 料金(米ドル) | 継続利用価格(米ドル) |
---|---|---|---|---|---|---|
db-n1-standard-1 | 1 | 3.75 | 10,230 GB | 4,000 | $0.13 | $0.09 |
となっていた。
これは上のDB standard Intel N1 1 VCPU running in Japan (with 30% promotional discount) の 0.088という数字と一致する
なるほど・・・高い
各インスタンスの設定
インスタンスの設定 | Cloud SQL ドキュメント | Google Cloud
こちらに詳しく書いてある
どうして720時間(一月休みなく動かし続けるとこの時間)使っていることになっているのか?
これが一番疑問だった
自分のサービスはCronで一日せいぜい2時間くらいしか動いていないのにどうして休みなく稼働していることになっているのか?
調べてみると、以下の通り明示的にDBを停止させない限り、ずーっと稼働していて、その分料金もかかるらしい
Mysqlの第 2 世代の特徴
第 2 世代機能 | Cloud SQL ドキュメント | Google Cloud
料金の違い Cloud SQL 第 2 世代では、従量制の料金パッケージが提供されていません。インスタンスの料金は、マシンタイプによって決まります。分単位の課金と継続利用割引の導入により、Cloud SQL 第 2 世代では多くのワークロードで費用対効果を高めることができます。詳しくは、料金のページをご覧ください。
アクティベーションポリシー
インスタンスの設定 | Cloud SQL ドキュメント | Google Cloud
アクティベーション ポリシー 第 2 世代インスタンスの場合、アクティベーション ポリシーはインスタンスを起動または停止するためにのみ使用されます。アクティベーション ポリシーを [常にオン] に設定するとインスタンスが起動し、[オフ] に設定するとインスタンスが停止します。
つまり、オフにしない限り利用し続けている状態となるらしい
- 第1世代にはON DEMAND というポリシーがあって、リクエストが来たら起動する(立ち上がりに時間かかるけど)という仕組みがあったらしいが、それは第2世代にはない
インスタンスの起動、停止はどうすればいいのか
インスタンスの起動、停止、再起動 | Cloud SQL | Google Cloud
ブラウザまたはgcloudコマンドで起動・停止できるらしい
料金を安くできないか
以上より、料金を安く抑えてCloudSQLを使うためには以下の3つが考えられそう
3番目については自動でgcloudコマンドを実行できるようにそのうちしたい
一番安いリージョンの一番安いマシンを調べる
https://cloud.google.com/sql/pricing?hl=ja
をいろいろ見た結果、 アメリカなどのリージョンのdb-f1-microマシンが一番安かったのでこれを選択 なんでも良かったので、アイオワにする
リージョン | マシンタイプ | 仮想 CPU 数 | RAM(GB) | 最大ストレージ容量 | 最大接続数 | 料金(米ドル) | 継続利用価格(米ドル) |
---|---|---|---|---|---|---|---|
アイオワ | db-f1-micro* | 共有 | 0.6 | 3,062 GB | 250 | $0.0150 | $0.0105 |
東京 | db-n1-standard-1 | 1 | 3.75 | 10,230 GB | 4,000 | $0.1255 | $0.0878 |
アイオワのdb-f1-micro は東京のdb-n1-standard-1の1/8以下
継続利用価格(米ドル)では$0.0105 / hour
※この料金は一時間あたりの利用料金のことらしい
Google Cloud Platform 料金計算ツール | Google Cloud Platform | Google Cloud - このツールで見積もりもできる
自分のサービスはSLAがめちゃめちゃ低く、レイテンシーはほとんど気にしない。データサイズも数十GBなので、 db-f1-microで十分そう
想定費用を計算してみた
リージョン×マシンタイプ | 料金(24時間×31日×110円 ※ 110は現在のレート) |
---|---|
アイオワ db-f1-micro | 0.01052431*110=859.32円 |
東京 db-n1-standard-1o | 0.08782431*110=7185.552円 |
インスタンスの他にストレージとネットワークの料金が別にかかるが、一旦これだけ考える
もとの金額に比べれば、趣味で使う金額としてだいぶ現実的になった
ストレージについて
ストレージはSSDのほうが当然高い
でもこれくらいなら許容しようかな(あとで変えられるのでSSDを選ぶ) - $0.17 per GB/month for SSD storage capacity - $0.09 per GB/month for HDD storage capacity
SKUの表で料金を確認
https://cloud.google.com/skus/?currency=JPY&filter=micro
CloudSQLの表と名前が一対一対応していないのでわかりにくいけど、おそらくこれが対応するSKUとその金額だと思える
DB generic Micro instance with burstable CPU running in Americas (with 30% promotional discount) - 0.0105 USD per hour
新規DBの作成
アップグレード
最初のアップグレードボタンを押して使えるようにする
インスタンスの作成
https://console.cloud.google.com/sql/instances から、アイオワリージョンのdb-f1-microを新しく作る
で「インスタンスを作成」
Mysqlを選択
任意のインスタンスIDとパスワードを設定する
このときに下の「設定オプションを表示」を押すと、マシンタイプなどを細かく設定できる - リージョンはあとから変えられない
※あとでも変更できるけど、何も設定しないとdb-n1-standard-1になってしまうので超注意!!
インスタンスの編集
上の、「設定オプションを表示」をいじらなくても、あとからマシンタイプなどは変えられる
Google Cloud Platform のSQLでインスタンスが見られるので、その画面の中の「設定」→「設定を編集」から設定を変更できる
マシンタイプとストレージの設定
マシンタイプとストレージの設定を編集
変更前
変更後
- マシンタイプ:db-f1-micro
- ストレージの種類:SSD
- ストレージ容量 :最低の10GB
- ストレージの自動増量を有効化:チェックした
自動バックアップの有効化
しない - とりあえず、自分で定期的に頑張ることにする
新旧DBデータ移行汎用手順
0. 準備
Cloud SQL for MySQL の使用 | Go の App Engine スタンダード環境 | Google Cloud
Cloud SQL for MySQL のクイックスタート | Cloud SQL for MySQL | Google Cloud
これに従ってcloud_sql_proxyをローカルマシンに入れておく
事前に新旧DBのインスタンス接続名を調べておく
Google Cloud Platform の左上のボタンを押してからSQLを選んで、
対象のインスタンスIDを選択して、 「このインスタンスに接続」->「インスタンス接続名」から取得できる
1. 旧DBへのプロキシサーバ立ち上げ
ローカルの端末で以下を実施
$ cloud_sql_proxy -instances=<インスタンス接続名>=tcp:3307
2. 旧DBのテーブルをDump(Export)
別の端末を開いてTableデータをDump
$mysqldump -u root -p --host 127.0.0.1 --port 3307 <Database名> <テーブル名> > dumpdata.sql
- Database名: show databases; で出てくるなかの対象のDatabase
3. 新DBのテーブルへImport
新DBへのプロキシサーバ立ち上げ
先程までの旧DBに繋がっていたプロキシサーバは止めて、ローカルの端末で以下を実施
$ cloud_sql_proxy -instances=<新DBのインスタンス接続名>=tcp:3307
4. 新DBでDatabaseを作っておく
別の端末で新DBに接続
$mysql -u root -p --host 127.0.0.1 --port 3307 MySQL [(none)]> CREATE DATABASE <Database名>;
5. 新DBのテーブルへImport
別の端末を開いてTableデータをImport
$mysql -u root -p --host 127.0.0.1 --port 3307 <Database名> < dumpdata.sql
GoogleHomeまたはAndroidに話しかけてスプレッドシートにメモを取る
概要
GoogleHomeやAndroidに話しかけるだけでスプレッドシートにメモを取ることができる
ちょっとしたメモを取るのに便利
こちらとほぼ同じ方法で作った
手順
これだけ 1.スプレッドシートを用意する 2.スプレッドシートにGoogleAppScriptを追加する 3.IFTTTを登録する
1.スプレッドシートシートを用意する
新しいスプレッドシートを作成 → 空白を選ぶ
ここでは適当に「test」という名前のスプレッドシートにしたが何でもいい
2.スプレッドシートにGoogleAppScriptを追加する
GoogleAppScriptを作成
上のタブの「ツール」→ 「スクリプト エディタ」を選択
適当に「test-gas」というプロジェクト名にするが何でもいい
コード.gsに以下を記入する
function addDate() { var sheet = SpreadsheetApp.getActiveSheet(); var lastRow = sheet.getLastRow(); var date = new Date(); var unixTimestamp = Math.round( date.getTime() / 1000 ); sheet.getRange(lastRow, 1).setValue(date); }
「lastRow, 1」にすると縦一列目に入力時刻が記載される
トリガーを追加
コード.gsの上の部分の時計のアイコンを選択
「新しいトリガーを作成します。」をクリック
必要な部分を選択 - 実行する関数を選択: addDate - 実行するデプロイを選択:Head ←これしかなかった - イベントのソースを選択:スプレッドシートから - イベントの種類を選択:変更時 - エラー通知設定:1週間おきに通知を受け取る ← 好みで変えていい
記載したら保存
「アカウントの選択」でGmailIDを選択
「このアプリは確認されていません」と出るが、無視して詳細を選択して、 「~~(安全ではないページ)に移動」をクリックする
「test-gas が Google アカウントへのアクセスをリクエストしています」 の下の「許可」を押す
ここまでの動作確認
ここまでやったら最初のtestシートに戻る
Bより右の列に適当に入力してEnterキーを押すと以下のようにA列に日付が自動で出力されるはず
これだけでもいいが、時刻までシートに表示させたいので、表示形式を変える
A列目を全部選択して、「表示形式」→ 「数値」→「日時」を選択
またA列目を全部選択して右クリックから「条件付き書式」を選ぶと、以降A列目だけ色がつくので見やすい(やらなくてもいい)
3.IFTTTを登録する
IFTTTのトップページにいって「My Applets」を選択
New Appletを選択
this
Google Assistantを選択
Say a phrase with a text ingredient を選択
「テスト $」や「test $」など発音しやすい(かつGoogleAssistantが他の言葉と区別しやすい)言葉を選択
Create triggerを押す
that
Google Sheetsを選択
Add row to spreadsheet を選択して以下のようにする
もしGoogleAppScriptおよびトリガーを設定せずに、左の列にCreatedAtを選択するとGoogleHome経由で入力すると「May 14, 2019 at 08:48PM」のような形式でスプレッドシートに記載される この機能は昔は機能していなかった気がするので、GoogleAppScriptをわざわざ作ったんだけど。。 ただ、日付の形式は変えられない。 もし形式がこれでよければスプレッドシート側のスクリプトは不要そう
動作確認
GoogleHomeまたはAndroidで「OK Google テスト 123」
なぜかGoogleHome経由からだと時刻が表示されない。 でもセルを見れば時刻がわかるからいいかな。。
GoogleHome経由でスプレッドシートに記入されないとき
Google ドライブ - 1 か所であらゆるファイルを保管 Google Driveを見て、以下のthatで定義したGoogleHomeのフォルダに対象のシートが入っているか確認する
別のフォルダの同名のシートに書き込まれていることがあった
GoogleAppEngine(GAE)でgoroutineを使う
GoogleAppEngineでgoroutineを使う
以下に記載されている通り、GoogleAppEngineでは並列処理はサポートしていない。
Go Runtime Environment | App Engine standard environment for Go | Google Cloud
App Engine の Go ランタイム環境は、goroutine を完全にサポートしていますが、並列実行はサポートしていません。goroutine は単一のオペレーティング システム スレッドにスケジュールされます。このシングルスレッド制限は、今後のバージョンで解除される予定です。特定のインスタンスで複数のリクエストを同時に処理することができます。これは、1 つのリクエストで Cloud Datastore API の呼び出しを待っている間に、同じインスタンスで別のリクエストを処理できることを意味します。
しかし、並行処理はできる
これは、同時に複数の処理を実行すること(並列処理)はできないが、APIのレスポンスなどを待つ間に別の処理をすること(並行処理)はできるということを意味する
【脱線】並行性と並列性の違い
「Go言語による並行処理」ではこの違いを以下の一文で表している
並行性はコードの性質を指し、並列性は動作しているプログラムの性質を指します。
ピンと来ない表現・・・
検証
検証用のコード
以下のような3つのHandlerを作成して、それぞれの挙動を確認する - testHandler - testGoroutineHandler - testGoroutineHandler2
挙動の違いが分かりやすいように、time.Sleep(3 * time.Second)を入れている
このコードは以下の「複数のgoroutineの結果の取得」に書いたものを使っている go言語の並行処理 - ludwig125のブログ
package main import ( "fmt" "net/http" "sync" "time" "google.golang.org/appengine" // Required external App Engine library "google.golang.org/appengine/log" "google.golang.org/appengine/urlfetch" // 外部にhttpするため(http.GetはGAEでは使えない) ) func main() { http.HandleFunc("/test", testHandler) http.HandleFunc("/test_goroutine", testGoroutineHandler) http.HandleFunc("/test_goroutine2", testGoroutineHandler2) appengine.Main() // Starts the server to receive requests } func testHandler(w http.ResponseWriter, r *http.Request) { // GAE log ctx := appengine.NewContext(r) client := urlfetch.Client(ctx) urls := []string{"https://www.google.com", "https://badhost", "https://www.yahoo.co.jp/"} checkStatus := func(urls ...string) []*http.Response { var response []*http.Response for _, url := range urls { log.Infof(ctx, "client.Get '%s'", url) resp, err := client.Get(url) if err != nil { log.Errorf(ctx, "%v", err) continue } response = append(response, resp) // 関数の実行にかかる時間を遅らせる log.Infof(ctx, "sleep 3 sec") time.Sleep(3 * time.Second) } return response } for _, response := range checkStatus(urls...) { log.Infof(ctx, "checkStatus: status: %s", response.Status) } } func testGoroutineHandler(w http.ResponseWriter, r *http.Request) { // GAE log ctx := appengine.NewContext(r) client := urlfetch.Client(ctx) type Result struct { Error error Response *http.Response } checkStatus := func( done <-chan interface{}, urls ...string, ) <-chan Result { resultChan := make(chan Result) go func() { defer close(resultChan) for _, url := range urls { log.Infof(ctx, "client.Get '%s'", url) resp, err := client.Get(url) // 関数の実行にかかる時間を遅らせる log.Infof(ctx, "sleep 3 sec") time.Sleep(3 * time.Second) result := Result{Error: err, Response: resp} select { case <-done: return case resultChan <- result: } } }() return resultChan } done := make(chan interface{}) defer close(done) urls := []string{"https://www.google.com", "https://badhost", "https://www.yahoo.co.jp/"} for result := range checkStatus(done, urls...) { if result.Error != nil { log.Infof(ctx, "error: %v", result.Error) continue } log.Infof(ctx, "checkStatus: status: %s", result.Response.Status) } } urls := []string{"https://www.google.com", "https://badhost", "https://www.yahoo.co.jp/"} for result := range checkStatus(done, urls...) { if result.Error != nil { log.Infof(ctx, "error: %v", result.Error) continue } //log.Infof(ctx, "%s", formatStatus(result.Response.Status)) log.Infof(ctx, "checkStatus: status: %s", result.Response.Status) } } func testGoroutineHandler2(w http.ResponseWriter, r *http.Request) { // GAE log ctx := appengine.NewContext(r) client := urlfetch.Client(ctx) type Result struct { Error error Response *http.Response } checkStatus := func( done <-chan interface{}, urls []string, ) <-chan Result { resultChan := make(chan Result, len(urls)) wg := new(sync.WaitGroup) defer close(resultChan) for _, url := range urls { wg.Add(1) go func(url string) { defer wg.Done() log.Infof(ctx, "client.Get '%s'", url) resp, err := client.Get(url) // 関数の実行にかかる時間を遅らせる log.Infof(ctx, "sleep 3 sec") time.Sleep(3 * time.Second) select { case <-done: return case resultChan <- Result{Error: err, Response: resp}: } }(url) } wg.Wait() return resultChan } done := make(chan interface{}) defer close(done) urls := []string{"https://www.google.com", "https://badhost", "https://www.yahoo.co.jp/"} for result := range checkStatus(done, urls) { if result.Error != nil { log.Infof(ctx, "error: %v", result.Error) continue } log.Infof(ctx, "checkStatus: status: %s", result.Response.Status) } }
実行して結果を確認
GoogleAppEngineの左側からトレースを選ぶと詳細がみられる
またトレース画面の右側のログの「表示」をクリックするとログがみられる
1.test(testHandler)
トレース - 3つのURLの処理を3秒おきに実行していることがわかる
ログ
2.test_goroutine(testGoroutineHandler)
- goroutineを使っているが、URLは3つを順番に処理している
- 3つそれぞれで3秒ごとのSleepを入れているため、全体の実行時間は9秒ちょっとになっている
トレース
ログ
3.test_goroutine2(testGoroutineHandler2)
- (ほぼ)同時に3つのgoroutineを実行している
- 「並列実行はサポートしていません」と公式に書いてあるので、おそらく全く同時ではないはずだが、これ見るとほぼ同時に見える。。
- ともかく、並行で3つの処理を行うようにしたことで、全体としての処理時間が3秒ちょっとになった
トレース
ログ
相関係数について
相関係数について
下記相関係数の導出までの説明
正の相関と負の相関
- データ列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 <対象ファイル>