euphonictechnologies’s diary

Haskell超初心者の日記です。OCamlが好きです。

follow us in feedly

Google Cloud Datalabを使って未経験からKaggleのTitanicサブミットまで - その2 サブミットにこぎ着ける

前回までで

Google Cloud Datalabのセットアップができました。楽ちんでしたね。

サブミットまで

今回は、サブミットまでたどり着きたいのですが、サブミットするだけならテストデータの要素数分ランダムにゼロイチ並べて出せばいいのですが、それだとあれなので普通に取り組んで答えを出してみます。

その前に

Datalab落ちちゃっている場合はVMのコンソール開いて

datalab connect --zone us-west2-a --port 8081 datascience-01a

名前やゾーンは適宜変えてくださいね。

Pandasを使う

テーブルっぽいデータ構造pandasをつかいます。この手のデータ構造はどんなプログラミング言語にもありますよね。それを使って前回ダウンロードした問題ファイルを読み込みます。

import pandas as pd

train_data = pd.read_csv('train.csv')
train_data.head(4)

f:id:euphonictechnologies:20180923111918p:plain

こんな感じ。中身が見えています。中身は問題ページに書いてありますし、大体から無名からわかりますよね。わかりにくいところだと

  • Pclass: チケットのクラスです
  • SibSp, Parch: 乗船してる兄弟とか配偶者(SibSp)と、両親か子供(Parch)の数。兄弟か、配偶者か、両親か子供かはこの数字からはわからない。
  • Embarked: 乗船した場所。C = Cherbourg, Q = Queenstown, S = Southamptonです。タイタニックはイギリスからアメリカに行く途中で沈没したのでこの地名は全部イギリスの地名ですね。

残りの説明は: Titanic: Machine Learning from Disaster | Kaggle

こんな感じ。

データのゴミ取り

データにはゴミがたくさん含まれておりまして、

train_data.info()

すると

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

こんな感じで見えます。これを見るとAgeはNaNが入っている可能性があります。Ageは整数であるべきですよね。

train_data.isnull().any()

これをやってみます。isnull()はそれぞれのテーブルセル一つ一つをNaNかどうかのブールに置き換えて、anyは列ごとにTrueが一つでもあればTrueになります。

PassengerId    False
Survived       False
Pclass         False
Name           False
Sex            False
Age             True
SibSp          False
Parch          False
Ticket         False
Fare           False
Cabin           True
Embarked        True
dtype: bool

というわけなので、あってたっぽいです。Age, CabinとEmbarkedがNaNがあります。Nameとかは大丈夫でした。ゴミ取りしましょう。

Embarkedのゴミ取り

a = train_data.copy()
a["Embarked"].fillna(value='', inplace=True)
a.Embarked.value_counts()

中身をとりあえず見てみましょう。train_dataはオリジナルデータをとっておきたいので、copy()しましょう。変数名はひどいです。aはやめましょう。NaNはfillnaで空白に置き換えます。参照透明性大好き人間としてはinplaceは嫌いです。

S    644
C    168
Q     77
       2
Name: Embarked, dtype: int64

すっとNaNが2個あることがわかりました。2個ならどっちでもええやろ、ってことで

a = train_data.copy()
# seems like Embaked NaN are only 2, so map it to S (maximum)
a["Embarked"].fillna(value='', inplace=True)
a = dummify(a, 'Embarked', {'S': 0, 'C': 1, 'Q': 2, '': 0}, 'int64')

として空白をSに割り当ててダミー変数化してしまいます。港をそれぞれ{0, 1, 2}に割り当てることにしました。dummify関数は私のオリジナルのやつで

def dummify(table, col_name, mapping, astype=None):
  ret = table.copy()
  ret[col_name] = ret[col_name].map(mapping)
  if astype:
    ret[col_name] = ret[col_name].astype(astype)
  return ret

こんな感じ!単にマッピング掛けるだけかーい。

Cabinのゴミ取り

CabinはCXXXとかAXXXみたいな形のようです。なので頭だけ取り出すことにします。多分キャビン番号使うと生存率予想に使えるはずですが、難しいので無視しましょう。

a = train_data.copy()
a["Cabin"].fillna(value='', inplace=True)
a.Cabin.value_counts()

とすると

               687
