OmniAICreator/Qiita-1.07M huggingface dataset:
GCP - Cloud StorageとBigQueryのリージョン変更を自動化してみた

# 動機
GCPの魅力の一つに（料金は別にして）データのコピー、移動の手軽さ、簡単さがあります。
これらはBigQueryとCloud Storage, Cloud StorageとFirestoreなどサービス間の連携に関しても同様で一度味をシメたらなかなか抜け出せなくなる魅力があります。

私が所属するプロジェクトでは業務システムをGCPのサービスを使って構築しているのですが、データストレージを東京リージョンからUSリージョンへ移行しました。
先に述べた通り、GCPのサービス間連携は非常に使い勝手がよいのですが、リージョン間連携となると勝手が違ったため、私が担当したのはCloud Storageのリージョン移行とBigQueryのリージョン移行について知見を共有したいと思います。

# Cloud Storageのデータ移行
Cloud Storge(GCS)とBigQueryは双子の兄弟のような硬い絆で結ばれている関係です。
GCPでBigQueryを利用している方の大半はGCSも利用していると思います。
実は今回のリージョン移行に関してもBigQuery自身がリージョン間を跨いだクエリ生成ができなかったためBigQueryのリージョンを変更したいという要件が先にありました。
Cloud StorageとBigQueryは同一リージョンでなければ連携できないため、Cloud Storageもお引っ越しをすることになりました。

## 概要

* 1. 転送先バケットを作成

転送先バケットを作成する

```
gsutil mb -p {project} -c STANDARD -l {location} -b on gs://{bucket_name}/
```

Storage管理者へサービスアカウントを追加

```
gsutil iam ch {member_type}:{member_name}:{iam_role} gs://{bucket_name}
```

* 2. 転送先バケットへオブジェクトを転送

オブジェクトを転送する

```
gsutil -m cp gs://{bucket_name}/\* gs://{destination_bucket_name}/
```

