中野智文のブログ

データ・マエショリストのメモ

BigQuery で、ある特定の期間の日を全て列挙する

背景

BigQuery で、ある特定の期間の日を全て列挙したくなった。

方法

generate_date_array を使う。

https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#generate_date_array

例:'2018-01-01' から '2018-12-31' までの期間を array に。

SELECT
  GENERATE_DATE_ARRAY('2018-01-01', '2018-12-31') dt_list

例:'2018-01-01' から '2018-01-31' と '2018-06-01'から'2018-06-30' までの期間を array に。

WITH
  date_range AS (
  SELECT
    DATE '2018-01-01' AS start_date,
    DATE '2018-01-31' AS end_date
  UNION ALL
  SELECT
    DATE '2018-06-01' AS start_date,
    DATE '2018-06-30' AS end_date )
SELECT
  ARRAY_CONCAT_AGG(GENERATE_DATE_ARRAY(start_date, end_date)) dt_list
FROM
  date_range

例:'2018-01-01' から '2018-01-31' と '2018-01-15'から'2018-02-15' までの期間をかぶりを考慮しつつ 展開

WITH
  date_range AS (
  SELECT
    DATE '2018-01-01' AS start_date,
    DATE '2018-01-31' AS end_date
  UNION ALL
  SELECT
    DATE '2018-01-15' AS start_date,
    DATE '2018-02-15' AS end_date ),
  date_list AS (
  SELECT
    ARRAY_CONCAT_AGG(GENERATE_DATE_ARRAY(start_date, end_date)) dt_list
  FROM
    date_range )
SELECT
  DISTINCT dt
FROM
  date_list
JOIN
  UNNEST(dt_list) AS dt

例:'2018-01-01' から '2018-01-31' と '2018-01-15'から'2018-02-15' までの期間をかぶりを考慮しつつ またarrayに

WITH
  date_range AS (
  SELECT
    DATE '2018-01-01' AS start_date,
    DATE '2018-01-31' AS end_date
  UNION ALL
  SELECT
    DATE '2018-01-15' AS start_date,
    DATE '2018-02-15' AS end_date ),
  date_list AS (
  SELECT
    ARRAY_CONCAT_AGG(GENERATE_DATE_ARRAY(start_date, end_date)) dt_list
  FROM
    date_range )
SELECT
  ARRAY(
  SELECT
    DISTINCT dt
  FROM
    UNNEST(dt_list) dt) AS dt_list
FROM
  date_list

FirebaseのRTDBからFireStoreに乗り換えるためにGCPのプロジェクトを作成したときのメモ

背景

GCPのプロジェクトの中では、Firebaseのストレージは、排他的で、RTBDかFirestoreのどちらしか選べないらしい。 強制的にFirestore にする手もあるが、戻れなくなるのも嫌なので、新しくGCPのプロジェクトを作ることにした。 なお、Firebaseのプロジェクトという言葉と、GCPのプロジェクトいう言葉があって紛らわしい。

対応

普通に作った。ただ、ユーザの設定を元のGCPのプロジェクトから引き継ぎたい。継承という文字列が見えるが別のプロジェクトから継承するものではないらしい。

またrole(役割)のコピーというコマンドも見つかったりするかもしれないが、割り当てられた状態をコピーするのではなく、role(役割)そのものをコピーするものなので関係がない。

答えは、 iam-policy を入手すること。 次のコマンドで得られる。

gcloud projects get-iam-policy OLD_PROJECT_ID > iam-policy.yaml

サービスアカウントも含まれているので、editor などで、不必要な項目は消す。

次に新しいGCPプロジェクトを作成し、そのプロジェクトに読み込ませる。

gcloud projects set-iam-policy NEW_PROJECT_ID iam-policy.yaml

以上で別プロジェクトのユーザの設定が引き継がれているはずだ。

まとめ

別のGCPプロジェクトからユーザをコピーするときは gcloud projects get-iam-policygcloud projects set-iam-policy を使う。

word2vec の demo word を colaboratory で

背景

word2vec を使ってみたいが、colaboratory で試してみる方法が分からない。

方法

https://colab.research.google.com/drive/1VhlWwexI2FR0eT288Mbv_D4uWbqHo2mK

解説

demo をたぐると、次のオプションで学習しているらしい。

-cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15
fi

これらを考えると、オプションは、

word2vec.Word2Vec(corpus_file='text8', sg=0, size=200, window=8, negative=25, hs=0, sample=1e-4, workers=20, min_count=5, iter=15)

となった。

まとめ

なかなか終わらない(20分くらいかかるかも)。GPU にしても効果なし。

Cloud FunctionsとPubSubを使ってGCPの予算アラートをslackに通知

背景

GCPの予算アラートをslackに通知したいが、GCPの予算のアラートに無駄金を使わないために、GCPのサーバレスの機能を使って実現したい。

手順

  • PubSub を作ろう
  • Slack で通知のためのWeb Hook を作ろう
  • Cloud Functionsで関数を作ろう
  • GCPのお支払いで設定をしよう

PubSub

ただ名前を作るだけ。GCPに閉じているのでAPIキーなどは必要なし。

Slack で Web Hook

次のページを参考にして作る。

get.slack.help

Cloud Functions

トリガーは前述の pubsub で作ったトピックに設定にする。

基本的にはここを、

Cloud Pub/Sub Tutorial  |  Cloud Functions Documentation  |  Google Cloud

しかし、内容的(slack通知など)にはここを参照

www.topgate.co.jp

const { IncomingWebhook } = require("@slack/client");