B96 B98          4
G6               4
C23 C25 C27      4
D                3
              ... 
B78              1
D56              1
C62 C64          1
B41              1
Name: Cabin, Length: 148, dtype: int64

こんな感じ。ほとんど空白やないか!と言うわけで、あるやつは頭のアルファベット、無いやつはUnknownのUと言うことにしましょう、とりあえず。

a["Cabin"].fillna(value='', inplace=True)  
a.Cabin = a.Cabin.map(lambda x : x[0] if len(x) > 0 else 'U')
a = dummify(a, 'Cabin', {'A': 8, 'B': 7, 'C': 6, 'D': 5, 'E': 4, 'F': 3, 'G': 2, 'T': 1, 'U': 0}, 'int64')

頭文字取り出して整数に割り当てます。一応連続性を考慮してAから数字が減っていって空白はゼロに割り当てました。どうなんでしょうねこれ。

Ageのゴミ取り

Ageはちょっと難しいです。適当に全体の平均値なり中央値で埋めてもいいんですが、それもちょっとまずそう。てことで何か使えないかというと、名前が使えそう。名前についている敬称は年齢をある程度反映してるかもしれないです。実際に見てみましょう。

まずは、敬称をとるために全部名前を小文字に変換して、その中身で"Sir"というカラム名をつけます。

a = train_data.copy()
a.Name = a.Name.map(lambda name : name.lower())
a.loc[:,'Sir'] = a.Name.map(lambda name : 1 if 'mrs' in name else (2 if 'mr' in name else (3 if 'miss' in name else 4 if 'master' in name else 0)))
a.head(10)

としてみると、Sirに敬称、"Mrs", "Mr", "Miss"そして"Master"に応じた数字が割り振られます。無いやつは0にマップしました。 a.loc[:,'Sir']は、あたらしカラムを作るためにそうしてます。他にやり方がわかりませんでした…。 んでもって中身を見てみると、

import matplotlib.pyplot as plt

plt.style.use('ggplot')
plt.scatter(a.Age,a.Sir,color="#cc6699",alpha=0.5)
plt.show()

f:id:euphonictechnologies:20180923120112p:plain

こんな感じで分布しています。横軸が年齢、縦軸はそれぞれの敬称。敬称によって年齢の分布が変わっていることはわかります。本当だったらその要素ごとに敬称と他のフィールド要素から最もらしい年齢を類推するのがよいのでしょうが面倒なので、適当にそれぞれの敬称グループごとの平均とって、それで。

# Check age and sir
a[['Age', 'Sir']].groupby('Sir').mean()

要素を並べたリストでaを引くと、そのテーブルだけselectできます。でもってgroupbyして、meanをとります。ここら辺SQLの勘が働く人は強いですね。

Age
Sir 
0   42.666667
1   35.689922
2   32.301158
3   21.805556
4   4.525000

いい感じに平均ばらけてるので、これでいきましょう。本当はMasterとか眉唾物ですね、

 = train_data.copy()
a.Name = a.Name.map(lambda name : name.lower())
a.loc[:,'Sir'] = a.Name.map(lambda name : 1 if 'mrs' in name else (2 if 'mr' in name else (3 if 'miss' in name else 4 if 'master' in name else 0)))
a.Age = a.Age.where(a.Age == a.Age, a.Sir.map({0: 43, 1: 36, 2: 32, 3: 22, 4: 5})).astype('int64')  
a.head(10)

このwhereがNaNでは失敗するので(NaN!=NaN)、そのマッチングが失敗してFalseになる部分ではマップを使ってSirによって当てはめる数字を決めて、入れ込みます。

あとはダミー変数化

あとは適当にダミー変数化していらないコラムをそぎ落とします。

まずは性別。

a = dummify(a, 'Sex', {'male': 1, 'female': 0}, 'int64')

次に、落とすカラム

a = a.drop('Ticket', axis=1).drop('Name', axis=1)

2つ落としちまいましょう。

SibSpとParch何かに使えない?

この二つは全部足すと家族の人数になります。詳しい分析は置いておいて、家族の人数ってことにしましょう。

a.loc[:, 'FamilySize'] = a.SibSp + a.Parch + 1