# BigQueryのデータ移行
BigQueryのデータセットのデータの移行はリージョンをまたぐ移行には制約がありました。
詳しくは「[データのロケーションと転送](https://cloud.google.com/bigquery-transfer/docs/locations?hl=ja)」を確認していただきたいのですが、今回の要件ではGCPが提供するリージョン間の転送サービスが利用できなかったため自作することになりました。

## 概要

* 1. 転送先データセットの作成

```
bq --location={location} mk \
            --dataset \
            --default_table_expiration {integer1} \
            {project}:{dataset}
```

* 2. 転送先テーブルの作成

```
mk = 'bq mk -t --expiration=0 --schema="{schema}"  \
                --time_partitioning_expiration="{time_partitioning_expiration}" \
                --description="{description}" \
                {project}:{dataset}.{table}
```

* 3 転送元データをCloud Storageへエクスポート

```
bq --location={location} extract \
                --destination_format CSV \
                --compression GZIP \
                --print_header=true \
                {project}:{dataset}.{table} \
                gs://{bucket}/{dataset}/{table}/*.csv.gz
```

* 4 Cloud Storageのデータを転送

```
gsutil -m cp -r gs://{source_bucket}/\* gs://{destination_bucket}/
```

* 5 転送先テーブルへデータをインポート

```
bq --location={location} load \
              --skip_leading_rows=1 \
              --source_format=CSV \
              {project}:{dataset}.{table} \
              {path_to_source} \
              {schema}
```

# Cloud StorageとBigQueryのリージョン移行自動化

上記の手順を自動化したものをGithubにあげてあります。
設定ファイルに設定項目を設定することで移行を自動化してくれます。（詳しくはREADMEを参照してくだいさい）
[Cloud StorageとBigQueryのデータ移行自動化スクリプト](https://github.com/kirakirapq/gcp-samples/tree/master/transfer-data)
PyTorchのDataLoaderでBrokenPipeErrorを回避する方法メモ

PyTorchのDataLoaderは，マルチプロセスでデータロードが出来る仕組みが備わっている．Windowsでそれを利用しようとしたところ，[こちら](https://github.com/pytorch/pytorch/issues/31465)と同じようなエラーを吐いて動作しなかった．そこで色々調べて解決を行ったため，その方法をメモする．

- 動作環境
    - OS: Windows10 Home(バージョン 2004)
    - Python: 3.7.3
    - PyTorch: 1.3.1


#DataLoaderのマルチプロセス
[公式のDocs](https://pytorch.org/docs/1.3.1/data.html)から引用すると
>A `DataLoader` uses single-process data loading by default.

>Within a Python process, the Global Interpreter Lock (GIL) prevents true fully parallelizing Python code across threads. To avoid blocking computation code with data loading, PyTorch provides an easy switch to perform multi-process data loading by simply setting the argument `num_workers` to a positive integer.

とのこと．非常にざっくり言うと，DataLoaderクラスの`num_workers`という変数の値を1以上にすれば，データの読み込みを並列化できる，ということである．
#BrokenPipeError
そこで，`num_workers`を1以上の値に設定して動かしたところ，

```
BrokenPipeError: [Errno 32] Broken pipe
```
とエラーを吐いて動作しなかった．
[PytorchのDatasetをDataLoaderで並列に読み込みたいときのエラー（Windows）](https://qiita.com/fmfm_mdk/items/726aeacf8f6efe10009e)を参考に，`Dataset`の定義を別のファイルで行っても同様のエラーが発生した．
#解決法
[こちら](https://discourse.pymc.io/t/multiprocessing-windows-10-brokenpipeerror-errno-32-broken-pipe/2259)を参考にすると，どうやらWindowsでマルチプロセスを行う場合，`if __name__ == "__main__"`内でマルチプロセスを行う関数を実行しなければならないとのこと．

修正前

```train.py
from torch.utils.data import DataLoader
from dataloader import MyDataset #作成したdataset

def train():
    dataset = MyDataset()
    train_loader = DataLoader(dataset, num_workers=2, shuffle=True,
                              batch_size=4,
                              pin_memory=True,
                              drop_last=True)

    for batch in train_loader:
        #do some process...

if __name__ == "__main__":
    train()
```

修正後

```train.py
from torch.utils.data import DataLoader
from dataloader import MyDataset #作成したdataset

def train(train_loader):
    for batch in train_loader:
        #do some process...

if __name__ == "__main__":
    #dataset, DataLoaderを移動
    dataset = MyDataset()
    train_loader = DataLoader(dataset, num_workers=2, shuffle=True,
                              batch_size=4,
                              pin_memory=True,
                              drop_last=True)

    train(train_loader)
```

DataLoaderの場合，インスタンスの生成を`if __name__ == "__main__"`内で行っていれば，データの読み込み自体は別の関数内で実行してもマルチプロセスは動作した．
#まとめ
Windows環境でDataLoaderの並列化するためのメモを記した．深層学習周りでは，Windowsだと動作しないまたは少し工夫をしないと行けないといった作業が多く大変である．なので，Windows周りで発生するエラーは定期的に記事にしてまとめていきたい所存
リストを<a>で囲ったらGoogle Lighthouseに叱られた

## 問題

自身のブログにページネーションを次のように実装しました。 

```sample.html
<!-- 実装例は簡略化しています -->
<ul>
  <a>
    <li>1ページ目</li>
  </a>
  <a>
    <li>2ページ目</li>
  </a>
  <a>
    <li>3ページ目</li>
  </a>
</ul>
```

するとサイト評価をしてくれるGoogle Lighthouseから、アクセシビリティの観点で注意されました。

* [Lists do not contain only \<li\> elements and script supporting elements\(\<script\> and \<template\>\)](https://web.dev/list/?utm_source=lighthouse&utm_medium=lr)

いわく、

**「リスト（\<ul\>や\<ol\>）は\<li\>しか含んじゃいけませんよ」**

つまり、\<a\>で\<li\>を囲うことはそのルールに反していました。

## 対応

次のようにcssを使うことで回避することができました。

```sample.css
li a {
  display: block;
}
```

```sample.html
<!-- 実装例は簡略化しています -->
<ul>
  <li>
    <a>1ページ目</a>
  </li>
  <li>
    <a>2ページ目</a>
  </li>
  <li>
    <a>3ページ目</a>
  </li>
</ul>
```

## 参考

* 参考にした記事
  * [要素全体をリンクにする方法](https://qiita.com/a01sa01to/items/bfca8ccdc5ca4798e9d5)
* 実装例（自身のブログ）
  * [koboriakira.com](https://koboriakira.com)
[Ansible]playbookの解説書

#はじめに
[Ansible](https://qiita.com/ren0826jam/items/4166eadf6352fbe34ccb)について記事を書いたが、playbookの詳細(書き方など)を書いていなかったのでここで解説します。

## playbookとは？
Ansibleに実行させることを記述することで、様々な処理を行える。
playbookには冪等性があるため、何回同じファイルを実行しても同じ結果が得られます。

## 書き方・手順・解説
まず、基本的なフォーマットは以下の通りです。
Playbookではまず「hosts:」で対象とするホストもしくはグループを指定し、そのホストに対して実行する「タスク」や「ハンドラ」を続けて記述していく。
※使えるモジュールは[こちら](https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html)から

```playbook.yml

- hosts: ＜対象とするホスト/グループ＞
  var:
    ＜変数名1＞: ＜値＞
    ＜変数名2＞: ＜値＞
      :
      :
  remote_user: ＜処理を実行するユーザー名＞
  sudo: yes
  tasks:
    ＜タスク1＞
    ＜タスク2＞
      :
      :
  handlers:
    ＜ハンドラ1＞
    ＜ハンドラ2＞
      :
      :
```

###タスクについて
Ansibleでは、そのホストに対する処理を「タスク」という単位で管理する。通常、1つのタスクには以下のように1つのモジュールを指定する。
※タスクを複数記述した場合、それらは記述された順番に実行される。

```yml

- name: ＜タスクの説明＞
    ＜モジュール名＞: ＜実行時に与えるパラメータ＞

# 「yum」モジュールを使って「httpd」パッケージをインストールする
- name: install httpd
    yum: name=httpd state=present

# 「command」モジュールを使えば任意のコマンドを実行
- name: disable selinux
    command: /sbin/setenforce 0
```

###ハンドラ
「ハンドラ」は特定のタスクの実行後、あらかじめ指定しておいた処理を実行するための仕組みだ。該当のタスクで「notify」項目を設定することで、そのタスクの実行後にnotify項目で指定したハンドラが実行される。タスクと同様、1つのハンドラには1つのモジュールを指定する。

```yml

- name: ＜ハンドラの説明＞
    ＜モジュール名＞: ＜実行時に与えるパラメータ＞

# 「template」モジュールを使ってApache HTTP Server（httpd）の設定ファイルを作成するタスクと、その設定の変更後にhttpdを再起動する
tasks:
  - name: install WordPress configuration file
    template: src=wordpress.conf dest=/etc/httpd/conf.d/wordpress.conf
    notify:
      - restart httpd
handlers:
  - name: restart httpd
    service: name=httpd state=restarted
```

playbookの実行は以下のコマンドを実行する。

```

$ ansible-playbook ＜実行したいPlaybookファイル＞
```

##参考
[エージェントレスでシンプルな構成管理ツール「Ansible」入門](https://knowledge.sakura.ad.jp/3124/)

[Ansible を利用したシステム構成情報の自動取得](https://www.softek.co.jp/SID/blog/archive/entry/20200423.html)

[Ansibleを効果的に使うのに欠かせないPlaybookの基本的な書き方まとめ(1/2)](https://www.atmarkit.co.jp/ait/articles/1607/26/news013.html)

[Ansibleを効果的に使うのに欠かせないPlaybookの基本的な書き方まとめ(2/2)](https://www.atmarkit.co.jp/ait/articles/1607/26/news013_2.html)
【Android Studio】予測変換の際に大文字と小文字を区別させない方法

Android Studioのデフォルト設定では、大文字と小文字をはっきりと区別して予測一覧を出してきます。

　例）変数「Number」を入力するために「n」と打っても予測に出てこない。

個人的には大文字と小文字の違いを無視して予測してほしいので、Android Studioの設定を変更することにしました。

## 設定変更方法

**※これは `Android Studio 3.6.3` の設定方法です。バージョンによって違う可能性があります。**

(1) File > Setting を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201882/b26574b7-12c5-be04-ec2f-9d289497fca1.png)

(2) Editor > General > Code Completion を開きます。

(3) 上の方にある `Match case` のチェックを外します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201882/ccb987ba-b907-8dc9-6fd8-49494c4f33e8.png)

これでOKです！

---

雑談ですが、大文字／小文字のこと、「case」で表すんですね。

調べてみたら、英語で
　　大文字：upper case
　　小文字：lower case
と言うそうです。ちょっと勉強になりました:grinning:
Ansibleが何なのか知りたいっっ！！！！！！

# Ansibleとは？
Red Hat社が、サポートを行なっていて、オープンソースプロジェクトで開発されているPython製の構成管理ツール(※1)です。
YAML形式で書かれた設定ファイルに従って、サーバやネットワーク機器などの設定を自動的に実行できます。
他にも、アプリケーションのデプロイやオーケストレーション(※2)などにも利用できます。

##メリット
- Chef等他の構成管理ツールでは、独自記述言語を採用しているため学習コストが高いが、AnsibleではYAMLを使用しているので導入が容易。

- モジュール(※3)による拡張がしやすい。利用できるモジュールは[公式サイト](https://docs.ansible.com/ansible/latest/modules/modules_by_category.html)で確認できる

- 既存の資産（シェルスクリプト）も流用できるので、スモールスタート(※4)できる。

##デメリット
- 他の構成管理ツールと比較すると、複雑な処理を表現しにくいという面があり、「多段の条件分岐」「反復構造」を記述する場合には、複雑な表記になりやすい。

##Ansible導入手順
0.もし上手くいかない場合は[公式](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#from-pip)を確認する。

1.以下のコマンドを実行します。

```

$ brew install ansible
```

2.インストール後、ansibleコマンドで動作確認します。
以下の場合、-mでモジュールを指定し、-aでモジュールのパラメータを指定しています。
※初回はインベントリファイルがないと警告が出ますが無視しても良い

```

$ ansible localhost -m shell -a 'hostname'
```
3.以下のコマンドを実行し、インベントリファイルを作成する。

```

$ sudo mkdir /etc/ansible/

$ cd /etc/ansible/
```

4.インベントリファイルを記述する。
[文字列]でグループを定義し、その下に管理対象のホストをIPアドレスかホスト名で記述し、その後ろにオプションを記述します。



```ruby

$ sudo vi hosts

#=>
[localhost]
127.0.0.1 ansible_connection=local

[web]
192.168.N.N
```

5.playbookを作成する。
詳しくは[こちら](https://qiita.com/ren0826jam/items/5b3390da5aa8e5ff8378)を参照してください。
※YAML形式で記述するため、インデントに注意する。

```

$ sudo vi playbook.yml
```

```playbook.yml

- hosts: ＜対象とするホスト/グループ＞
  var:
    ＜変数名1＞: ＜値＞
    ＜変数名2＞: ＜値＞
      :
      :
  remote_user: ＜処理を実行するユーザー名＞
  sudo: yes
  tasks:
    ＜タスク1＞
    ＜タスク2＞
      :
      :
  handlers:
    ＜ハンドラ1＞
    ＜ハンドラ2＞
      :
      :
```

6.ansible-playbookコマンドを実行します。
-iでインベントリを記述したファイルを指定し、-uでユーザ、-kでコマンド実行時にパスワード入力プロンプトを起動します。

```

$ ansible-playbook -i hosts -u root -k playbook.yml
```

##わからない単語一覧
※1 構成管理ツール
サーバとかソフトウェアとか何らかの構成を構築したり管理するのを代わりにやってくれたり、あるいは人がやるのを補助してくれるソフトウェアのこと。

※2 オーケストレーション
コンピュータシステム、アプリケーション、およびサービスにおける、設定、管理、調整の自動化の事。

※3 モジュール
直訳は「部品」という意味で、他のものと合わせて使うもののこと。

※4 スモールスタート
新たな事業を立ち上げる際に、最初は機能やサービスを限定するなどして小規模に展開し、需要の増大などに応じて順次規模を拡大させていくこと。

##参考
[Ansibleに挑戦してみた（Ansible超入門）](https://ops.jig-saw.com/tech-cate/howto_use_ansible)

[Ansibleでできることを中の人が教えます - インストールと実行〜EC2へのNginx投入までを学ぼう](https://employment.en-japan.com/engineerhub/entry/2019/04/12/103000)

[エージェントレスでシンプルな構成管理ツール「Ansible」入門](https://knowledge.sakura.ad.jp/3124/)

[Ansible を利用したシステム構成情報の自動取得
](https://www.softek.co.jp/SID/blog/archive/entry/20200423.html)

[Ansibleとは何か　構成管理ツールの目的〜Ansible導入まで最速で理解する](https://qiita.com/Brutus/items/1894629105d61f4854bc)
TechCamp卒業まであと２６日



#現在の状況:hot_pepper:
最終課題でフリマアプリを作っているのですが、
あとはAPIを使ってクレカ決済する機能を完成させるだけのところまでやってきました。
はじめは訳わからないruby on railsでしたが、慣れてくきたらこのフレームワークの便利さがわかるようになり楽しいですね.
カリキュラムでは書かれている通りにやってたので理解があまりできてなくても進めてしまいましたが、最終課題は自身で考えなければいけないので、カリキュラムで今まで勉強したことが身についた感じがします:point_up_tone1:


#これからの人生のあらすじ:corn:
とりあえずあと三日以内に最終課題を終わらせてポートフォリオを作る時間をたくさん取りたいですね。作るものはあまり決めていないのですが
現在ITパスポートの試験勉強もしており、暇などなくなりました
あわよくばプログラミングを始める前にたくさん勉強したTOEICも受験したいですし:fountain:
現在はひとりぼっちの忙しい日々を大いに楽しんでおります:hamburger:ワハハ
【初心者向け】オブジェクト指向・クラスの意味の理解補助

#この記事について&前提条件
初心者向け記事です。
クラスってなんだ？OOP（オブジェクト指向プログラミング）ってなんだ？何の意味があるんんだ？と思って調べると、よく出てくるのが、動物とか車のとの図。
その図を見て、「言わんとしてることは分かるけど、それが何なんだ？」と思った初心者向けの記事。
私も初心者なので、これが答えの全てではないだろうが、少しでも理解の助けになればと思い書きました。

（20200911更新）
>・クラスとサブクラスの関係、クラスとインスタンスの関係を明確に分けた方がよい
>・現実世界とオブジェクト指向では関係が異なる

というコメントをいただいたので、記事修正しております。


#本文

**1つの答え：メンテナンスしやすい**

よく見るのが下記のような図ですね。
これは、クラス（乗りもの、動物）と、サブクラス（乗用車・バイク・バス、人間・犬・猫）の関係です。
![qiita1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240673/b5de1954-a36e-0436-f4de-0fdb5980a503.png)
![qiita2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240673/a10eb27d-3975-3c9c-8a00-53db7c7832df.png)
「お、おう、それがどうした」という感じ。
「そりゃあそうだろ、まともな教育受けていればそんなことをわかるよ」と。

問題は、「これがどう使えるか？」という話。
下記の図のように、クラス（もしくはサブクラス。プリウスという車種）とインスタンス（現実に存在するプリウス1~3）を考えたほうがよいと思う。
![qiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240673/44e055a1-589f-c9a4-920e-297d415a63a2.png)

例えば、プリウスのプログラムにある条件でブレーキが利かなくなるという問題が発生したとする。
当然すでに販売したプリウス1~3にもそのような問題が発生する。
さあどうやって対応しようか？

ここで、2つの選択肢がある。

1. 頑張ってすでに販売した車1つ1つを見て回り（もしかしたら全国に点在してるかも）、1台1台のプログラムを直す
2. あらかじめ、プリウスの親のプログラムを直せば自動で1台1台のプログラムに反映されるようにしておき、親のプログラムを直す

これはさらに抽象化して、親クラスと子クラスを考えても同じ。
トヨタの全車種にまたがる問題が見つかったときに、各車種（プリウスとかカローラとか）のプログラムを1つずつ直すのか、それとも全車種の共通プログラムを作っておいてその共通プログラムだけ直せばOKなのか。

これは例えばWordPressの記事でも同じ。
記事1つ1つに編集や削除の機能（メソッド）を個別に作成するのではなく、記事というクラスに編集とか削除とかの機能を持たせる。


**現実世界とオブジェクト指向では関係が異なる**という点について、
現実世界では、「乗り物」は、乗用車・バイク・バスなど、要素を包括した概念（@shiracamusさんからいただいた図を記載させていただきます）
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f32393037302f62323165306333622d666564612d336162372d343265322d3931356665313137636536612e70 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240673/01bd3fe9-e2c4-9074-b130-5f0b76834047.png)

オブジェクト指向では、「乗り物」は、乗用車・バイク・バスの共通データ・共通機能
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f32393037302f36323031313261312d373437322d393066332d363365382d6135316332306238376264652e70 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240673/7e3b8a5f-b553-b846-a8fe-949e6685bb62.png)

なので、オブジェクト指向においては、「乗り物」を修正すると、乗用車・バイク・バスの機能のうち、「乗り物と共通している機能」が修正できるということかと。


このように、オブジェクト指向でクラスを用いてプログラムを作ることで、より管理しやすくなる、というのがオブジェクト指向の意味なのでは、と思いました。
【自然言語処理/NLP】Pythonで簡単に機械翻訳による逆翻訳（back translation）をする方法


NLPのコンペティションなどで、**データの水増し（Data Augmentation）**のために、**逆翻訳**をPythonでしたい時ありませんか？

例えば、Kaggleの「Toxic Comment Classification Challenge」では 1st place solution にこの手法が用いられていたりします。
https://www.kaggle.com/c/jigsaw-toxic-comment-classification-challenge/discussion/52557

この記事では、Pythonで**機械翻訳**を用いて、簡単に逆翻訳をする方法について説明します。

> ![機械翻訳による逆翻訳](https://amitness.com/images/nlp-aug-back-translation.png)
機械翻訳による逆翻訳の例
引用: https://amitness.com/2020/05/data-augmentation-for-nlp/

# 概要

**googletrans** を使えば、APIキーなど不要で、簡単に逆翻訳ができます。

# googletrans のインストール

環境は Python3 を想定しています。

```
$pip install googletrans
```

# 逆翻訳のプログラム

```python
from googletrans import Translator

def BackTranslation(text, original_lang, via_lang):
    translator = Translator()
    return translator.translate(translator.translate(text, dest=via_lang).text, dest=original_lang).text
```

引数の text には原文、original_lang には原文の言語、via_lang には経由したい言語を指定します。

lang に指定できる言語については、以下のgoogletransのドキュメントを参考にしてください。
https://py-googletrans.readthedocs.io/en/latest/

# 使用例

「The destiny of man is in his own soul.」
という英語の文章を日本語を経由して逆翻訳してみます。

```python
text = "The destiny of man is in his own soul."
BackTranslation(text, "en", "ja")
```

返り値（逆翻訳の結果）は以下のようになります。

```:逆翻訳の結果
'The fate of man lies in his own soul.'
```

また、中継した言語（日本語）を出力させると、以下のようになります。

```:中継した言語
人間の運命は彼自身の魂にあります。
```

# 参考文献
A Visual Survey of Data Augmentation in NLP
https://amitness.com/2020/05/data-augmentation-for-nlp/

Googletrans: Free and Unlimited Google translate API for Python
https://py-googletrans.readthedocs.io/en/latest/

逆翻訳は機械翻訳の錬金術師か？
http://deeplearning.hatenablog.com/entry/back_translation
Android Instrumentation Testで特定クラスのcoverageを出力する

JVMのテストは１テスト、あるいは１テストクラスごとにcoverageを簡単に確認できる。
が、instrumentation testで同じようなことをする方法がわからず、CI任せにしていた。

でもやっぱりローカルでも見られるようにしたい :thinking: 

## 1. Run Test

### CLIの場合

```
adb shell am instrument -w -r --no_window_animation -e emma true -e class 'your.app.package.TestClassName', -e coverage true your.app.package.test/android.support.test.runner.AndroidJUnitRunner
```

_（不要なパラメータあるかも）_

### Android Studioの場合
Run/Debug ConfigurationsにAndroid Instrumentation Testを追加。
Module、Class（あるいはMethod）にテスト対象を設定。
Instrumentation argumentsに下記パラメータを追加。

- coverage: true
- emma: true

## 2. Get coverage

```
adb pull /data/data/your.app.package/files/coverage.ec ./coverage.exec
```

:exclamation: libraryのテストの場合、`your.app.package.test`に保存されてる様子。
:exclamation: 上記パスはemulatorの場合。実機だとパスが違うっぽい。

:bulb: `coverageFile`パラメータで保存先を指定もできる

cf. https://developer.android.com/studio/test/command-line?hl=ja#AMOptionsSyntax

## 3. Open coverage
Android Studio上で見る場合は、下記メニューから、pullした`coverage.exec`を選択
`Analyze` -> `Show Code Coverage`

`Analyze` -> `Generate Coverage Report`でHTML出力もできる

:exclamation: Android Studio 4からは、`Run`メニューの中に移動された様子

-----

何かもう一工夫すれば、もっとステップ減らせるかも。
新規アプリ作成　rails new から githubで新規リポジトリ作成まで

###この記事では...
Ruby on Railsでアプリを新規に作る際に、
①アプリのデータベースをMYSQLにする
②アプリをGitHubで管理する
この二つを行います。
使用しているPCはMacになります。
###①RailsのデータベースをMysqlにする
まずターミナルを開き、そこで新規にアプリを作るディレクトリに移動します。
移動したら


```
$ rails new アプリケーション名 -d mysql
```
と入力します。
これでディレクトリに新規アプリのフォルダが出来ているはずです。

今回はversionの指定をしていません。versionを指定しない場合は自動で最新のverになります。
アプリを作る際に必要なGemなどがversionによって対応しない場合もあるので、可能なら設計段階で確認しておくと良いと考えます。

###②新規リポジトリを作ってアプリとGitHubと連携させます。
まずターミナルで

```
$ cd アプリケーション名            #新規アプリのディレクトリに移動
$ bundle exec rake db:create  #データベースの作成
```

次は　GitHub Desktop の操作になります。
起動したのち、左上の「Current Repository」をクリックして、
![スクリーンショット 2020-09-02 11.48.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426675/cfc222c3-062b-6fdc-9b3b-44e8051d4675.png)

そのまま「Add」→「Add Existing Repository」を選択してください。
![スクリーンショット 2020-09-02 11.49.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426675/06c08dc2-cd89-55af-9830-827642999980.png)

そこで出てきた小窓にある「Choose...」をクリックして新規アプリのフォルダを指定して、「Add Repository」をクリックして下さい。

そこまで来れば後は最初のコミットをして「Publish repository」をクリックすれば完了です。
おそらく途中othersの下記にあった新規アプリのリポジトリが見慣れた位置に戻っているはずです。
ブラウザでのGitHubにも新規アプリのリポジトリが出来ているはずです。

これで新規アプリ作成の下準備は終わりました。
今回の記事はこれで完了になります。備忘録的な意味合いが大きいのですが誰かの役に立てれば幸いです。

####参考にさせて頂いた記事
[rails new した後のgit管理（GitHub Desktop使用)](https://qiita.com/Coconew5/items/bc3ce525588a64ff208e)
[railsのDBをmysqlに変更する。](https://qiita.com/pchatsu/items/a7f53da2e57ae4aca065)
人間の本質

なにも、心理学・医学の観点での話ではありません。
皆さんは、仕事をどのようにとらえていますか？
自立したベンチャーやご自身の思いでビジネスを興し活動している方を除いて、
ほとんどの方が**『楽しい・ワクワク・嬉しい』**といったポジティブの感情を抱く方は少ないのではないでしょうか？
**『自分の時間を削って会社に奉仕して、その対価として給与をもらうのだから、辛い』**と答えた方がいました、こんな姿勢で良い仕事ができるはずがありません。
ここ数年、感情知能（Emotional　Intelligence）が重要とされ、IQ（Intelligence Quotient,：知能指数）よりもEQ（Emotional Intelligence Quotient：心の知能指数）が高い方が人生を豊かに過ごせ、成功者に多いとの報告があります。
**自分の感情を客観的理解し、相手の感情も分かり、その状況において感情を蔑ろにせず対処できる能力**のことです。
仕事・作業を進めていくのは個人ではできません。必ず、関係する人がいて、その人たちとの関係性によって結果が良くも悪くもなります。
この関係性も個々の感情によって良くも悪くもなります。
トヨタの教科書には次の方程式があります。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621379/dd51cc8b-10b2-be6d-692e-a61c64149dc5.png)

品質・生産性を含むすべての業績の方程式です。
多様性とさけばれる時代に必須要素である**個性**、さらに感情である**モチベーション**が入っています。
この方程式は、2009年発刊のトヨタ流の教科書（著：堀切俊雄：TMS&TPS県低教会理事長）に掲載されています。いまから10年も前に一般人が入手できる本に載っていますが、トヨタでは面々と受け継がれてきたDNAの一つなのです。
能力は様々な技術であり勉強・訓練すれは向上できます。私たちはこの点のみトレーニングしてきただけなのです。しかしながら、次にあげる大切な事をこの方程式は訴えています。
**個性とは元来人が生まれ落ちた時から持っていて、その人の経験で培われてきたもので、世界に唯一の貴方（個人）の証である大切なもの**、そしてこの**個性を大切にして、個人の感情がポジティブになる**ことがとても大切にすること。
人を大事にするとは、この点が重要であるのです。個性＋モチベーションで人間力とも置き換えることが出来るでしょう。モチベーションは与えられたからといって高まるモノではありません、初期のモチベーション向上は環境や他者によることも大きいのですが、継続そして大事を成す時には、内なるモチベーションを高める気位が作用してきます。
**モチベーションはマネジメントでも大きく影響を受けます。令和の時代、昭和的なマネジメントではモチベーションは最悪な状態に陥ってしまいます。**
**人間力は問題解決能力、マネジメントにも必要な内容です。**
仕事をするうえで大切なのはこの様な人の本質ではないでしょうか？
チームビルディングとは、この人間の本質を大切にしたチーム作りといえます。チームビルディングについては次の項で述べます。
Macの環境変数を設定した話

#背景

Go をMacにインストールしたのですが、go -version　と打っても
-bash: go: command not found　となってしまいました。

調べるとどうやらパスが通っていないようです。


#.bash_profileに以下を追記した

```
export PATH=$PATH:/usr/local/go/bin
```
ホームディレクトリ内の.bash_profileか.bashrcで設定できるとのこと

このファイルはログイン時に1回、実行されるものです。

これで再起動して、再びgo -versionと打ってみると、、

```
go version go1.15 darwin/amd64
```

と表示されましたのでgo がインストールされたことが確認できました。

#参考
下記サイトを参考にさせていただきました。
https://qiita.com/Saku_Rito/items/7ac34960fb8caac9f85e
気になった関数まとめ

<h3>ini_set</h3>
```
ini_set( $varname , $newvalue ) 
```
あらかじめ設定された[$varname](https://www.php.net/manual/ja/ini.list.php "$varname")を $newvalueの値に変更。

<h3>serialize</h3>
```
serialize( array('first' => 1, 'second'=>two)
//a:2:{s:5:"first";i:1;s:6:"second";s:3:"two"}
```
型:値{型:文字数:値;型:値}

<h6>型別の値</h6>
PHP:serialize-Manual User Contributed Notes 引用
>
String
s:size:value;

>Integer
i:value;

>Boolean
b:value; (does not store "true" or "false", does store '1' or '0')

>Array
a:size:{key definition;value definition;(repeated per element)}

>Null
N;

文字は長さと値、数字は値、真偽は1(真)か0(偽)


<h3>strpos</h3>
```
$haystack = 'abcdefg'
$needle = 'e'
echo strpos($haystack, $needle)
//4
```

 ＄haystack　中で ＄needleが最初に現れる位置を返します。

<h3>time</h3>
```
echo time()
//1599018369
```
現在時刻(2020/09/02 12:46)時点のUNIXスタンプを返す
UNIXスタンプは協定世界時 (UTC) での1970年1月1日午前0時0分0秒から形式的な経過秒数）。



#CakePHP

<h3>newEntity</h3>
```
$entity = $this->{$tableName}->newEntity($requestData, ['validate' => false]);
```
＄reqestData 配列 の ['validate' => 'false']にして、 {＄requestData}table.php のvalidationを無効にしたentityを新たに作成して$entityに代入する。

<h3>set</h3>
```
public function set($name, $value){
    
    $this->viewVars = $data + $this->viewVars;
    return $this;
}
//$this->viewVars[$name => $value]
```

setでviewVarsにインデックスが＄name,値が＄valueの要素を追加する。
コントローラー側で　＄this->viewVars[$name] で呼び出すことができる。
画面の方のdebug kitの　Variables　内にviewVarsが格納されている。
sfdcで指定した二つ日付の間の月を取得

・指定した二つ日付の間の月を取得
　startFy：202009
　endFY：202109
・返却値：
　202009,202010,202011,202012,202101,202102,202103,202104,202105,202106,202107,202108,202109

    String startFy = '202009';
    String endFY= '202109';
    // getFYを呼び出す
    Set<String> setFY = getFY(startFy, endFY)

    /*
     * 年月を取得
    */
    public Set<String> getFY(String startFy, String endFY){
        Set<String> setFY = new Set<String>();
        
        if (String.isEmpty(startFY) || String.isEmpty(endFY)) {
            return setFY;
        }
        // 削除開始年月の取得
        Integer startYear = Integer.valueOf(startFY.substring(0, 4));
        Integer startMonth = Integer.valueOf(startFY.substring(4, 6));
        // 削除終了年月の取得
        Integer endYear = Integer.valueOf(endFY.substring(0, 4));
        Integer endMonth = Integer.valueOf(endFY.substring(4, 6));
        for (integer i = startYear; i <= endYear; i++) {
            String dateFy = '';
            if (startYear == endYear) {
                for (integer j = startMonth; j <= endMonth; j++) {
                    if (j < 10) {
                        dateFy = i + '0' + j;
                    } else {
                        dateFy = i + '' + j;
                    }
                    setFY.add(dateFy);
                }
            } else {
                if (i == startYear) {
                    for (integer j = startMonth; j <= 12; j++) {
                        if (j < 10) {
                            dateFy = i + '0' + j;
                        } else {
                            dateFy = i + '' + j;
                        }
                        setFY.add(dateFy);
                    } 
                } else if (i == endYear) {
                    for (integer j = 1; j <= endMonth; j++) {
                        if (j < 10) {
                            dateFy = i + '0' + j;
                        } else {
                            dateFy = i + '' + (j + 1);
                        }
                        setFY.add(dateFy);
                    }
                } else {
                    for (integer j = 1; j <=12; j++) {
                        if (j < 10) {
                            dateFy = i + '0' + j;
                        } else {
                            dateFy = i + '' + j;
                        }
                        setFY.add(dateFy);
                    }
                }
            }
        }
        return setFY;
    }
Angularのページ遷移時に現在のURLのPathを取得する

# ユースケース
ログイン画面やサインアップ画面ではヘッダーを非表示にしたい。

ログイン画面(`/login`)、サインアップ画面(`/signup`)ではヘッダーを非表示にするために表示しているページのパスを取得し、事前に定義したパスにマッチしているかをチェックする。

例えば、
`http://localhost:4200/login`→ヘッダー非表示
`http://localhost:4200/menu`→ヘッダー表示

# Routerを使うのが今のところ最適解っぽい
ActivatedRouterのsnapShotを使うなどいくつかの方法がありますが、この方法がシンプルかと思います。

## テンプレート
```html
<app-header *ngif="!hidden"></app-header>
    <router-outlet></router-outlet>
<app-footer></app-footer>
```

## TypeScript
```typescript
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  hiddenPath: string[] = ['login', 'signup'];
  hidden: boolean;
  constructor(
    public router: Router,
  ) { }
  ngOnInit() {
    this.router.events.pipe(
      filter(f => f instanceof NavigationEnd)
    )
      .subscribe((s: any) => {
        this.hidden = this.hiddenPath.some(e => s.url.includes(e));
      });
  }

}

```

# 参考URL
[Angular 日本語ドキュメンテーション - NavigationEnd](https://angular.jp/api/router/NavigationEnd)
[[Angular] router-outletの外で現在のurlを取得 | COTeggのバケツ](https://www.cotegg.com/blog/?p=2559)
[JavaScriptのsomeとeveryがすごく便利 - Qiita](https://qiita.com/i_am_master_yoda/items/224ff73443b4566ec8e8)
データウェアハウスとは

### データウエアハウスとは
データウエアハウスとは、分析しやすく検索できるようにしたデータを蓄積したデータベースのこと。データの倉庫とも言います。あらゆるデータを加工しデータウエアハウスに集め、そのデータを分析しやすい形に変換。分析の目的ごとにデータを作成・蓄積を行う。データウエアハウスで重要なことは「分析しやすいデータ」を格納してあるということ！！
企業のデータはさまざまなところから生まれます。例えば、実店舗やeコマースなど様々なデータが分散しています（サイロ化という）。このサイロ化したデータを企業の意思決定に生かすにはそれぞれのデータを分析しやすいデータに加工し、集める必要があります。この分析しやすいデータを集めたデータベースをデータウエアハウスといいます。
ここで疑問に思うのがそれぞれの実店舗のデータベースのデータをそのまま分析すればいいのではないか？ということです。これでも分析を行うことが出来るとは思いますが、処理するのに不可が掛かります。実店舗やeコマースそれぞれのデータベースというのはトランザクションに最適化されており、分析を実行するのには向いていません。また、実店舗のデータはそもそも分析しやすいようなデータではありません。そこでデータ分析に最適化されたデータベースであるデータウエアハウスが誕生したという感じです。
データウェアハウスは列ごとに処理ができるデータベースでありデータも列ごとに保存しています。実店舗から生成されるデータは行ごとに保存されるが、分析する際に行全体を読み込むには時間が掛かり無駄が多い。実際分析クエリが一度にアクセスするのは数列程度であるためすべての列は必要ないです。そのためデータウエアハウスでは列ごとに保存しています。


### データウエアハウスの特性
データウエアハウスには大きく４つの特性があります。
[統合化・サブジェクト試行・時系列・恒常性]

・統合化
それぞれのデータベースのデータの意味や表記ゆれを統一する。これを行うことによって分析者はデータの違いを意識することなく利用することができる。
例
和暦を西暦に変換
大文字をすべて小文字に変換
JSTからUTCに変換など

データの変換方法
ETL(Extract Transform Load)…ETLツールでデータを変換してロードする方式
ELT(Extract Load Transform)…ステージングエリアにデータをロードし、SQLでデータを変換する方法

・サブジェクト試行
商品や顧客のような分析軸ごとに集計し蓄積したデータであるデータマートを作成する。
（データマートとは企業で蓄積されたデータから、用途・目的などに応じて必要なものだけを抽出、集計し、利用しやすい形に格納したデータベースのことです。簡単に言うとデータウェアハウスから目的に合わせたデータを取り出したものです。）

・時系列
日々生成されるデータであり、その時点のデータ状態を保存する。これは過去にさかのぼってデータ分析を行うことが目的です。

・恒常性
蓄積されたデータは変更せず参照可能な状態で保存する。これはいつでもデータ発生時の値を参照することが目的です。


### ディメンジョナルデータモデル

ディメンジョナルデータモデルはラルフ・キンボールさんが提唱するモデルで、スタースキーマでデータウェアハウスを構築することです。分析したい値を持つ「ファクトテーブル（製品によってはメジャーともいう）」と分析の軸となる値「ディメンションテーブル」から構成される。ファクトテーブルを真ん中に置き、それを囲むようにディメンションをリンクさせます。この完成したモデルの形が星形に似ていることからスタースキーマと呼ぶようです。

ファクト（メジャー）…売上高・ページビュー、顧客数などの指標です。ファクトテーブルはこのファクトとディメンジョンテーブルを参照する外部キーの列を持っている。

ディメンション…年齢別・性別・都道府県のような属性。

### データウエアハウスのサービス
データウエアハウスのサービスは以下の4つがあげられる。
Redshift(AWS)
Athena(AWS)
Snowflake(snowflake社)
BigQuery(Google)





＜参照したサイト一覧＞
データ分析を支える技術 DWH再入門←私にとって神動画でした！！！
https://www.youtube.com/watch?v=G7weKwUE6KY&t=628s
データウェアハウスに関して
https://el.jibun.atmarkit.co.jp/dwbi/2011/05/1-c6a3.html
gulp を使用したSCSS(SASS)環境の構築

普段はvsvcodeの拡張機能「Live Sass Compiler」を使用しているが、gulpを使用する機会があったので覚え書き

**事前準備**
Node.js　[https://nodejs.org/ja/](https://nodejs.org/ja/)
※インストール方法は@sefoo0104さんが紹介してくれているのでそちらを参照
・[Node.jsをインストールする](https://qiita.com/sefoo0104/items/0653c935ea4a4db9dc2b)

gulp(ガルプ)について詳しく知りたい方は公式サイトもしくはCodeGridを参照
・gulp [公式サイト](https://gulpjs.com/)
・CodeGrid [現場で使えるgulp入門](https://app.codegrid.net/entry/gulp-1)

**モジュール**
Node.jsの設定が終わったら以下のモジュールをインストール
※今回はカレントディレクトリにインストール

一人で開発する場合(``devDependencies``)

```ruby:
> npm install --save-dev パッケージ名
> npm install -D パッケージ名
```

開発環境を共有する場合(``dependencies``)

```ruby:
> npm install --save-パッケージ名
> npm install -S パッケージ名
```

**devDependenciesとdependenciesの違い**
パッケージ(package.json)を公開した場合、開発者以外の人が``npm install``を実行すると、``dependencies``に記録されたパッケージが``node_modules``内にインストールされ、``devDependencies``に書かれているパッケージはインストールされません。

**SASS、SCSSコンパイルに必要なモジュール**

|モジュール名|内容|
|---|---|
|gulp|gulp本体|
|gulp-sass|Sass/SCSSのコンパイル。sourcemapが使えない|
|gulp-ruby-sass |gulp-sassと同じ処理だがこちらはsourcemapが使える|
|gulp-postcss|CSSパーサとそれを取り扱うのに便利なAPIを提供しているツール|

gulp-sassとgulp-ruby-sassはお好みの方を選択
※npmjsサイトで提供されているモジュールと設定方法が紹介されている
[https://www.npmjs.com/](https://www.npmjs.com/)

gulpを動かすには``gulpfile.js``が必要となるので、``node_modules``と同じ階層に
``gulpfile.js``を作成。
gulpfile.jsの書き方は@y_sekitobaさんが紹介しているのでそちらを参照
[初めてのGulp入門 ~gulpfile入門~](https://qiita.com/y_sekitoba/items/363eae906925ada83289)

上記で必要最低限の環境は構築できたが、このままでは面倒な手順を踏んでgulpをインストールした意味がいないので、下記のモジュールも合わせてインストールする。

|モジュール名|内容|
|---|---|
|gulp-notify|Mac,Linux,Windowに対してメッセージを通知する|
|gulp-plumber|エラーが起きても処理を中断させない|
|autoprefixer|コンパイル時にベンダープレフィックスを自動で記述してくれる|

上記インストールが完了したら``npm list --depth=0``でインストールされているパッケージ名を確認。

```ruby:
npm list --depth=0

+-- autoprefixer@9.8.6
+-- gulp@4.0.2
+-- gulp-notify@3.2.0
+-- gulp-plumber@1.2.1
+-- gulp-postcss@8.0.0
+-- gulp-sass@4.1.0
```

**保存時に自動でCSSを作成**
``gulpfile.js``を下記に修正

```ruby:gulpfile.js
/*
src 参照元を指定
dest 出力さきを指定
watch ファイル監視
series(直列処理)とparallel(並列処理)
*/
const { src, dest, watch, series, parallel } = require('gulp');

// プラグインを呼び出し
const sass = require('gulp-sass');
const plumber = require("gulp-plumber");
const notify = require("gulp-notify");
const postcss = require("gulp-postcss");
const autoprefixer = require("autoprefixer");

//参照元パス
const srcPath = {
  css: '../scss/*.scss',
}
//出力先パス
const destPath = {
  css: './css',
}

// 処理
const cssSass = () => {
 return src(srcPath.css) 
  .pipe(
    plumber(              //エラーが出ても処理を止めない
      {
        errorHandler: notify.onError('Error:<%= error.message %>')
        //エラー出力設定
      }
    )
  )
  .pipe(sass({
    outputStyle: "expanded"
  })
  )
  .pipe(postcss([autoprefixer({browsers: ['last 2 versions']})]))
  .pipe(dest(destPath.css))     //コンパイル先
}

/**
 * Sassファイルを監視し、変更があったらSassを変換します
 */
const watchSassFiles = () => watch(srcPath.css, cssSass);

// タスクをまとめて実行
exports.default = watchSassFiles;
```
対象のscss(sass)ファイルと出力先は任意に変更」


gulpの実行
カレントディレクトリに必要なパッケージが保存されているので、下記コマンドで実行

```ruby:
npx gulp
```

上記方法ではSCSS(SACC)のコンパイルを記述したが、LESSのコンパイルも用意されているので、LESSを使用している場合、必要なモジュールをインストールして``gulpfile.js``を修正。
Databrciks 事例紹介　~教育編~　

# はじめに
本記事では Databrciks の教育業界での事例をご紹介いたします。
【掲載事例】
McGraw-Hill Education
公式事例URL: https://databricks.com/customers/mcgraw-hill-education
ウエスタンガバナーズ大学
公式事例URL: https://databricks.com/customers/western-governors-university


#McGraw-Hill Education
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614396/34436948-91da-1163-5929-822df763d9ea.png)

##ユースケース
- 企業とユーザーとの関係性の強さと定着率を向上させる
- 個別学習
- 機械学習の活用
- 取り込みや ETL 処理

##課題
- 効果的なデータ分析が必要
- 市場投入までの時間の短縮
- 学習者への取り組みと適応

##成果
- 1000 万以上の個々の学生のやり取りが正常にキャプチャ、集約、分析され学生の定着率を 19% 以上向上できました。
- 機械学習を使用して、生徒の理解度のレベルを分析し、推奨事項を提供して生徒の合格率を 13% 向上できました。
- 550 万人をこえる学生にソリューションをより迅速かつ効果的に提供できました。
- 運用コストが 30% 削減されました。


#ウエスタンガバナーズ大学
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614396/aeaa6d36-8495-02c3-9a3a-cf759ee864f3.png)

##ユースケース
- 学生のプロファイル、マーケティングデータ、評価データなどの様々なデータに基づく内部意思決定
- データ処理時間の短縮
- 機械学習の活用

##課題
- データ量が増加していくことでデータ処理を集中的に短期間で行いたい。
- 様々なデータをエンドユーザーが簡単に保存・整理・アクセスできる仕組みの構築。
- データサイエンティストが行うべき仕事ではない作業に時間がかかっていた。

##成果
- クラスター管理が自動化されクラスターのプロビジョニングが簡素化されました。それにより、データサイエンティストが本来の仕事に多くの時間をかけることが可能になりました。
- Databricks のワークスペースにより、データサイエンティストの開発環境が向上しました。
- 複数のプログラミング言語がサポートされたことで、様々なスキルを持つチームがデータにアクセスして分析が可能になりました。
- ETL 処理が向上し、夜間の ETL でのジョブの実行にかかる時間を 3 時間から 10 分未満に短縮されました。
- クエリの応答時間を 30 分以上から数秒に短縮できました。

#おわりに
Databricks は教育業界での様々なデータに対応できる親和性の高いサービスになっています。

#参考
【お客様の成功事例】　
公式 URL: https://databricks.com/jp/customers
iTerm2でドラッグ&ドロップを無効にする

# iTerm2でドラッグ&ドロップを無効にする

設定->advanced
`terminal drag`で検索をかけ、一番上の設定でYesを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187150/68cd2a6a-af89-aa6d-df12-e6d84be67c47.png)

不慮の事故を防ぎましょう
3D地図周りの知っておきたい地形情報に関する概念

# ジオイド/[geoid](https://en.wikipedia.org/wiki/Geoid)
* 重力によって海の表面が形成した球体。
* ジオイドの表面が海面、ジオイド基準の高度は海抜。
* ジオイドの表面はその場所の重力と垂直となる。  
重力は、その場の密度にも影響されるので、ジオイドがそんなに滑らかではない。
* goidの差分のを1000倍に大きくすると理解しやすいでしょう。
![Screen Shot 2020-09-02 at 10.36.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/ad50ccf8-5bac-10e9-d702-0a7e4e24845f.png)

# 地球楕円体/[Earth ellipsoid](https://en.wikipedia.org/wiki/Earth_ellipsoid)
* 地球楕円体は、ジオイドを抽象的的に表現した楕円体。  
その楕円体は、赤道の半径xがやや長い、南極-北極の半径zがやや短い。扁平率はf=(x-z)/x.
* 日本の測量で使われているITRF94とか、WGS84の一個前のモデルであるGRS80とかは、  
WGS84と些細な差があるが基本同じものと考えていいでしょう。（扁平率が少し違うだけ）
![Screen Shot 2020-09-02 at 10.57.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/a14b9ee5-cbc2-ecc0-ebf4-d15db304d4d5.png)

# 世界測地系/World Geodetic System(ここではGPSが使う[WGS84](https://en.wikipedia.org/wiki/World_Geodetic_System)だけ集中して説明する)
* WGS84は地球楕円体のうちの一つ。
* GPSが使うので、ドローンも使うし、車のカーナビーも基本これを使う。
* WGS84の長半径は 6,378,137 m、扁平率は 1/298.257223563
* 上記の地球楕円体の図の通り、x,y,zが決められている。
* z:中心点から北極へ、  
x:中心点から赤道と本初子午線の交差点へ、  
y:xとzと垂直して東経90度へ。

# 直交座標系表記 vs 地図学？表記/Cartesian vs Cartographic
* 3D地図でプログラムやると、必ず出会うのはCartesian(x, y, z)表記とCartographic(latitude, longitude, height).
* Cartesianでは、世界測地系の図のどおり、地球を楕円として考えて、
z:中心点から北極へ、
x:中心点から赤道と本初子午線の交差点へ、
y:xとzと垂直して東経90度へ
3つのVectorで地理の位置を表す。単位は通常mそのまま。
* Cartographicでは、表示の通り、経度、緯度、対地面高度で地理の位置を表す。
* Cartographicの経度、緯度は、
度数のDegreesでも、  
ラジアンのRadians(=Degree*pi/180)でも  
どっちてもOKです。  
heightは通常mそのまま。
* ここに注意しないといけないのは、ここの地面、つまりheight=0の場所は、
そのCartographicが準ずる地球球体のシステムによって変わります。
その球体がwgs84か、geoidかによってだいぶ変わります。
* 例：wgs84基準で、経度、緯度Cartographic(0, 0)の場合(赤道と本初子午線の交差点)、  
Cartesianのxは地球半径、y=z=0である。
![Screen Shot 2020-09-04 at 19.30.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/c45ccba8-5c5a-e9dd-1e1c-71fdae1234fc.png)
* 例：wgs84基準で、経度、緯度Cartographic(0, 90)の場合(北極)、  
Cartesianのzは地球半径、y=x=0である。
![Screen Shot 2020-09-04 at 19.32.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/f8309325-d403-df7c-1adc-268de6f7dfff.png)
* 例：wgs84基準で、経度、緯度Cartographic(90, 0)の場合(インド洋)、  
Cartesianのyは地球半径、z=x=0である。
![Screen Shot 2020-09-04 at 19.33.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/76d170d7-f0a1-c078-23d6-9dc59c9e323b.png)


# 地表とジオイドと世界測地系(WGS84)との、高さの関係
* GPSから得られる地面の高さ(図の楕円体高)は、WGS84の地球楕円体がベースになる。
* 標高とか、海抜とかは、ジオイドがベースとなる。
![Screen Shot 2020-09-02 at 11.14.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/57c3a241-9cdb-45bb-3c9c-2efaee6cf80e.png)
![Screen Shot 2020-09-02 at 11.18.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547017/7c173cca-8762-efe1-87a6-768c91bd8e35.png)
インタープリタを作る その6

#概要

インタープリタを作ってみた。
練習問題をやってみた。 

#数9を4個つかって1から15までの数を表す式をつくる。

1

```
  push  9
  push  9
  /
  push  9
  push  9
  -
  +
  out
  end
```

2

```
  push  9
  push  9
  /
  push  9
  push  9
  /
  +
  out
  end  
```

3

```
  push  9
  push  9
  +
  push  9
  +
  push  9
  /
  out
  end
```

4

```
  push  9
  push  9
  push  9
  +
  push  9
  /
  dup
  +
  out
  end
```

5

```
  push  9
  push  9
  push  9
  +
  push  9
  /
  dup
  +
  -
  out
  end
```

6

```
  push  9
  dup
  push  9
  +
  push  9
  +
  push  9
  /
  -
  out
  end
```

7

```
  push  9
  push  9
  push  9
  +
  push  9
  /
  -
  out
  end
```

8

```
  push  9
  push  9
  push  9
  drop
  push  9
  /
  -
  out
  end 
```

9

```
  push  9
  push  9
  -
  push  9
  *
  push  9
  out
  end 
```

10

```
  push  9
  push  9
  /
  push  9
  dup
  push  9
  /
  +
  out
  end 
```

11

```
  push  9
  push  9
  push  9
  +
  push  9
  /
  +
  out
  end 
```

12

```
  push  9
  dup
  push  9
  push  9
  +
  +
  push  9
  /
  +
  out
  end 
```

13

```
  push  9
  push  9
  push  9
  +
  push  9
  /
  dup
  +
  +
  out
  end
```

14

```
  push  9
  dup
  push  9
  push  9
  +
  push  9
  /
  dup
  +
  -
  +
  out
  end 
```

15

```
  push  9
  dup
  dup
  push  9
  +
  push  9
  +
  push  9
  /
  -
  +
  out
  end 
```



#成果物

命令を拡張した。dupとdropを追加。

https://embed.plnkr.co/plunk/4C3hBP44mlmZRZSo

以上。
Databricks事例紹介　～医療編～

#はじめに
今回はDatabricksの医療業界での事例をご紹介させていただきます。
【掲載事例】
1.REGENERON
公式事例URL : https://databricks.com/customers/regeneron
2.SANFORD
公式事例URL : https://databricks.com/customers/sanford-health
3.OPTUM
公式事例URL : https://databricks.com/customers/optum

#REGENERON
![2020-09-03_09h26_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578416/2f7418ee-0de8-bf51-bf9b-fb2216a1c084.png)
###ユースケース
大容量のゲノムデータを分析し、新薬の開発を効率化しました。
###課題
REGENERONでは新薬開発のために、
40万人分以上の遺伝子データを記録したデータベースを構築しました。
しかし、それらのデータを戦略的に活用するには以下の課題がありました。

- ゲノムデータと臨床データが複雑に分散している
- 10TBのデータセットのETL処理だけで何日もかかる
- データ量が多いため、モデルの分析およびトレーニングは困難を極めた

###成果

- データセット全体に対してのクエリ実行時間が30分から3秒に短縮(600倍の高速化)
- 共同作業環境を構築できたことで、エンジニアチームとしての生産性が向上
- 分析環境の管理を自動化
- ETL処理全体が3週間から2日に短縮

#SANFORD
![2020-09-03_09h27_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578416/de2ce363-4df9-59e7-cc83-db685fb536da.png)
###ユースケース
大規模な患者データセットから
(10万人分以上の遺伝子データ、医療画像、健康記録など)
慢性疾患の原因となる遺伝子疾患を特定し、
患者ごとに最適化された治療を提供する基盤を構築しました。
###課題
分析環境を構築するにあたり、以下のような問題がございました。

- エンジニアリングやインフラ知識の不足
- 上記が原因で、分析基盤のセットアップや拡張が困難
- 機械学習を大規模に実行するために、数か月または数年も準備する必要があった

###成果

- データから知見を得るのに数日から数時間に短縮
- データエンジニアリングと運用を簡素化
- データをモデルに組み込むことが簡単になり、予防ケアと精密医療の提供を推進

#OPTUM
![2020-09-03_09h26_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578416/fa22c0f9-11cb-e821-f6b2-83873f3ee41e.png)
###ユースケース
病院の保険会社への医療保険請求から
過小支払いを検出をするのに利用しました。
###課題

- 1日あたり数百万件の請求から、8時間以内に病院に結果を返す必要があった
- データ規模の拡大につれて、パフォーマンスの課題が増大
- 計算パフォーマンスの拡張が困難
- 現状の計算に莫大なコストが発生

###成果

- 8000万件の処理をする時間が86分間から160分間に短縮
- 分析の高速化により、失われる可能性のあった数百万ドルの収益を節約
- オートスケーリングにより計算コストを最適化
- あらゆる形式のデータの取り込みが容易に

#おわりに
Databricksは大容量の臨床データなどを分析するのピッタリなサービスですので
非常に医療との親和性は高いです。
#参考リンク
https://databricks.com/jp/customers
CSSの属性セレクタと否定擬似クラスを同時に使用するサンプル

属性セレクタは直感的でよく使うのですが、否定の構文がないので困っていました。調べると否定擬似クラスがあることが分かりました。属性セレクタと組み合わせて使う方法が難しかったのでメモしておきます。

### HTML

link要素が２つあります。media属性が存在しないlinkだけ取得する方法の紹介です。

```html
<head>
  <link href="a.css" rel="stylesheet" media="only screen and (max-width: 320px)" >
  <link href="b.css" rel="stylesheet">
</head>
```

### DevTools コンソール

Chromeの開発ツールのコンソールで指定する例

```javascript

$('head > link[rel="stylesheet"][href]:not([media])').attr('href')
```

#### 結果

```javascript

"b.css"
```

### puppeteer

puppeteerで指定する例。セレクタの指定内容はDevToolsと同じです。

```javascript
const href = await page.$eval('head > link[rel="stylesheet"][href]:not([media])', el => el.href);
console.log(href);
```

#### 結果

```javascript
b.css
```

## 参考

* CSSで特定の順番や種類の要素を指定してスタイルを調整する https://tech.qookie.jp/posts/css-nth-child-style/
* 属性セレクタ https://developer.mozilla.org/ja/docs/Web/CSS/Attribute_selectors
* 否定擬似クラス https://developer.mozilla.org/ja/docs/Web/CSS/:not
* DevTools コンソール https://developers.google.com/web/tools/chrome-devtools/console?hl=ja
* $eval() https://pptr.dev/#?product=Puppeteer&version=v5.2.1&show=api-frameevalselector-pagefunction-args-1
【Rails】コメント機能（登録・表示・削除）

投稿に対するコメント機能実装の備忘録。

# ゴール
- Userテーブル、Postテーブルに紐付く、Comentテーブル(中間テーブル)でコメントを管理。
- Comentテーブルは、コメント内容、コメントしたpost_id、コメント投稿者user_idカラムを設置。
- (前提) ユーザー機能は、devise を使用。

# 実装
## コメントモデルとテーブル作成
- コメントモデルとテーブル（コメント内容、コメント投稿者）を作成

```rb:ターミナル
% rails g model Comment comment_text:text user:references post:references
% rails g controller Comments
% rails db:migrate
```
モデルにアソシエーション追加。

```rb:Commentモデル
belongs_to :post      # Comment.post で、コメントされた投稿取得
belongs_to :user      # Comment.user で、コメント投稿者取得
```

```rb:Userモデル
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy    # User.comments で、ユーザーのコメント取得

validates :comment_text, presence: true, length: { maximum: 1000 }   # 空をバリデーション、文字数制限とか。。
```

```rb:Postモデル
belongs_to :user
has_many :comments, dependent: :destroy    # Post.comments で、その投稿のコメント取得
```

## 詳細ページにコメント欄設置

```haml:詳細ページ
-# コメント表示
= @comments.each do |c|
  = c.user.name
  - if c.user.id == @post.user.id   # 記事投稿者
    %p 投稿者
  = simple_format(c.comment_text)   # 改行ありで表示
  = link_to '削除', post_comment_path(@post, c), data: {confirm: '本当に削除しますか？'}, method: :delete
    -# コメント削除には、@post（親）, @commentsのc に渡す必要がある

-# コメント投稿
= form_for [@post, @comment], local: true do |f|   # comment は、postに紐づくので、配列で書く
  = f.text_area :comment_test
  = f.submit "投稿"
```

一覧表示ページに紐づくよう、ルーティングを設定。

```rb:routes.rb
resources :posts do   # posts に紐づかせるため、ネストにする 
  resources :comments, only: [:create, :destroy] 
end
```

## コントローラーにアクション定義
コメント登録/削除アクション(create/destroy)。

```rb:ターミナル
% rails g controller comments
```

```rb:commentsコントローラー
before_action :set_post
before_action :authenticate_user!    # ログイン中のみ許可

def create
  @comment = @post.comments.create(comment_params)
  if @comment.save
    redirect_to blog_path(@post) notice: 'コメントしました'
  else
    flash.now[:alert] = 'コメントに失敗しました'
    render post_path(@post)
  end
end

def destroy
  @comment = Comment.find(params[:id])
  if @comment.destroy
    redirect_to post_path(@post), notice: 'コメントを削除しました'
  else
    flash.now[:alert] = 'コメント削除に失敗しました'
    render post_path(@post)
  end
end

private
def set_post
  @post = Post.find(params[:post_id])
end

def comment_params
  params.required(:comment).permit(:comment_text).merge(user_id: current_user.id, post_id: params[:post_id])
end
```
投稿ページ(show)に、コメント一覧も表示するので、posts#showで、コメントデータ取得。

```rb:postsコントローラー
      :
def show
  @post = Post.find(params[:id])

  @comment = Comment.new     # フォーム用のインスタンス作成(コメント追加用)
  @comments = @post.comments # コメント一覧表示用
end
      :
```
ワーニングって？

ワーニングって取らないとヤバいすよね。戒めを込めて記載しておく。
て、取ってなかった自分が悪いですがね、他のチームが作った部分で
ワーニング出っ放しなっていたし、あんまり気にしなかったので。
テストプログラムだしいいかと思っていたら、コアダンプ吐いて
どこが原因じゃってなった時に、自分が作った所から怪しむのだが
ワーニング取ってなかった。c言語やしポインタの箇所怪しいけど、、、
直してもコアダンプ吐くし、全部のワーニングとるしかねえなあ。。。
てなりました。結局他の人も、協力して全部取って動きましたとさ。
ES6でのthisについて

ES6から追加になったアロー関数ですが、もちろん短く完結に書けるという点で便利なのですが、  
他にもES5でよく悩まされたthisの扱いでも便利な点があることに気付いたため試してみます。

## ES5でのthis

以下のようなコールバック関数でthisを使うとします。  
*※ 後で気付いたのですが、constはES6以降なので純粋にES5で実行するにはvarですね。他にもテンプレートリテラルもないでしょ、とか本題と関係ないところはおいておきます。*

```js:aboutthis.js
const team = {
    members: ['太郎', '花子'],
    teamName: 'Aチーム',
    teamSummary: function() {
        return this.members.map(function(member)  {
            return `${member}は${this.teamName}の所属です。`
        })
    }
}

console.log(team.teamSummary())
```

```bash:実行結果]
$ node aboutthis.js
[ '太郎はundefinedの所属です。', '花子はundefinedの所属です。' ]
```

よくある勘違いなのですが、thisは'team'ではなく、'undefined'になってしまうわけです。  
これを解消するためによくやるのはthisを変数に取っておくこと。もう一つはbindでthisを束縛するやり方。

### 解決法その１　thisを変数に取っておく

```js:index.js
const team = {
    members: ['太郎', '花子'],
    teamName: 'Aチーム',
    teamSummary: function() {
        var self = this         //ここでthisを変数にとっておき
        return this.members.map(function(member)  {
            return `${member}は${self.teamName}の所属です。`    //ここで使う
        })
    }
}

console.log(team.teamSummary())
```

```bash:実行結果]
$ node aboutthis.js
[ '太郎はAチームの所属です。', '花子はAチームの所属です。' ]
```

無事、所属が出力されました。

### 解決方法その２　bindでthisを束縛する

```js:index.js
const team = {
    members: ['太郎', '花子'],
    teamName: 'Aチーム',
    teamSummary: function() {
        return this.members.map(function(member)  {
            return `${member}は${this.teamName}の所属です。`
        }.bind(this))
    }
}

console.log(team.teamSummary())
```

```bash:実行結果]
$ node aboutthis.js
[ '太郎はAチームの所属です。', '花子はAチームの所属です。' ]
```

さっきよりはすっきりしました。

## ES6では？　アロー関数

最初の状態に戻し、コールバック関数をアロー関数に変えてみます。

```javascript:index.js
const team = {
    members: ['太郎', '花子'],
    teamName: 'Aチーム',
    teamSummary: function() {
        return this.members.map((member) => {
            return `${member}は${this.teamName}の所属です。`
        })
    }
}

console.log(team.teamSummary())
```

```bash:実行結果]
$ node aboutthis.js
[ '太郎はAチームの所属です。', '花子はAチームの所属です。' ]
```

するとあら不思議、所属がちゃんと取得できてます。  
アロー関数にするとthisが呼び出し元と一緒になるんですね。  
レキシカル（lexical）thisといいます。  

## 最後に

lexical thisとは関係ない話ですが、今回の例題はもっとすっきり以下のように書けます。

```js:index.js
const team = {
    members: ['太郎', '花子'],
    teamName: 'Aチーム',
    teamSummary: function() {
        return this.members.map(member => `${member}は${this.teamName}の所属です。`)
    }
}

console.log(team.teamSummary())
```
初心者向け、kerasでよくあるエラーの対処法

pythonで機械学習をする上でよく使われるkeras(&tensorflow)。

kerasを使う上でのエラー対処法についてまとめた記事がなかったので、teratailの解決済み質問をもとに、稚拙ですがまとめておきます。

#ImportError,AttributeError
・バージョンを下げる、上げるなどで解決(ImportError,AttributeError)
・再インストールで解決することもある(ImportError)
・importするファイルが間違っている(AttributeError)
・tensorflow.kerasとkerasは別物
・タイプミス(大文字、小文字の区別はしっかりと)

kerasではいろんなライブラリのバージョンによる影響が大きい。古すぎたり新しすぎたりするとInportErrorを起こすことがある。

AttributeErrorについては、以下記事を参照。

[[python]「AttributeError: module(object) ‘xxx’ has no attribute ‘yyy’」が起きたときの対処法5選](https://qiita.com/VDiUZnM1hUIzKvb/items/4d18ca1d781ed6ff2b2f)

#ValueError

###共通項
・モデルのsummaryを確認するmodel.summary()
・print(data.shape)でデータの形状を確かめる

ValueErrorで多いのは、データの次元がモデルの期待と異なること。データの形状を確認し、違っていればreshape()を用いてデータを整形する。

[numpy.reshapeのドキュメント](https://numpy.org/doc/stable/reference/generated/numpy.reshape.html)

###Shapes A and B are incompatible
・モデルの出力と出力データの次元が合っているか

2値分類のはずなのにモデルの出力が3(Dense(3)とか)になっている場合など。model.summary()でモデルを分析する必要がある。

###expected ndim=A, found ndim=B
・Denseの入力は基本1次元配列なので、reshapeやFlattenで1次元に整形する
・もしくはinput_shapeを変える必要あり
・input_shapeは、batch sizeを含まない
・画像データは(サンプル数, 高さ, 幅, チャンネル) になるようreshapeする
・LSTMの場合[バッチ数, 時間軸, チャンネル数]とする必要あり

###expected layer_name to have shape A dimensions but got array with shape B
・RGBと白黒を間違えてないか(画像の場合)
・入力データとモデル入力の次元が合っているか
・1×要素数のテンソルにreshapeする(テストデータが1件のみの場合起こる)

###Input arrays should have the same number of samples as target arrays. Found A input samples and B target samples

・入力データと出力データの数が合っているか

#ResourceExhaustedError
・OOM(Out Of Memory、メモリ不足、ResourceExhaustedError)
・バッチサイズを下げる
・PCを再起動させる

GPUやパソコンのメモリ不足の際に起こるエラー。メモリを増やすか処理量を減らすのが一般的な方法。

ものによってはプログラムがメモリに蓄積？して発生することもあるので、再起動でリセットするとうまくいくことがある(今までうまくいっていた場合など)。

#学習精度が良くないとき
・データの前処理をする
・入力データ自体に問題がある
・予測データは合っているか
・ハイパーパラメータを変える
・機械学習でも学習できないことはある

画像データなら正規化([0,255]→[-1,1])できているか、入力データや予測データが破損してないかなどなど。

とにかくデータが正確かどうかは必ず確認する必要がある。データが正確でなければ、学習も予測も当然正確でない。

また、データの種類が足りない場合も、学習精度は上がらない。気温だけで天気予報ができたら予報士は苦労しない。

そして、機械学習で学習できないこともある。例えばサイコロの出る目はランダムなので、機械学習を使って予測することはできない。向き不向きはある。



#まとめ

以上、エラー対処法になります。

自分自身もそれほどプロフェッショナルというわけではないので、内容に間違いがあるかもしれません。その時はコメントにてご指摘ください。
Azure IoT CentralとIoT Hub

# Azure IoT Central と Azure IoT Hubは何が違う？
AzureでIoTソリューション/サービスを検討しようと思ったとき、Azure IoT Central(以下IoT central）とAzure IoT Hub（以下IoT Hub）のどちらを使うのがいいのか迷う、もしくはそもそもどんな違いがあるんだというところで思考停止してしまう人もいるのではないでしょうか。（自分だけ？
まず何が違うんだよと思ったので、その調べた過程と結論をまとめてみようと思います。

## IoT Central=SaaS、IoT Hub=PaaS説
ググるとよく見かけるのが、IoT CentralはSaaS（Software as a Service）、IoT HubはPaaS（Platform as a Service)という説。
IoT centralは完全にGUIから操作することを想定し、特定のユースケースに大してサンプルテンプレートをだし、
IoT HubにはViewの部分はユーザー次第となっているので、あながち間違いではないかも。
ただ、どこにカテゴライズされるかというのでわかったつもりになるだけで、
自分が今考えたいのはどういうときにどちらを選べばいいのか判断したい。ということだったことに気づく。

## IoT Hub はIoT centralを構成するOne of featuresだった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627617/e39bfd11-1ee2-4589-5ccf-30883eb9518c.png)
[公式ドキュメント](https://docs.microsoft.com/en-us/azure/iot-fundamentals/iot-services-and-technologies)におけるAzure IoTのポートフォリオを見ると、この二つは比較するものではない。
自分の知識不足による認識違い（よくある

## 結論（2020/09/02）
azureはサービスとかPaaSが増えてきて複雑になってきている。
利用シーンからどのサービスが自分たちにとってベストなのか慎重に確認する必要があるが、それにも限界があるので早めにMSの担当者に聞いた方が良い（MSの宣伝をしているわけではないですw
また、サービスが増えたことに伴い、azure portalのトップページも、メニューバーはあるが検索から入るようなナビゲーションになっているのは非常に興味深い

## 追記(2021/6/26)
AZ-900の試験勉強をしていると、この類の記載があったので
やはり勉強不足であったことを再認識。
ついでにAzure Sphereというのもけっこう使えそうなサービスということもわかり、
これはデバイスが改ざんされていないことを保証するためのセキュリティを提供してくれるものである。
既存のプロジェクトをMVPにして気づいた点、注意点

 
## はじめに

現場ではCocoaMVCだったのですが、まぁ今後のプロダクトでどのよーなアーキテクチャが良いか
比較して、実際に既存のプロダクトの１画面作り変えてみました。
実装→iOSチームでレビューの過程ででた所感や気づきを共有します。

MVP自体はネットにゴロゴロありますし、
MVP自体についてざっくり知りたい方はアーキテクチャ比較も投稿したのでよければ。

## ざっくり所感

- 各コンポーネントの役割が決まっているので、読みやすい。
- ユーザーの入力部分について（ViewController）とPresenter,Modelと作業分担しやすい。
ただ、作る際にある程度認識合わせ、ルールを考えておかないと実装漏れやコンフリクトのような問題が起きそう。

- ViewControllerの肥大化は抑えられるが、FatPresenterになってしまう恐れがある。
  例）Presenterは画面遷移やViewの操作も全て行うのか。
　　今回は全部行うようにしたが、「Delegate多すぎ」って意見もあったので。
- viewDidLoadなどのライフサイクルイベントもviewからのユーザー入力としてprotocol化したけど、分かりにくいという意見もあった。一応サンプルではそうなっていたし、PresentationLogicも全てってなった時にそのほうが自分的には抵抗なかった。

- 2人開発では必ずしも取り入れるものではないと感じた。
- viewController自体は100行~くらい減らせた。
- FatViewControllerの解消、可読性の向上は少し実感できた。
- UIテストしやすい。
- 既存のコードの変更は学習しながら1.5日くらい。慣れればもっと早くなる。
- Cocoa MVC,Delegate, protocolを理解していればそんなに学習コストは高くなさそう。

## 最後に

可読性ってそのターゲットによって違うと思うので、逆にMVPのパターンを知らないと見辛くなる場合もあるんだなと。
MVVM編も投稿しようと思います。