/**
 * Background Cloud Function to be triggered by Pub/Sub.
 *
 * @param {object} event The Cloud Functions event.
 * @param {function} callback The callback function.
 */
exports.helloPubSub = (event, callback) => {
  const pubsubMessage = event.data;
  const name = pubsubMessage.data ? Buffer.from(pubsubMessage.data, 'base64').toString() : '{}';
  const data = JSON.parse( name );

  if (typeof data.costAmount === "undefined"  || typeof data.budgetAmount === "undefined" ||
      data.costAmount <= data.budgetAmount ) {
    callback();
  } else {
    
    const body = {
      attachments: [{
        text: `Price: ${name}!`,
      }]
    };
    const webhook = new IncomingWebhook('https://hooks.slack.com/services/XXXXXXX');
    webhook.send(body, (err, res) => {
      if (err) { console.error(err); }
      if (res) { console.log(res); }
      callback();
    });
  }
};

GCP でお支払い設定

次を参考に www.topgate.co.jp

pubsub のトピックに紐づける。

注意点

  • 1日ごとの累計などは分からないので、突発的に利用料が増えた場合のアラートなどには向いていない。
  • 予算超過の関係なしに通知は1時間ごとに来るのだが、そのあたりの話はどこにも書いていない。いろいろ悩ましい。
  • 見ての通り、関数内で予算を超過したかを判断する必要がある。
  • テストは jsonbase64エンコードしたものを送る必要がある。

pyplot で背景とグリッドに色を付ける

背景

google colaboratory は無料で jupyter notebook を使える環境で最高なのだが、一部微妙にカスタマイズされていて困ることがある。 例えば、pyplot の背景が、グレーになっている。

具体的には、素のjupyter notebookだと、

f:id:nakano-tomofumi:20180720195641p:plain

という感じだが、colaboratory だと

f:id:nakano-tomofumi:20180720195810p:plain

というように、背景がグレーになって、seaborn 風になってしまう。 (データが違うとかそういう話ではない)

論文とは限らないが白黒の出版物に出すのに、グレーの背景はまずい。

背景を白にする。

これは簡単で、

ax.set_facecolor("white")

とすればよい。

これが f:id:nakano-tomofumi:20180720201137p:plain

こうなる。

f:id:nakano-tomofumi:20180720201156p:plain

ただこのままだと、グリッドの線も白なので、何も見えなくなってしまう。

グリッド線に色を付ける

色は悩ましいが、真っ黒だと目立ちすぎるので出版物としては怪しいが、薄い灰色にしてみよう。

ax.grid(color='lightgray')

f:id:nakano-tomofumi:20180720201440p:plain

やっぱり気になった。完全な黒で点線にしてみよう。

ax.grid(color='black', linestyle=':')

f:id:nakano-tomofumi:20180720203049p:plain

まとめ

colaboratory の pyplot の背景を白にすることができた。また同様にグリッド線も黒にすることができる。

pyplot は eps も出力できるので、本格的に使うときはそのような機能も利用したほうが良いだろう。

SQLクエリのみでCREATE TABLEを使わず一時的な小さなテーブルを作る

背景

SQLクエリのみでCREATE TABLEを使わず一時的な小さなテーブルを作りたい時がある。

例えば、{'ios': 1, 'android': 2, 'windows': 3} みたいな小さなテーブルが一時的に欲しいことはある。 それがないために、IF(os_id=1, 'ios', IF(os_id=2, 'android', IF(os_id=3, 'windows', NULL))) AS os_name みたいな謎のクエリを書いた上で WHERE os_name is not NULL のような条件式があったり、難しい。

もし小さなテーブルがあればJOINするだけなのにと思う。ある条件にて複数行にすることは ARRAY と UNNEST を使ってできなくはないが、より可読性が落ちる。(JOINの方が可読性が高いと思っているのは私だけかもしれないが)

UNION ALL でひたすら結合する方法

例:

SELECT
  1 AS os_id, 'ios' AS os_name
UNION ALL SELECT
  2 AS os_id, 'android' AS os_name
UNION ALL SELECT
  3 AS os_id, 'windows' AS os_name

ARRAY を UNNEST する方法

例:

SELECT
  _os.id as os_id,
  _os.name as os_name
FROM (
  SELECT
    [STRUCT(1 AS id, 'ios' AS name),
    STRUCT(2 AS id, 'android' AS name),
    STRUCT(3 AS id, 'windows' AS name)] os)
JOIN
  UNNEST(os) _os

両社とも BigQuery (標準SQL)にて確認。

luigi RangeDaily にて謎のエラー

背景

luigi RangeDaily の謎のエラーについて書く。

なお、RangeDaily については下記記事を参照。 nakano-tomofumi.hatenablog.com

ValueError: invalid literal for int() with base 10: 'None'

RangeDaily のパラメータ now が None であると怒っている。しかし、デフォルトがNone なのである。謎であるが…

対応

import time
...
    RangeDaily(....
           now=int(time.time()),
           ...)

now を具体的に与える。「他は datetime.date 型とかで与えているのに、ここだけunixtime秒かよ」とか突っ込みたくなる気持ちがある。

luigi.parameter.UnknownParameterException: XxxXxxx[args=(), kwargs={'parameter': 'XXXX', 'None': datetime.date(2018, 4, 26)}]: unknown parameter None

date パラメータとかに入ってほしいのに、None というパラメータに無理やり日付を入れようとしている。

対応

    RangeDaily(....
           param_name='date')
           ...)

RangeDaily にて日付の入ってほしいパラメータを、param_name にて指定する(この例の場合は date)。 これを指定しなくてもうまくいく場合もあるんだが…