自分を含めた家族の人数です。多分ですが、家族が多いほど死にやすいはず。兄弟と両親のカラムはFamilySizeに置き換えたいので、FamilySizeを追加して、他二つは落としましょう。

a = a.drop('SibSp', axis=1).drop('Parch', axis=1)

テーブルのお掃除をまとめます

というわけで、ここまでの作業を全部一つの関数にまとめます。こんな感じ。

def dummify(table, col_name, mapping, astype=None):
  ret = table.copy()
  ret[col_name] = ret[col_name].map(mapping)
  if astype:
    ret[col_name] = ret[col_name].astype(astype)
  return ret

def get_normalized_dataset(train_data):
  a = train_data.copy()

  # seems like Embaked NaN are only 2, so map it to S (maximum)
  a["Embarked"].fillna(value='', inplace=True)
  a = dummify(a, 'Embarked', {'S': 0, 'C': 1, 'Q': 2, '': 0}, 'int64')

  a["Cabin"].fillna(value='', inplace=True)    
  a.Cabin = a.Cabin.map(lambda x : x[0] if len(x) > 0 else 'U')
  a = dummify(a, 'Cabin', {'A': 8, 'B': 7, 'C': 6, 'D': 5, 'E': 4, 'F': 3, 'G': 2, 'T': 1, 'U': 0}, 'int64')

  a.Name = a.Name.map(lambda name : name.lower())
  a.loc[:,'Sir'] = a.Name.map(lambda name : 1 if 'mrs' in name else (2 if 'mr' in name else (3 if 'miss' in name else 4 if 'master' in name else 0)))
  a.Age = a.Age.where(a.Age == a.Age, a.Sir.map({0: 43, 1: 36, 2: 32, 3: 22, 4: 5})).astype('int64')
    
  a = dummify(a, 'Sex', {'male': 1, 'female': 0}, 'int64')    

  a.loc[:, 'FamilySize'] = a.SibSp + a.Parch + 1
  
  a = a.drop('Ticket', axis=1).drop('Name', axis=1)
  a = a.drop('SibSp', axis=1).drop('Parch', axis=1)

  return a

この関数にpandasテーブルを渡すと結果が返ってきます。

train_data = pd.read_csv('train.csv')
train_data.head(4)
a = get_normalized_dataset(train_data)
a.head(10)

こんな感じ。

f:id:euphonictechnologies:20180923132429p:plain

大分減っちゃいましたね。ここで全部数値データになっていることを確認しておきます。

学習するんや

学習しましょう。こっから楽しいところですね。LightGBMを使います。決定木をたくさん集めるアンサンブル学習を勾配ブースティングを使ってうまいことやってくれるらしいです。中身はまあ、あれです。使ってみましょう。

とりあえず使う

from sklearn.model_selection import train_test_split

aa = pd.get_dummies(a)
x_train, x_test, y_train, y_test = train_test_split(
    aa.values[:, 2:], aa.values[:, 1], test_size=0.33, random_state=201612
)

とりあえずpandasのget_dummies関数をかましています。多分ちゃんときれいなテーブルになっていたらいらないやつです。ここでtrain_test_split関数を使ってtrainデータを訓練用と検証用のデータに分けます。レコード数を2:1に分割します。aa.values[:, 2:]は生存結果の右側、使うデータ。values[:, 1]は生存結果、つまり答え。

そうするとx/y_trainは訓練用のフィールドがたくさん並んだリスト、x/y_testは答えの入った単なるバイナリ値のリストです。名前はもうちょっと考えた方がいいですね。

import lightgbm as lgb

gbm = lgb.LGBMClassifier(objective='binary',
                        num_leaves=22,
                        learning_rate=0.1,
                        min_child_samples=10,
                        n_estimators=100)

gbm.fit(x_train, y_train,
        eval_set=[(x_test, y_test)],
        eval_metric='multi_logloss',
        early_stopping_rounds=10)

y_pred = gbm.predict(x_test, num_iteration=gbm.best_iteration_)

とりあえずこれでLightGBMが動きます。うれしいですね。

最後の行は検証用訓練データを使って検証用データへの答えを出力します。検証用データの実際の答えと比べてみます。

# eval
print('Accuracy is:', 1 - abs(y_test - y_pred).sum() / len(y_test))

これで、正解率を計算すると

Accuracy is: 0.840677966101695

84点ですね。まあまあでしょうか。とにもかくにも与えられた訓練用データ全体を訓練用と検証用に勝手に分けて点数をつけているだけの話です。早速サブミットしてkaggleに点数をつけてもらいましょう。

テストデータを評価する

test_data = pd.read_csv('test.csv')
test_data.head(4)
b = get_normalized_dataset(test_data)
b.head(10)

テストデータを読み込んでさっきのデータ調整関数に通して予測できる形にします。

pred_for_submit = gbm.predict(b.values[:, 1:], num_iteration=gbm.best_iteration_)

このgbmは訓練データを読み込ませてfitしたやつです。これで予測して、結果を出します。

array([0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 1., 0., 1., 1., 0.,
       0., 1., 1., 0., 1., 1., 0., 1., 0., 1., 0., 0., 0., 0., 0., 1., 1.,
       0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 1., 0., 1., 1., 1.,
       0., 1., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 1., 1., 0.,
       0., 1., 1., 0., 0., 1., 1., 0., 0., 1., 0., 1., 1., 0., 0., 0., 0.,
       0., 1., 0., 0., 1., 1., 0., 1., 0., 0., 0., 1., 0., 1., 0., 1., 0.,
       0., 0., 1., 0., 0., 0., 0., 1., 0., 1., 1., 1., 1., 0., 0., 1., 0.,
       1., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 1., 0., 1., 0., 0.,
       1., 0., 0., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 1., 0., 1., 0.,
       0., 1., 0., 0., 0., 1., 1., 0., 1., 1., 0., 1., 1., 0., 1., 0., 1.,
       0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 1., 0., 1., 1., 1., 0., 1.,
       0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 1., 0., 1., 0., 1.,
       0., 1., 0., 1., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
       1., 1., 1., 1., 0., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 0.,
       0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
       1., 1., 0., 1., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 1., 0.,
       0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1.,
       0., 1., 1., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 1., 1.,
       1., 1., 0., 1., 0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 0., 0., 1.,
       0., 0., 0., 1., 1., 1., 0., 1., 0., 1., 1., 0., 0., 0., 1., 0., 1.,
       0., 0., 1., 0., 1., 1., 0., 1., 0., 0., 0., 1., 0., 0., 1., 0., 0.,
       1., 1., 0., 0., 0., 0., 0., 0., 1., 1., 0., 1., 0., 0., 0., 0., 1.,
       1., 1., 0., 0., 1., 0., 1., 0., 0., 1., 0., 1., 0., 0., 1., 0., 0.,
       1., 1., 1., 1., 0., 0., 1., 0., 0., 1.])

こんなのが得られます。確かに1か0に分類されていますね。

サブミットしましょう

答えがpred_for_submitに格納されています。というわけで、サクッとサブミットしてしまいましょう。まず提出用のcsvに書き出します。

import csv
with open("predict_result_data.csv", "w") as f:
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow(["PassengerId", "Survived"])
    for pid, survived in zip(test_data.values[:,0].astype(int), pred_for_submit.astype(int)):
        writer.writerow([pid, survived])
    print('Written.\n')

でもって、これをkaggleのCIツールで提出します。

%%bash
kaggle competitions submit -f predict_result_data.csv -m 'ここに提出データの説明を割と詳しくつけましょう' -c titanic

f:id:euphonictechnologies:20180923140742p:plain

という感じで、サブミットした結果。どうなったかというと…

f:id:euphonictechnologies:20180923140952p:plain

うーん。あんまり点数高くないですね。

というわけで、サブミットまでできました。

リーダーボードのランキングを見てみると上の方は全部1.00です。つまり、全問正解というわけですね。ちょっと萎えますね。

これでひとまず全部一回通せたので他のコンペティションにも取り組んでみたいと思いました。おしまい。

全部のコード

%%bash
pip install kaggle lightgbm
%%bash
echo '{"username":"my_user_name","key":"key_key_key"}' > ~/.kaggle/kaggle.json
%%bash
chmod 600 ~/.kaggle/kaggle.json
%%bash
kaggle competitions download -c titanic
from google.datalab import Context
import google.datalab.bigquery as bq
import google.datalab.storage as storage
import pandas as pd
from io import BytesIO as StringIO
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV
import sklearn.preprocessing as sp

train_data = pd.read_csv('train.csv')
train_data.head(4)

def dummify(table, col_name, mapping, astype=None):
  ret = table.copy()
  ret[col_name] = ret[col_name].map(mapping)
  if astype:
    ret[col_name] = ret[col_name].astype(astype)
  return ret

def get_normalized_dataset(train_data):
  a = train_data.copy()

  # seems like Embaked NaN are only 2, so map it to S (maximum)
  a["Embarked"].fillna(value='', inplace=True)
  a = dummify(a, 'Embarked', {'S': 0, 'C': 1, 'Q': 2, '': 0}, 'int64')

  a["Cabin"].fillna(value='', inplace=True)    
  a.Cabin = a.Cabin.map(lambda x : x[0] if len(x) > 0 else 'U')
  a = dummify(a, 'Cabin', {'A': 8, 'B': 7, 'C': 6, 'D': 5, 'E': 4, 'F': 3, 'G': 2, 'T': 1, 'U': 0}, 'int64')

  a.Name = a.Name.map(lambda name : name.lower())
  a.loc[:,'Sir'] = a.Name.map(lambda name : 1 if 'mrs' in name else (2 if 'mr' in name else (3 if 'miss' in name else 4 if 'master' in name else 0)))
  a.Age = a.Age.where(a.Age == a.Age, a.Sir.map({0: 43, 1: 36, 2: 32, 3: 22, 4: 5})).astype('int64')
    
  a = dummify(a, 'Sex', {'male': 1, 'female': 0}, 'int64')    

  a.loc[:, 'FamilySize'] = a.SibSp + a.Parch + 1
  
  a = a.drop('Ticket', axis=1).drop('Name', axis=1)
  a = a.drop('SibSp', axis=1).drop('Parch', axis=1)

  return a

a = get_normalized_dataset(train_data)
a.head(10)

# Check age and sir
a[['Age', 'Sir']].groupby('Sir').mean()

plt.style.use('ggplot')
plt.scatter(a.Age,a.Sir,color="#cc6699",alpha=0.5)
plt.show()

a.Cabin.value_counts()

aa = pd.get_dummies(a)
x_train, x_test, y_train, y_test = train_test_split(
    aa.values[:, 2:], aa.values[:, 1], test_size=0.33, random_state=201612
)

gbm = lgb.LGBMClassifier(objective='binary',
                        num_leaves=22,
                        learning_rate=0.1,
                        min_child_samples=10,
                        n_estimators=30)

gbm.fit(x_train, y_train,
        eval_set=[(x_test, y_test)],
        eval_metric='multi_logloss',
        early_stopping_rounds=10)

y_pred = gbm.predict(x_test, num_iteration=gbm.best_iteration_)

# eval
print('Accuracy is:', 1 - abs(y_test - y_pred).sum() / len(y_test))

# feature importances
print('Feature importances:', list(gbm.feature_importances_))

test_data = pd.read_csv('test.csv')
test_data.head(4)
b = get_normalized_dataset(test_data)
b.head(10)

pred_for_submit = gbm.predict(b.values[:, 1:], num_iteration=gbm.best_iteration_)

import csv
with open("predict_result_data.csv", "w") as f:
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow(["PassengerId", "Survived"])
    for pid, survived in zip(test_data.values[:,0].astype(int), pred_for_submit.astype(int)):
        writer.writerow([pid, survived])
    print('Written.\n')
%%bash
kaggle competitions submit -f predict_result_data.csv -m 'LightBGM, adjusted3' -c titanic

Google Cloud Datalabを使って未経験からKaggleのTitanicサブミットまで - その1 データラボを使えるようにする

Kaggleが、やりたいです

もうプログラマーである以上はいい加減Kaggleから逃げられないご時世になってきました。諦めてデータサイエンス覚えましょう。さもなくばクビです。

この記事はこんな人向けです

  • プログラミングはできる
  • Pythonは触ったことあるけどpythonのelse-if文の書き方忘れたなあ
  • Google Cloud Platformは触ったことねえ
  • Kaggleのアカウントだけ作った

なんでGoogle Cloud Platform?

セットアップが超簡単です。30分もあれば導入完了です。そのうち叩くコマンドは4つくらいです。簡単だね!

そして、BigQueryが使えます。今回は使わなかったけど。BigQuery使えるようになりたいですよね、deathよね。

あとAWS飽きた。

Kaggleのアカウント作りましょう

メールアドレスで登録するだけです。

Google Cloud Platformのアカウント有効にしましょう。

しましょう。

KaggleのTitanic問題に登録しましょう

f:id:euphonictechnologies:20180917121410p:plain

これです。

Google Cloud Platformで作業しましょう

プロジェクトを選ぶ

Google Cloud Platformのダッシュボードに移動して、プロジェクトを新規作成しましょう。

f:id:euphonictechnologies:20180917153924p:plain

左上に書いてあるやつが今ダッシュボードで作業中のプロジェクトです。名前をわかりやすくつけましょう。

ビリングを設定する

GCEは金かかりまっせ。支払い方法を設定しておきましょう。

支払い方法の追加、削除、更新  |  Cloud Billing のドキュメント  |  Google Cloud

APIをイネーブルする

APIをイネーブルします。APIを管理する画面を開いて、

f:id:euphonictechnologies:20180917154049p:plain

そこから追加画面を開きます。

f:id:euphonictechnologies:20180917154337p:plain

そこからAPIを選択できます。こんな感じ。

f:id:euphonictechnologies:20180917154443p:plain

最低限必要なのは

  • Compute Engine API

です。名前の通り必須です。他にも後々使いそうなのはBigQueryとかBigTableとか。いつでもこの画面で追加できます。必要な操作をやるとAPIが必要な場合はそういう風に怒ってくれるので必要になり次第API追加していきましょう。

GCE準備できたのでDatalabをはじめる。

f:id:euphonictechnologies:20180917154753p:plain

ここからコンソールを開きます。多分開いているページの下半分がコンソールになります。

f:id:euphonictechnologies:20180917155130p:plain

こんな感じ。

Datalabインスタンスを作成する。

簡単です。こんだけ。

datalab create --disk-size-gb 10 --machine-type f1-micro datascience-01a
  • --disk-size-gb : このVMインスタンスにくっつけるディスクのサイズです。20GBのディスクが自動でくっつくのでデータを保管する作業場所のサイズです。30GBまでならAlways Freeなはずなので、差し引き10GBを割り当てています。本当に無料になるかどうかは知りません。
  • --machine-type : f1-microを指定しています。これもAlways Free Eligibleなはず。本当に無料になるかどうかは(略)

datascience-01aはVMインスタンスの名前です。適当につけましょう。

途中でインスタンスをデプロイする場所を聞かれます。お金持ちは近い場所asiaとか、で、私のような貧乏人はus-のどこかAlways Free Eligibleな場所にしましょう。本当に(略)

最後に作ったDatalabを保護するためのSSHトンネリング用のパスワードを聞かれます。忘れないやつをつけましょう。

そんだけ!

Datalabでの作業を始める

開く

まずは開かないと話になりません。開きましょう。 2つ方法があって

  1. 表示されたURLをコピーしてブラウザにぶち込む
  2. ウェブプレビューボタンを活用する

私は最初1でやりました。それでいいと思いますが、2を覚えておくと便利です。

f:id:euphonictechnologies:20180917155830p:plain

このボタンを押して、開くと

f:id:euphonictechnologies:20180917155854p:plain

モーダルが出るので、8081に変更して開く。

f:id:euphonictechnologies:20180917155927p:plain

すると

f:id:euphonictechnologies:20180917160013p:plain

データラボじゃー!

フォルダとノートブックを作る

f:id:euphonictechnologies:20180917160259p:plain

この二つのボタンを使って作業場をこしらえます。私は既存のnotebooksフォルダの下にKaggleフォルダを作って、さらに問題フォルダを掘ってそこにノートブックを置きました。

f:id:euphonictechnologies:20180917160358p:plain

フォルダ構成は自由です。憲法にも保障されている基本的人権の一つです。適当にやりましょう。

フォルダはボタンを押したあとしばらくしてできあがります。反応無いからつって連打するとたくさんできます。我慢しましょう。フォルダは無名で作成されるのでRenameしましょう。

ノートブックを作ると空っぽのノートができるはずです。開きましょう。

ノートブックを開く

まあ、開けますよね。

問題を取り込む

kaggleツールをインストール

いろいろなやり方があると思いますが、私はクソめんどくさがりなのでノートブックから取り込みました。kaggleは専用ツールを公開していて、そいつを使うと問題データを簡単に取り込めます。kaggleの問題ページにこんな感じに表示されていると思います。

f:id:euphonictechnologies:20180917160738p:plain

これを使います。まずはツールをインストールしましょう。

f:id:euphonictechnologies:20180917160835p:plain

こんな感じでノート上でシェルコマンドが実行できます。

%%bash
pip install kaggle

実行しましょう。普通のpipコマンドが使えるので使いたいライブラリはこんな感じでどんどん足せます。

んで、このツールを使うにはツールが認証を突破できるようにしなければいけないので、kaggleのAPIトークンを発行しましょう。

kaggle APIトークンの取得

kaggleのページの右上に表示されているあなたのアバターの

f:id:euphonictechnologies:20180917161152p:plain

でアカウントページを開いて、

f:id:euphonictechnologies:20180917161253p:plain

ここからAPIトークンを取得します。トークンはユーザー名と暗号化トークンのふたつからなる辞書です。それをコピペしてDatalab上にファイルとしておいときます。

%%bash
echo '{"username":"oreno_user_name","key":"cdac546cacd78dc9ac7897dc....."}' > ~/.kaggle/kaggle.json

こんな感じ。

ファイルをダウンロード!

するとkaggleコマンドが使えるようになっているので、問題ページのやつをコピペしてシェルコマンドとして実行しましょう。

%%bash
kaggle competitions download -c titanic

これでノートブックと同じフォルダにファイルが置かれているはずです。

f:id:euphonictechnologies:20180917161549p:plain

こんな感じ!

中身を確認

少し中身をのぞいてみましょう。実際にこれから作業していくのはトレーニングデータtrain.csvです。

from google.datalab import Context
import google.datalab.bigquery as bq
import google.datalab.storage as storage
import pandas as pd
try:
  from StringIO import StringIO
except ImportError:
  from io import BytesIO as StringIO

train_data = pd.read_csv('train.csv')
train_data.head(4)

こんな感じでデータ、見えました?

f:id:euphonictechnologies:20180917161856p:plain

今回はここまで

長くなってきたのでここら辺で。次回は実際に読み込んだデータで問題に取り組みます。

Rails - administrateはどうやってナビゲーションバーを作っているのか

ごく

私的なメモです

ナビゲーションバーはどこへ?

./vendor/bundle/ruby/2.4.0/gems/administrate-0.8.1/app/views/administrate/application/_navigation.html.erb

どんなコード?

administrate/_navigation.html.erb at master · thoughtbot/administrate · GitHub

<nav class="navigation" role="navigation">
  <% Administrate::Namespace.new(namespace).resources.each do |resource| %>
    <%= link_to(
      display_resource_name(resource),
      [namespace, resource.path],
      class: "navigation__link navigation__link--#{nav_link_state(resource)}"
    ) %>
  <% end %>
</nav>

Administrate::Namespace.new(namespace).resourcesの中身は?

./vendor/bundle/ruby/2.4.0/gems/administrate-0.8.1/lib/administrate/namespace.rb

どんなコード?

administrate/namespace.rb at master · thoughtbot/administrate · GitHub

module Administrate
  class Namespace
...
    def routes
      @routes ||= all_routes.select do |controller, _action|
        controller.starts_with?(namespace.to_s)
      end.map do |controller, action|
        [controller.gsub(/^#{namespace}\//, ""), action]
      end
    end

で、resourcesroutesをResourcesオブジェクトに仕立てたもの。

つまり?

routes.rbに書いてあって名前が"admin"ではじまるコントローラのくっついているroutesをナビゲーションバーに表示するよ!

routes.rbから消したらエラーになるんだけど?

普通はroutes.rbのnamespace admin以下のresourcesを消せばそれはナビゲーションバーから見えなくなる。 ただ、rootが設定されている場合はそれがadminではじまるコントローラ扱いになってroutesに残ってresources消したからshowかなんかが見えなくなってpathとれなくてエラー吐く。

rootもあわせて変更すること!