OmniAICreator/Qiita-1.07M huggingface dataset:
仕事を辞めて「フィリピン留学でプログラミングを勉強」した話

# 仕事を辞めて「フィリピン留学でプログラミングを勉強」した話
今、流行りのフィリピン留学で英語勉強のついでに「プログラミング」を勉強しました。
留学前に情報収集したくても、体験記みたいなものが少ないので「こういうサイトがあれば良かったなぁ」という経験から書かせていただきます。

## フィリピン留学を選んだ理由

####1. マンネリ化した業務への不満
当時私は、製薬会社の工場に勤務しておりそれなりの給料を頂いていました。
ただ、ルーチン業務という「マンネリ化」に退屈しており、成長を実感できずに不満を抱えており、**大胆に環境を変えたい**と思っていた。
####2. プログラミングへの興味
会社では社内SEとして働いていました。ただ、SE（システムエンジニア）という仕事は詳しいITの知識はなくてもできる仕事で、「知識が身につかない」という点で不満を覚えていました。また、**最低限のプログラミングが読み書きできないSEはお荷物**という記事をいくつも目にし、プログラムを書けるようになりたいと考えるようになった。
####3. IT業界と英語の親和性が高い
留学を決める前に[progate](https://progate.com/)や書籍でプログラミングの勉強した際に、プログラミングは英語で構成されていることに気づきました。また、プログラミングする際にはフレームワークやライブラリといったプログラミングのサポートツールを使用するのですが、これらの説明が英語でされていることも多く、**IT勉強に英語は必須**と感じました。
####4. 値段が安い
IT留学は最近の流行りですし、検索すれば相当数がヒットすると思います。フィリピン以外だと「オーストラリア」や「カナダ」などが人気です。
その中でフィリピンを選んだ理由は、とにかく安いからです。他国と比べると半額以下がほとんどです。

## 授業での英語力は心配ない
フィリピン人講師　＋　日本人講師（サポート）　＋　生徒（数名）でクラスが構成されます。
同期の生徒をフィリピンでは「バッチメイト」と呼ぶようで、バッチメイトは私を含め4人でした。一人を除きほぼ全員が元社会人で平均年齢は23~24歳ぐらいでした。（当時24歳でした）
肝心の授業は、すべて英語です。ただ、授業のパワーポイントは英語＋日本語で記載されており、英語が全く分からなくても大丈夫です。

## 授業外での勉強は必須
なんでもそうですが、スタートダッシュが肝心です。
留学最初の週に「**自主学習する癖**」をつけましょう。上で「英語が分からなくても大丈夫」と書きましたが、あくまで授業の話です。3か月という限られた時間で「プログラミング」と「英語」を身に着けるには、確実に自主学習が必要です。
私のバッチメイトの中に、**講師に英語で質問できないまま留学を終えた**人がいました。

私の場合はプログラミングの基礎を留学前に抑えていたので、プログラミングよりも英語を勉強していました。
留学後にはTOEICが700点ぐらいになっていました。（留学前は300点前後）

## 留学後の就職について
留学後はとにかく学んだ知識を早く使いたくて、国内が問わず仕事を探していました。
「プログラミング＋英語」という人材は国内ではかなり稀少な様で、15社受けて13社で内定をいただきました。（中には皆さんも知る大企業も入っています）
また、国外でも2社で内定いただきました。
今は東京の中小企業でプログラマ兼webマーケターとして働いています。

## 最後に
私が留学したのは2019年4月～2019年6月の3か月間です。その間に多くのことを学び、経験しました。もちろん、時には観光しジンベエザメと遊泳なんかも楽しみました。
海外でのプログラミング研修にハードルを感じている皆さんの一押しになればと存じます。

ちなみに私が参加したIT留学は[こちら](https://xn--mckzb595rgyv.com/global-it/)。
あと、フィリピン留学中にIT留学を提供してくれているsun*社にインタビューしていただきましたので[参考に](https://framgia.com/study-abroad/graduate-interview-naoki_iwamoto/)。
Minttyのフォント設定でConsolasを使いつつ日本語だけメイリオにし、同時にEast Asian Ambiguous Width Character問題も解決する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136875/0bb583da-4e30-9687-6c07-dab87612e200.png)

.minttyrcで以下の設定をする。

```
Font=Consolas
FontChoice=Hiragana:2;Katakana:2;Han:2
Font2=Meiryo
Locale=C
Charset=UTF-8
```

* ○、△、★などの記号で表示が崩れるEast Asian Ambiguous Width Character問題も、Consolasの半角フォントが使われることで問題が起きなくなる
* tmux、Vimなども対策が不要になる
* MinttyにEast Asian Ambiguous Width Characterを半角で描画してもらうために、Locale=Cの設定が必要な点に注意
  * ja_JPにしたい場合は、.bashrcで上書きすれば良さそう
* 恐らくFontChoiceはバージョン3.0.2 (13 July 2019)以降でないと使えないので注意
自分用

divとは、タグで囲った部分をブロック要素として、グループ化できるタグ。
グループ化することによって、align属性により、水平方向の位置をまとめて指定したり、指定した範囲にスタイルシートを適用できる。
同じ意味を持つタグとして、spanタグがある。
divは、ブロック要素としてグループ化を行うので、見出しや段落、リストなどとして見なされ、前後に改行が入る。
spanタグはインライン要素で文章の一部として利用されるので改行はいらない。
データ分析初心者がML-Askでサラリーマン川柳を感情分析したみたら失敗した。

#記事を作成した背景
今後データ分析に携わる職種に就くにあたって、Pythonでデータ分析の練習をしたいなと考えました。
そこで以下のHPを発見し、真似して挑戦してみました。
[pythonで感情分析に入門](https://gkalumnium.com/devlog/python%E3%81%A7%E6%84%9F%E6%83%85%E5%88%86%E6%9E%90%E3%81%AB%E5%85%A5%E9%96%80/)

なんとなくサラリーマン川柳はネガティブな句が多いイメージなので、その仮説が正しいかを検証してみようと考えました。
※初めに書いておくと、残念ながらタイトル通りうまいこと検証できませんでした。

#概要
以下から取得したサラリーマン川柳をすべてML-Askにて1句ごとに「ネガティブ」「ポジティブ」「ニュートラル」のいずれかを判定し、それぞれがどこに属するのかを振り分けて集計していきます。
判断できないものは「例外」として別途集計します。
[第33回サラリーマン川柳｜キャンペーン情報](https://event.dai-ichi-life.co.jp/company/senryu/)

対象データは第32回の優秀作（1位～100位）と第5回以降の1位の作品です。


ML-Askの分析結果は以下のような辞書型で出力されるので一番わかりやすい「orientation」から判断を行います。

```

>{'text': '近年では、親愛の情を込めて友人、知人や親族にも贈るお歳暮へと少しずつ変化してきているようです。',
> 'emotion': defaultdict(<class 'list'>, {'suki': ['親愛']}),
> 'orientation': 'POSITIVE', 
> 'activation': 'NEUTRAL', 
> 'emoticon': None, 'intension': 0, 
> 'intensifier': {}, 
> 'representative': ('suki', ['親愛'])}

```

#コーディング
大したコードではないのですべて記載します。

```python:サラリーマン川柳.py
#必要ライブラリをインポート
from mlask import MLAsk
#ファイルのオープン
file_data = open("C:\\Users\\owner\\Desktop\\サラリーマン川柳.txt","r",encoding="utf-8")
#※上記のデータは上述のＨＰより対象川柳部分のみ張り付けただけのテキストファイルです。
emotion_analyzer = MLAsk()
#初期化
senryu =[]
positive_count = 0
negative_count = 0
neutral_count = 0
exception_count = 0
for line in file_data:
    #改行コードを削除して、感情分析を行います。
    emotion_senryu = emotion_analyzer.analyze(line.replace("\n",""))
    #orientationが設定されない場合があるので、try exceptで囲っています。
    try:
        if  "POSITIVE" in emotion_senryu['orientation']:
            positive_count += 1
            #print("POSITIVE") ←デバッグ用です。
            #print(line)
        elif "NEGATIVE" in emotion_senryu['orientation'] :
            negative_count += 1
            #print("NEGATIVE")
            #print(line)
        else:
            neutral_count += 1
    #判定不能の場合はexception_countに割り振ります。
    except:
        exception_count += 1
#トータルの件数
print("ネガティブ：" + str(negative_count))
print("ポジティブ：" + str(positive_count))
print("ニュートラル：" + str(neutral_count))
print("例外："+str(exception_count))

```

#実行結果
以下のようになりました。

|    感情    |    件数    |
|:--------:|:-------------:|
|ネガティブ |8
|ポジティブ |3
|ニュートラル|3
|例外        |113

例外が発生しすぎています。。。

例外処理が発生した分析結果を抜き出してみると、以下の通りorientationが設定されていないことが分かります。

```
{'text': '五時過ぎたカモンベイビーUSAばらし', 'emotion': None}
{'text': 'いい数値出るまで測る血圧計', 'emotion': None}
{'text': 'メルカリで妻が売るのは俺のもの', 'emotion': None}
{'text': 'ノー残業趣味なし金なし居場所なし', 'emotion': None}
{'text': '「やせなさい」腹にしみいる医者の声', 'emotion': None}
{'text': 'やっと縁切れた上司が再雇用', 'emotion': None}
{'text': '手紙書き漢字忘れてスマホ打ち', 'emotion': None}
{'text': '下腹が気づかぬうちにひょっこりはん', 'emotion': None}

```

#結論

確かに判断できたものでは「ネガティブ」が多めではありましたが、さすがに母集団が127件で判断できないものが113件もあったために、分析ができていないと言わざる負えません。

どうしてこうなった。。。

なので今後もし、似たようなことを行う場合の対策と反省を自分用に記載しました。

【対策】
 今回は辞書ベースの感情分析だったので別の手法を用いて分析を行えば、もう少し良い結果が出るのではないかと思います。
 ※例えば以下のからの引用ですが、機械学習で正解付きコーパスを学習させるなどです。

[pythonで日本語文の感情分析（＋言語処理の基礎）](https://qiita.com/matsu0228/items/0323f299d03f5b07efdc)

【反省】
そもそも感情分析の練習が目的なのだから川柳ではなく、twitterの文章のようなわかりやすい別の題材で行うべきでした。
川柳は文字数の制限や、「好き」「嫌い」と直接的な表現ではなく文脈で情景や感情を表現するため、練習で行うには不適切でした。


もしよかったら、「ここをこうすればよかったのに」といったアドバイスなどをいただけると嬉しいです。

以上です。
20191028 ソフトウェアテスト・QA勉強会【（仮）テスト自動化の光と闇（1年間の苦労話）】

#Outline

テスト自動化・自動テストの取り組みについて、D-Cubeから講演のお誘いがありました。
そこで、業務上の概ね闇ばかりをお話ししました。
https://d-cube.connpass.com/event/149831/

<A HREF="https://www.slideshare.net/SadaakiEmura/20191029-automation-struggle"><IMG SRC="https://image.slidesharecdn.com/20191029d-cube-191029013859/95/slide-1-1024.jpg" width=80%></A>


#Contents

## 1. 自動化を立ち上げる時の闇（苦労）

<B>状況</B>

- テスト自動化をまだ導入していないフェーズ
- マニュアルテストでなんとか品質は担保できている
- テスト自動化はコストがかかるし、コストだけではなく初めて見ることに高い壁を感じていた

このような状況のため、テスト自動化をやってみようという雰囲気ではなかった。
机上でROIの議論はでたものの、実際に自動化がどんなもので、どんな効果があるのか見えにくかった。
自分は、エンジニア経験があったため、作業効率化含めて自分でやってみればいいんじゃないかと思っていた。

そこで、周囲の人にテスト自動化を正しく理解させることから始める必要があった。
QA立ち上げ時でもあり、アプリエンジニア経験あるのは自分だけなので、一人で隙間時間でPOCを行った。

- 自動化で、テストや、それ以外の作業効率化を実数値で見える化
- 当然、コストメリット（時間コスト含む）も試算

このような経緯でもって、テスト自動化の予算承認が下りて導入に漕ぎつけたが、次の課題が出てくる

<B> 状況 </B>

- テスト自動化エンジニアが少ない
- 現在のQAプロセスで自動化をどのように使うのかがわからない

いまのQAメンバでできるテスト自動化のプラットフォームを構築。
Open Sourceでコードバリバリ書くスタイルはやめました。
今のチームでできるメンバーがほどんどいなく、かつメンテナンスコストもかかるからです。
そこで有償ライセンスのテスト自動化をベースに、環境を構築した。

そのうえで、QAプロセスでテスト自動化の活動をどのようにいれるかも、試行錯誤した。

## 2. 自動化が立ち上がった時の闇（苦労）

<B>チーム内の状況</B>

- 処理を自動化しているが、validationしてなく、テストになっていないものが多い
- 自動化の作成数がメインとなり、マニュアルテストでもあまり価値のないテストの自動化までも行われる

当初、”この機能を自動化しよう”という風に、自動化の詳細な内容に関しては明確にしてこなかった。
そのため起きた事象。
マニュアルテスト経験の人もいたが、アプリエンジニア経験が強い人が多かったこともあり、このようなことが起きてしまった。
そのため、自動化の定義を明確にして進めないといけないと感じた。

まずは、テストを自動化することが目的なので、マニュアルと同様に、なにをvalidateするのかを明確にし、それに基づいて実装するようにした。
次に、自動化のする・しないの判断軸を明確にする。例としてアジャイルテストの4象限があります。
自分のチームに適した、自動化のする・しないの基準を設け、それをもとに自動化を取り組んだ。
また、せっかくの自動化なので、テスト以外で自動化により作業改善できる事、例えばテストデータ作成等、そういった多方面の使い方も検討に入れた

<B>チーム外からの状況 </B>

- テスト自動化の過剰な期待が高まる
- すべてが自動化に置き換わるのでは？説

テスト自動化をよく理解していないステークホルダからは、銀の弾丸のように見えてくることがある。
テスト自動化が全てやってくれるのでは？　と。
自動化の予算をとったこともあるため、自動化に関して消極的なことは言えない。
とはいえ、マニュアルテストと自動化をバランスよく進めないと、前に進めないことを説明する必要がある。

これは地道に、テスト自動化に関して、例えばテスト自動化の8原則のようなことを説明するしかなかった。
テストエンジニアでも自動化の理解が乏しい人がいたので、モブプロちっくに実際に自動化作成をやらせてみせて、体験してもらうこともした


## 3. 自動化が安定してきたときの闇（苦労）

<B>テスト環境の状況 </B>

- テスト環境のインフラ（ネットワーク、サーバー等）が全社共通で利用しているため、不安定
- アプリケーションが頻繁に更新して、不安定
- テストデータが、複数人利用して不安定

それにより、自動化がよく落ちる。
よく落ちるため、その原因調査、復旧に時間が取られてしまう。

これを回避するために、まず最初にしたことが、失敗の可視化。
JENKINSを複数起動してテスト自動化しているが、これを毎日定期的に全てチェックするのは大変。
teamsで失敗した時、通知する方法もとっているが、現時点どれだけfailしているかがわからない。
そして、よく失敗するテストがなにかがわからない。
それらを可視化する仕組みをつくった。
可視化ができれば、よく落ちる原因をもとにエンジニアと環境整備等をはたらきかけ、
自動化のスクリプトもその不安定さに耐えうる実装できないかの検討もしぼりこみができ、着手しやすくなった

<B>プロダクトの状況 </B>

とはいえ、自動化が落ちるのはそれだけではない。
プロダクトに変更が入れば、自動化はfailする。

- 2週間サイクルでちいさな改修が発生する
- 重要な機能にも改修が入る
- 自動化の考え方として、頻繁に変更の入るところの自動化は不向きというが、そのスタンスは取っていない

このため、自動化のスクリプトを定期的にプロダクトに追従する必要性がある。
これを実現するために、以下のようなフローにした

- プロダクトの更新と既存の自動化スクリプトの影響範囲の確認をするフロー
- 自動化のスクリプティングに優先度を決め、先にfailするであろう、既存scriptの改修から作業する。最悪新規機能の自動化はリリース後に対応

## 4. 現在の闇（苦労）

いまのQA組織立ち上げから約3年間自動化をやっていますが、テスト自動化のエンジニアが少ない。
協力会社にも協力を仰いでいるが、それでも少ない状況。
それをなんとかしたい！
sqlldrのskipオプション

sqlldr でskipオプションを付けて実行することがあった。

```powershell:SQL*Loader
sqlldr XXXXX/XXXXX CONTROL=test.ctl skip=1
```

この時ヘッダ有の複数のcsvファイルを読み込んでおり、
連番をSEQUENCEオプションを使って採番していたのだが、
連番の最大値と件数が一致しない。

どうもskipオプションでスキップするのは最初のファイルの1行目だけみたい。
なので結局ヘッダを全部削除してskipオプション無しで実行することで対応。
EKS上でELBを動かす

## Nginxが複数起動するDeployment

```sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80
```

## デプロイ

```
kubectl apply -f sample-deployment.yaml
```

確認

```
~/Desktop ❯❯❯ kubectl get pod | grep sample-deployment
sample-deployment-6cd85bd5f-48pp4   1/1     Running            0          39s
sample-deployment-6cd85bd5f-swkq8   1/1     Running            0          39s
sample-deployment-6cd85bd5f-zl9sh   1/1     Running            0          39s
```

## ロードバランサー用サービス

```sample-lb.yaml
apiVersion: v1
kind: Service
metadata:
  name: aws-lb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
  labels:
    app: sample-app
spec:
  type: LoadBalancer
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 80
      targetPort: 80
  selector:
    app: sample-app
```

## デプロイ

```
kubectl apply -f sample-lb.yaml
```

## 確認

```
k get service aws-lb
```

出力結果

```
NAME     TYPE           CLUSTER-IP   EXTERNAL-IP                                                                     PORT(S)        AGE
aws-lb   LoadBalancer   10.100.0.6   adef5be59fa4111e9919f0251c115a68-cc4f6a95cee97702.elb.us-east-1.amazonaws.com   80:30859/TCP   59s
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293481/4fc9e584-36b6-cc6b-8cef-7dacedd2ae4c.png)
初心者によるプログラミング学習ログ 140日目

# 100日チャレンジの140日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

140日目は、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>140日目<br><br>メンターさんのjavascript課題<br><br>疲れて寝落ち<br>二日続けて…<a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/javascript?src=hash&amp;ref_src=twsrc%5Etfw">#javascript</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a> <a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1187477228000407552?ref_src=twsrc%5Etfw">October 24, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[Kotlin]AndroidでiOSのUiPageViewControllerのdots（ドット）有を実装

# TODO
 - チュートリアル等で使われる横スクロールして１ページずつ閲覧する画面をAndroidで実現する

例はiOSのホームです
<img width="375" alt="File.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199764/8d87a6b8-8e6d-e6ae-2b19-e138c8e5e892.jpeg">

# 実装
ViewPagerとTabLayoutで実現します。
ドット部分がTabLayoutです

このselectorがキモです
選択状態白く、非選択状態ではunselectedIndicatorで灰色にするとiOSに近いです

```indicator_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <shape
            android:innerRadius="0dp"
            android:shape="ring"
            android:thickness="4dp"
            android:useLevel="false">
            <solid android:color="@android:color/white" />
        </shape>
    </item>
    <item>
        <shape
            android:innerRadius="0dp"
            android:shape="ring"
            android:thickness="4dp"
            android:useLevel="false">
            <solid android:color="@color/unselectedIndicator" />
        </shape>
    </item>
</selector>
```

```fragment_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/sign_walk_through_view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/sign_walk_through_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:layout_marginBottom="8dp"
        android:background="@android:color/transparent"
        app:tabBackground="@drawable/indicator_selector"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp" />

</FrameLayout>
```

```WalkThroughFragment.kt
class WalkThroughFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_walk_through, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        adapter = WalkThroughFragmentPagerAdapter(childFragmentManager)
        walk_through_view_pager.adapter = adapter
        walk_through_indicator.setupWithViewPager(walk_through_view_pager)
    }

    private inner class WalkThroughFragmentPagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {

        override fun getItem(position: Int): Fragment? {
            when (position) {
                0 -> return WalkThroughFirstFragment()
                1 -> return WalkThroughSecondFragment()
                2 -> return WalkThroughThirdFragment()
                3 -> return WalkThroughFourthFragment()
            }
            return null
        }

        override fun getCount(): Int {
            return 4
        }
    }
}
```
yarnでハマった話「Incorrect integrity when fetching from the cache」

#yarnを使ってパッケージをインストールした際にハマった

以下のようにパッケージをインストールした際にエラーが出ました。

```terminal
yarn add gatsby-source-contentful
```


**エラー文**

```terminal
yarn add v1.19.0
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
error Incorrect integrity when fetching from the cache
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.
```


#エラー文の意味

```terminal
Incorrect integrity when fetching from the cache
```
単純に日本語訳してあげると
「**キャッシュからフィッチしてきて際に、整合性が取れてないよ**」
という意味らしい。

グローバルにインストールしたyarnを使う際に、プロダクト内のnpmとのcacheとバッティングしているのが原因なようです。

#解決法

yarnのキャッシュを削除してあげましょう。

```terminal
yarn cache clean
```

ちなみにcacheは以下のコマンドで確認できます。

```terminal
yarn cache dir
```


プロダクト内でyarnを再インストールします。以下のようなコマンドでインストールすることで、キャッシュなしでインストールすることができます。

```terminal
yarn install --no-cache
```

これで、パッケージをyarnでインストールすることができるようになりました！

#まとめ
yarnをグローバルインストールするのはあんまり良くないのかなあ。
詳しい方教えてください。

#参考

[yarn add](https://yarnpkg.com/ja/docs/cli/add)
[yarn cache](https://yarnpkg.com/lang/ja/docs/cli/cache/)
【UiPath】エクセルからコピーしてシステムに貼り付ける動作をExcel無しで行った

タイトル分かりにくいですよね。すみません。。

背景としましては、

作業員の方が作業する際は、システムに40項目くらいある（横一列の）項目を一つ一つ手で入力していっているらしい。

ただ実はもう一つやり方があって、ExcelからデータをCtrl+Cでコピーして、基幹システムにそのままCtrl+Vで貼り付けるというやり方でもうまくいくらしい。


※Excelのイメージ図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/5344d320-e328-e56b-7305-7bb14c577e8b.png)


UiPathで実行する際には、Excelからコピーをしてきて、Ctrl+Vで貼り付けるのが確実だろうということで、そちらを採用することにしました。

# 最大の課題：別にExcelからデータを取ってくるわけでもないのに、Excel開きたくねぇ

そうなんです。実はこのやり方を採用しようとしていて、、、

1. データをいろんなところから取ってくる
2. UiPath内部でデータテーブルを構築して、データテーブルに整理して格納する。
3. Excel開く
4. Excelに範囲を書き込み
5. Excelからコピー
6. システムに貼り付ける

**3~5はできればExcel使いたくねぇ！！**

となっていました。

そしてどうやったかといいますと。。。

# ExcelはTabで区切られた一つの文字列らしい

Excelからデータをコピーしてメモ帳に貼り付けると、こうなるんですよ。

```
aa	ii	uu	ee	oo	kaka	kiki	kuku	keke	koko
```

Excelってタブ区切りなのか！！

というわけで、

1. データをいろんなところから取ってくる
2. UiPath内部でデータテーブルを構築して、データテーブルに整理して格納する。
3. Excel開く
4. Excelに範囲を書き込み
5. Excelからコピー
6. システムに貼り付ける

これから、

1. データをいろんなところから取ってくる
2. UiPath内部でデータテーブルを構築して、データテーブルに整理して格納する。
3. データテーブルを出力する
4. （ヘッダーが一行目にくっついてきてしまうので）一行目を削除する
5. カンマをタブに置換する
6. システムに貼り付ける


これに置き換えました。ちなみにこれでいけました。すごい！


# 技術的な話

データテーブルを出力して、一行目を削除して、カンマをタブ区切りにした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/49caf095-8024-eb3a-3296-d0696af8fd6b.png)



一行目を削除はこちら

```
str_data.Remove(0, str_data.IndexOf(vblf)+1)
```

カンマをタブ区切りにした（なぜかvbTabだとできなかったです。何が違うんだろう）

```
str_data.Replace(",", ControlChars.Tab)
```

です！

Excelがタブ区切りだってことに驚き！！しかも、技術的にそんな難しくないですね！

#おわりに

Excelからコピーして何かに貼り付ける作業で、Excelを開きたくないことから始まった戦いですが、無事にExcelを開くことなく完成しました。

なかなかこの現象を伝えるのが難しいですが、うまく伝わりましたでしょうか...？

ぜひ活用してみてください！
BigQueryにエクスポートされたGA360のデータを移行する方法

BigQueryにexportされたGA360のテーブルを手っ取り早く他のプロジェクトに移行する方法をいくつか考えてみました。

- bq extract コマンドでエクスポート

```
bq extract \
--destination_format AVRO \
'dataset.ga_sessions_*' \
gs://bucket/ga_sessions-*.avro
```
->結果、エラーになってダメだった。

- データセットごと共有する -> これが一番簡単？

- 全件検索して destination_table にコピー先を指定
うまくいったが、日付別テーブルにならないので--time_partitioning_fieldオプションでdateを指定しないといけない。ただ、GA360のdate列がSTRINGになってたので面倒で以下のようにしてしまいましたorz

```
bq query --destination_table project:dataset.ga_sessions --use_legacy_sql=false 'SELECT * FROM `project.datasret.ga_sessions_*`'
```

どなたか他に良い方法ご存知でしたらご指摘ください！
[ゼロから作るDeep Learning]自作ニューラルネットワークの学習機能使い方

#はじめに
この記事は**ゼロから作るディープラーニング 5章ニューラルネットワークの学習**を自分なりに理解して分かりやすくアウトプットしたものです。
文系の自分でも理解することが出来たので、気持ちを楽にして読んでいただけたら幸いです。
また、本書を学習する際に参考にしていただけたらもっと嬉しいです。


#自作ニューラルネットワークに実際に学習させてみた
前回フレームワークなどを使わずにニューラルネットワークを実装し、完成させました。
今回は、前回作ったニューラルネットワークに学習をさせたいと思います。

**学習の手順**

**1,** 学習データの数が多い為サンプリングして量を減らす(ミニバッチ処理)

```python
#ミニバッチ取得
batch_index = np.random.choice(train_size, batch_size)#サンプルのインデックス取得
x_batch = x_train[batch_index]# 入力データのサンプルを変数に入れる
t_batch = t_train[batch_index]# 正解データのサンプルを変数に入れる
```

np.random.choiceで学習用データの中からサンプリングをして、選ばれた学習用データのindexを取得する。
さっき取得したサンプルのindexが入った配列を使って、学習用データの配列からサンプルに選ばれたデータだけを参照する。

**2,** 勾配を出す

```python
#ミニバッチ取得
batch_index = np.random.choice(train_size, batch_size)#サンプルのインデックス取得
x_batch = x_train[batch_index]# 入力データのサンプルを変数に入れる
t_batch = t_train[batch_index]# 正解データのサンプルを変数に入れる

#勾配を求める
grads = network.slopeing_grad_net(x_batch, t_batch)
```

前回作ったニューラルネットワークの勾配を求めるクラス関数を使って、勾配を求める。


**3,** 勾配を元にパラメータのの更新

```python
#ミニバッチ取得
batch_index = np.random.choice(train_size, batch_size)#サンプルのインデックス取得
x_batch = x_train[batch_index]# 入力データのサンプルを変数に入れる
t_batch = t_train[batch_index]# 正解データのサンプルを変数に入れる

#勾配を求める
grads = network.slopeing_grad_net(x_batch, t_batch)

#パラメータの更新
    for p in ['W1',　'b1',　'W2',　'b2']:
        network.params[p] = network.params[p] - (learning_rate　*　grads[p])# 勾配降下法で、元パラメータから勾配を引くことで、勾配が示す損失関数の最小化に近く。
```

勾配はニューラルネットワークの学習でいうと、損失関数を最小化させるための最適変数の方向を表す。
そのため、勾配を引くと、最小の損失関数に近づく。

この作業を何回も繰り返すことで、徐々に損失関数が小さくなっていき、精度が増していく。
HTML+CSSでシュッと出てくる折りたたみメニュー

作りました。

<p class="codepen" data-height="250" data-theme-id="0" data-default-tab="html,result" data-user="mui_nyan" data-slug-hash="xxxLBgj" style="height: 250px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="クリックでシュッと出るメニュー">
  <span>See the Pen <a href="https://codepen.io/mui_nyan/pen/xxxLBgj">
  クリックでシュッと出るメニュー</a> by むいにゃん 🧗 (<a href="https://codepen.io/mui_nyan">@mui_nyan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# ソース

```html
<details>
    <summary>絞り込み</summary>
    <div>
        シュッと出てくるコンテンツ
    </div>
</details>
<p>次のコンテンツ</p>
```

```css
details[open] summary ~ * {
    animation: sweep .2s ease-out;
    margin-left: 1em;
}

@keyframes sweep {
    0%    {opacity: 0; margin-left: 0}
    100%  {opacity: 1; margin-left: 1em}
}
```

# 解説

details要素とsummary要素の組み合わせにより、スクリプトなしで折りたたみメニューが実現できます。
この要素が使えるようになる以前は、スクリプトなしで実現するにはcheckboxと組み合わせてトリック的なCSSを書いていました。↓みたいなやつ

```css
.block {
    height: 0;
    overflow: hidden;
    transition: .2s;
}
.checkbox:checked + .block{
    height: 100px;
}
```

この手法と比較して、details/summary要素ならばHTMLのみで折りたたみが作れる特徴があります。
ただし欠点として、開閉時にCSS Transitionを効かせることができません。
代わりに、`animation` プロパティを利用して出現時にシュッとさせています。

# 参考文献
* https://stackoverflow.com/questions/38213329/how-to-add-css3-transition-with-html5-details-summary-tag-reveal
Electronインストール for Ubuntu18

Ubuntu18 PCへのElectron環境インストール手順とサンプルアプリの実行方法をまとめています。

## Electron環境構築

Node.js等の必要なパッケージ類をインストールします。

````
$ sudo apt install nodejs
$ sudo apt install npm
$ sudo apt install libappindicator1
````

Electron本体（中身はChromium + Node.jsライブラリ群）はグローバルな環境にパスを通してインストールしておきます。

````
$ sudo npm install electron -g
````

## サンプル実行
#### Quick start Electron demo app
よく出てくるElectronのクイックスタートサンプルです。

```
$ git clone https://github.com/electron/electron-quick-start
$ cd electron-quick-start
$ npm install
$ npm start
```

#### YouTube Music Desktop App
YouTube Musicのデスクトップアプリです。よく出来ています。

```
$ git clone https://github.com/adlerluiz/ytmdesktop
$ cd ytmdesktop
$ npm install
$ npm start
```
Pythonで渦巻をお絵描きしたい

#Pythonで渦巻描いてみた
Pythonで渦巻描いてみた

```python:uzumaki.py

import numpy as np
import matplotlib.pyplot as plt

theta = np.arange(0.0, 4 * 2 * np.pi, 0.01)
r = 0.5 * theta
plt.polar(theta, r)

plt.show()
```

#実行結果

![B10F130D-BCDC-4367-8020-0CA2D7EA7E4A.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362045/dcbb8639-7260-0e81-da42-36986954d9e6.jpeg)
[VSCode] エディタで使用するフォントを変える

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
VisualStudio Codeの標準設定のフォントは等幅のフォントではないようで、微妙に縦の文字位置がずれる。等幅フォントを使用するようにして、縦の位置がそろうようにしたい。

# やり方
- `Ctrl + ,`を押し、設定画面を出す。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/2ecf2b7c-8f20-4629-e5a5-7abcd93dacbf.png)
- `Font Family`に、使用したいフォントを入力する。(ここでは`MS Gothic`を指定)

# 参考
Visual Studio Codeで等幅フォントを使う
https://minus9d.hatenablog.com/entry/2018/12/22/111047
[VSCode] テキスト入力窓側でも表がきれいに見えるようにする

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
VisualStudioCodeで.mdファイルに表を書くと、プレビュー窓側では表になって見えるが、テキストを入力している窓の方ではなんだかごちゃっとした文字が並んでる感じになってしまって、表のこのセルの文字を直したいのにどこを直せばいいか直感的にわからない。どうにかならないか。

# やり方
`Table Formatter`をインストールして使う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/8ea5c64c-eddc-8b86-b24c-ab1029815464.png)

これを入れてから下記の手順をすると、見やすく整形してくれる。

- `Ctrl + Shift + P`を押してコマンドパレットを出す
- パレットに`Table: Format Current`と入れて、Enterを押す

これで、整形してくれる。

他にもテーブルのテキストを整形してくれるアドオンがあったが、日本語を入力したときにきれいにならないなどがあったので、この`Text Formatter`に落ち着いた。
[VSCode] クリップボードにある画像を直接貼り付けできるようにする

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
VSCodeで手順書などを書く際、画像を貼りたいときがある。
いちいち画像ファイルを作成して、mdと同じところに保存して、mdの中にパスを書く、とかしたくないので、なにか便利な方法がないか？

# やり方
`Paste Image`というExtensionを使用する。
https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/b532f5c0-6d4a-8e33-e598-51a77ddbb2fe.png)

これをインストールして、クリップボードに画像がある状態で`Ctrl + Alt + V`を押すと、mdファイルと同じ階層に張り付けた画像が保存され、mdにもそのパスが入力される。

# 自分の使い方(Windows10 19H1時点)

- `Ctrl + Win + S`で、画面キャプチャする
- 画面右下に「切り取り領域がクリップボードに保存されました」が出るので、表示が出ている間にそこをクリック
- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/3219e35b-68a7-0416-ba31-fe663935e4e2.png)
- 「切り取り領域とスケッチ」画面が出るので、手書きでメモなどを追記し、`Ctrl + C`でコピー
- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/23b16b8d-feaf-740c-3944-a7f52f147d04.png)

- それをVSCodeで`Ctrl + Alt + V`で貼り付けする

おわり
YAMLで複数行テキストを書きたい時のあれこれ

# TL;DR

```sample.yml
text0:
  吾輩は猫である
  名前はまだ無い
text1: |-
  吾輩は猫である
  名前はまだ無い
text2: >-
  吾輩は猫である
  名前はまだ無い
text3: "\
  吾輩は猫である\
  名前はまだ無い"
```

```rb
YAML.load_file('sample.yml')

=>
{
  "text0"=>"吾輩は猫である 名前はまだ無い",
  "text1"=>"吾輩は猫である\n名前はまだ無い",
  "text2"=>"吾輩は猫である 名前はまだ無い",
  "text3"=>"吾輩は猫である名前はまだ無い"
}
```

# 発端
Railsでの設定ファイルとして、Yaml形式がよく使われています。特にi18n用のファイルはよく長文を記述されます。しかし、長文を一行で書くことは保守上問題ですし、改行を入れて複数行にすると、変な改行やスペースが残されますし、今まではやむをえず、読み取り側で改行を取り除くロジックを組みました。
色々調べて、やっと[こちらの回答](https://stackoverflow.com/a/21699210)にたどり着いた。

# わかったこと
`Yaml 複数行`で検索すると、だいたい`foo: |`や`foo: >`を使いなさいと書かれるが、使い方は実は微妙でした。
## `foo: |`、`foo: |-`、`foo: |+`

### まずは基本形の`foo: |`

```yaml
foo: |
  bar
  baz
```
上記Yamlはこう解釈されます。

```rb
{ "foo": "bar\nbaz\n" }
```

### `-`記号を追加すると、最後の改行が無くなります。

```yaml
foo: |-
  bar
  baz
```

```rb
{ "foo": "bar\nbaz" }
```

### `+`記号について

**`+`記号は滅多に使われないが、その意味は最後に改行が複数ある場合、そのまま反映します。**

```yaml
foo: |
  bar
  baz


bar: 1
```

```rb
{ "foo": "bar\nbaz\n", "bar": 1 }
```

```yaml
foo: |+
  bar
  baz


bar: 1
```

```rb
{ "foo": "bar\nbaz\n\n\n", "bar": 1 }
```

## `foo: >`、`foo: >-`、`foo: >+`
**`foo: >`シリーズもだいたい一緒で、`foo: |`との違いは、行末の改行がスペースになります。**

```yaml
foo: >
  bar
  baz
```
```rb
{ "foo": "bar baz\n" }
```

## `""`や`''`や何も書かない時
英語圏であれば、上記の２つで十分ですが、日本語で長文を書いて、改行をしたい時は、どうしても改行もスペースもなくなりたいので、次の方法でできます。

ヒントは[Heredoc記法](https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88)でした。

よくShellで、行末に`\`追加する手法があります。それは`ここで改行しません`という意味です。

```sh
cat <<DOC
foo\
bar
DOC

=> foobar
```

Yamlでは`""`で囲むと、似たような使い方ができます。

```yaml
foo: "\
  bar\
  baz"
```
上記Yamlはこう解釈されます。

```rb
{ "foo": "barbaz" }
```

書き方は色々あります。例えば下記の書き方も可能です

```yaml
foo: "bar\
  baz\
"
```

ちなみの行末の`\`がない時、改行がスペースと解釈されます。
もし改行が必要な場合、`\n`を入れましょう。

```yaml
foo: "\
  b\nar\
  baz\
"
```
```rb
{ "foo": "b\narbaz" }
```

余談ですが、Rubyの仕様と同じように`''`で囲むとき、改行がスペースになり、`\n`などエスケープ文字は全部解釈されなくなります。

```yaml
foo: '\
  bar\
  baz\
'
```
```rb
{ "foo": "\\ bar\\ baz\\ " }
```

何も書かない時も、実は複数行の記入も可能でした。

```yaml
foo:
  bar\
  baz
```
```rb
{ "foo": "bar\\　baz" }
```

改行がスペースになる、エスケープ文字は解釈しない、そして最初と最後の改行は無視されます。

# 最後に
[こちらの回答](https://stackoverflow.com/a/21699210)はいろんなニッチな使い方も羅列しており、ぜひ読んでみてください。


# 参考
- [YAML 基本記法まとめ](https://qiita.com/tfrcm/items/5f8e4c5795ce41b214d1)
- [How do I break a string over multiple lines?](https://stackoverflow.com/questions/3790454/how-do-i-break-a-string-over-multiple-lines)
[Nuxt.js] いらない機能をばっさりカット "SUPER slim builds" を試してみる [軽量化]

**※2019/10/29現在、実験的な機能です**:bulb:

#SUPER slim builds?

<img width="761" alt="スクリーンショット 2019-10-29 0.53.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418007/e6674f73-5e75-537d-fd66-349806d6c454.png">
[Release v2.10.0 · nuxt/nuxt.js](https://github.com/nuxt/nuxt.js/releases/tag/v2.10.0)


Nuxt.js の v2.10.0 リリースノートに **🌟Highlights** としてアナウンスされているものの、実験的機能ということもあってか、公式 Doc にも全然情報がないので SUPER 気になって調べてみた。

##要約

>The Nuxt Vue-app has quite a bit of features which until this PR were always included, whether you used them or not. This PR defines a list of features which are used to either include or omit the corresponding code in the Vue-app templates. This makes it possible for a user to further optimize their bundle sizes.

[feat(vue-app): support configurable features by pimlie · Pull Request #6287 · nuxt/nuxt.js](https://github.com/nuxt/nuxt.js/pull/6287#issue-310394815)

*「Nuxt で作られたアプリは機能てんこ盛りすぎるので、**必要な機能だけ選んでバンドルサイズを減らせる**ようにします。」*

というもの。
ちなみに機能の正式な呼称としては **configurable features** になる模様。

#使い方

要：Nuxt.js v2.10.0 以上  

`nuxt.config.js` に features list を追加。

```nuxt.config.js

export default {

  features: {
    store: true,
    layouts: true,
    meta: true,
    middleware: true,
    transitions: true,
    deprecations: true,
    validate: true,
    asyncData: true,
    fetch: true,
    clientOnline: true,
    clientPrefetch: true,
    clientUseUrl: true,
    componentAliases: true,
    componentClientOnly: true
  },
  //...
}

```

後は不要な機能を OFF ( `false` ) にしてビルドするだけで、 *"SUPER slim builds"* ができる。

#検証

`npx create-nuxt-app` で作成したイニシャルな Nuxt プロジェクトの、バンドルアナライザ ( `nuxt build -analyze` )でサイズ比較。

###結果

![FireShot Capture 002 - configurable-features [28 Oct 2019 at 19_52] - .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418007/af906963-db9d-a9f4-f5b7-bfead6dfd554.png)
features 全部 ON （デフォルト） : **578.53KB**

![FireShot Capture 003 - configurable-features [28 Oct 2019 at 19_54] - .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418007/8eb1272b-ffe4-f423-74db-913d74694a09.png)
features 全部 OFF : **486KB**

**100KB 程スリムになっている。**（ commons.app.js から vue-meta が消えているのも目視できる）

しかしながら Gzipped の状態では 10KB 程度の違いだったので、劇的に軽くなる訳ではない。
加えて全部 OFF にしてこれ且つ実験的機能ということもあるので、現段階では無理してプロダクション環境に導入するほどではないというところか。

#将来的には

>A REALLY REALLY nice improvement to support minimal builds for nuxt. Can't wait to also have router disabling support. With nuxt 3 we can have nuxt to be used like:

>>Create app.vue
npx nuxt dev

>TADA! A fully minimal/universal/efficient vue app is ready!


https://github.com/nuxt/nuxt.js/pull/6287#pullrequestreview-282350753

vue-router の無効化や、使われていない機能の自動検知なども考案されている。

#☕
ペライチの LP を作りたい時なんかに良いかもしれません。（そもそも Nuxt を使うかどうかは置いておき）
Nuxt.js がますます便利・スマートになっていきますね。
Dev チーム、コントリビューターの皆さんに感謝。

$Thank$ $you.$
アスペクト維持 x トリミング x 横幅:レスポンシブ x 最大幅:元画像サイズ で画像表示

画像のアスペクト比を維持しながら、指定の比率でトリミングしたい。
表示サイズはレスポンシブ。ただし最大幅は元画像の横幅での表示の例です。

```css:css
    .img-fluid-trim{
        display: inline-block;
    }
    .img-fluid-trim > div{
        height:0;
        width: auto;
        overflow:hidden;
    }
    .img-fluid-trim-1x1 > div{
        padding-bottom:100%; /* w:h = 1:1 */
    }
    .img-fluid-trim-2x1 > div{
        padding-bottom:50% /* w:h = 2:1 */
    }
    .img-fluid-trim-1x2 > div{
        padding-bottom:150%; /* w:h = 1:2 */
    }
    .img-fluid-trim-16x9 > div{
        padding-bottom:56.25%; /* w:h = 16:9 */
    }

    .img-fluid-trim > div > img{
        width:auto;
        height:auto;
        max-width:100%;
        max-height:initial;
      /*transform: translate(0, -10%); トリミング開始高さを変える場合*/
    }
```

```html:html
        <div class="img-fluid-trim img-fluid-trim-16x9" >
            <div><img src="/path/to/img.jpg" ></div>
        </div>
```
「スケーラブルデータサイエンス データエンジニアのための実践Google Cloud Platform」を読んだメモ

<a target="_blank"  href="https://www.amazon.co.jp/gp/product/4798158836/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4798158836&linkCode=as2&tag=shogo807-22&linkId=0ee948b6e7cad3278d203df73564cc6b"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=JP&ASIN=4798158836&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=shogo807-22" ></a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=shogo807-22&l=am2&o=9&a=4798158836" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<a target="_blank" href="https://www.amazon.co.jp/gp/product/4798158836/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4798158836&linkCode=as2&tag=shogo807-22&linkId=2a65561aa813bdff545dcea1d636011f">スケーラブルデータサイエンス データエンジニアのための実践Google Cloud Platform</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=shogo807-22&l=am2&o=9&a=4798158836" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />

# メモ

### 読んだ理由

> スケーラブルなデータ処理や分析、機械学習モデルの構築を行う一連の手順を具体例とともに紹介

本書では、GCPを使ったデータエンジニアリングを総括的に捉え、具体的に学べるということで読んでみた。特に、機械学習のアーキテクトとか環境作りはまだ経験したことがないので、そのあたりまで含めたGCPのサービス利用イメージを掴みたかったので読んでみた。

### 思ったこと
機械学習を行う環境作りの仕組み化まで具体的に説明され、一連の手順が紹介されていたので、全体的な作業のイメージがつかめるようになった事はよかった。ただ、正直後半にいくにつれ、機械学習の詳しいモデリングの話とかは理解するのに骨が折れた。だいぶ飛ばしたので、実際業務で使う場合は改めて読みたい。

あとは、第一章「データに基づく意思決定」での議論がおもしろかった。

筆者は冒頭でデータ分析の目的を明確に示す。

> データ分析の主目的は、よりよい決定を下すこと

分析をすることや、示唆を得るだけが目的ではなく、そこからよりより意思決定をするという事が強調されていた。

さらに、「データエンジニアとは？」 という話になっていく。
筆者は、Googleでのデータエンジニアの定義を以下のように紹介する。

> データエンジニアというのは、「データ分析を実行してビジネスで成果を出す」ことができる人
> Googleでは、データエンジニアは、 **モデルの構築から自動化までをカバーできる** と考えている

全部できる人だ。

データを収集して安全に管理するだけじゃダメだし、ビジネス示唆を与える分析ができるだけでもだめだし、データからモデルを作れるだけでもだめ。全部できなきゃいけない。

これは厳しい。読みながらそう思った。

でも、クラウドの進化により難易度は下がってきていると。実はそんなに、無理な話ではないと。
データ収集、管理、モデル構築、仕組み化はますますシンプルになっていくと。

将来的には、データアナリストと、データエンジニアと、データサイエンティストの垣根はなくなっていく。
「よりよい決定を下すこと」への道のりは、データエンジニアという職種が全てまかなうようになる。らしい。
JSONSchemaのススメ

システム開発をしている時、マスタデータを投入するシーンがあるかと思います。
そんな時に、マスタメンテナンスやImport機能があれば理想的ですし、RDBを使っているならばCSVで作れば大体何とかなります。
が、そのどちらでもないケース、ドキュメント型データベースにデータを投入しようということになると、かなり面倒です。
階層構造を持つデータを入力しなければいけないため、多少コードにより加工をするにしても元データ自体が階層構造を持ったデータ、つまりJSON(やYAML、TOML)になることのが普通です。
(いや、ならない。CSVでも綺麗に出来る、というのならそもそもDBの選定や設計を間違ったことを疑うべきです)


# JSONでデータをデータを作るのはしんどい
しかし、JSONでデータを作るのは中々人に取って骨が折れます。
CSVの場合は項目名は先頭行やプログラム内に定義してしまえばそれで終了ですが、
JSONやの場合各項目のプロパティ名について間違えないように書かなければなりません。
配列の中にObjectが入り込んでいる場合など、うんざりします。
単純に記述量が多くなって大変ですし、えてしてドキュメントデータベースの類はスキーマレスなことが多いのでデータ投入までのフローに静的チェックをかけてくれる部分がありません。これは怖い。

# せめて手元でValidationや補完が出来るようにしよう
そんな時に訳に立つのがJSONSchemaです。
これはJSONにおけるSchema定義を作るもので、VSCodeなどのモダンなエディタなら、
この定義に基づいて補完やバリデーションをかけてくれます。

参考：https://qiita.com/t-mimura/items/e0f12ea5678b1a10bba3

エディタで補完とバリデーションがかかるだけで作業効率・正確性共に向上します。

# いやでもSchemaの書き方よく分からないし作るの面倒くさいんだけれども
では、Schemaをどこから調達すればいいのか。
世に広まっているシステムの設定ファイルだったら探せばどこかしらに転がっていますが、
今回の想定は自前のシステムのためのJSONです。

きちんとJSONSchemaの書き方を学んで手書きするのも一つの手ですが、
今の世の中色々と~~手抜き~~効率化する方法があります。

## [JSON Schema Tool](https://jsonschema.net)
サンプルとなるJSONを張り付けてInferボタンを押すと、JSONSchemaを自動作成してくれるツール。
Enum等まできちんと作ろうとするとさすがに手直しが必要ですが、
項目名の補完やチェック程度なら大体これで済んでしまいます。
結果をコピーすると何故か行番号までついてきたりと若干突っ込みどころがありますが、
それを差し引いても非常に便利なサービスです。

## [Typescript JSON schema generator](https://github.com/vega/ts-json-schema-generator)
こちらはVsCodeの拡張ですが、Typescriptの型定義からJSONSchemaを作成できます。
TypeScriptの型に沿った形で作成されるので、かなり高精度に書けます。

# まとめ
* JSON Schemaがあると非常に安心感がある
* いちいち手書きしなくても自動作成に頼ることも出来る
Javaしか知らないプログラマがPythonを使ってつまづいたこと

Javaしか使ったことがない私が、Pythonを使ってコーディングすることに。

つい、Javaの癖がでてつまづいてしまうことを備忘録変わりにメモする

#関数を記載する順番が大切
Javaの場合、使用する場所の後ろに関数を定義してもエラーにはなりません。
しかし、Pythonだとエラーになります。
関数が見つからないと言われます。
「使うものは先に書く‼️」

#型がわからない
Javaは当たり前ですが、変数を使用するときに型を宣言します。
なので、使えるメソッド等は直ぐにわかります。
しかし、Pythonは型宣言がないので、どんなメソッドが使えるのかわからん‼️
そのわりに数値型とか文字列型とかは勿論存在する。。。
迷子にならないコツを勉強しなきゃ。。。

#感想
まだまだ使いこなせなくて、明らかに生産性落ちてますが(笑)色々と調べて使いこなせるようにしたいと思います。

メモは気づいた都度更新します。
if文を使わない条件分岐

例として、
「ある数nが偶数の場合はそのまま、奇数の場合は-にしなさい」
といわれたとき

#ifを使う場合
```python
#[_ for _ in range(1,11)]
n=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def case_if(n):
    if n%2==0:
        return n
    elif n%2!=0:
        return n*(-1)
ans=list(map(case_if,n))
```
```python
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
```

#ifを使わない場合
```python
#[_ for _ in range(1,11)]
n=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def case_notif(n):
    return n*(-1,1)[n%2==0]
ans=list(map(case_notif,n))
```
```python
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
```

上記のように、
`(False,True)[条件式]`
の形式でifを使わず条件分岐ができる。

他にどのような使い方ができるか検索してみたが、なんと検索したらいいか分からず答えに辿り着かなかったため、いくつか実験してみた。

##条件式に論理演算子を使えるか
####andの場合
```python
n=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def case_notif_and(n):
    return n*(-1,2)[n%2==0 and n<5]
ans=list(map(case_notif_and,n))
```
```python
[-1, 4, -3, 8, -5, -6, -7, -8, -9, -10]
```
####orの場合
```python
n=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def case_notif(n):
    return n*(-1,2)[n%2==0 or n<5]
ans=list(map(case_notif,n))
```
```python
[2, 4, 6, 8, -5, 12, -7, 16, -9, 20]
```

結果:使える

##演算子での計算以外に使えるか
####print()に直接`[条件式]`をつける
```python
n=5
print(n*2,n)[n%2==0]
```
```python
10 5
TypeError: 'NoneType' object is not subscriptable
```
####print()の引数に`(False,True)[条件式]`の形式で

```python
n=5
print((n*2,n)[n%2==0])
```
```python
10
```

####文字列replaceをsに格納
```python
s='ABCDE'
s=(s.replace('C',' '),s.replace('C','  '))[len(s)==5]
print(s)
```
```python
ABFiveDE
```

結果:関数に直接`[条件式]`をつける以外で正常に使える

##複数処理を行う
```python
s='12345'
s=(
    (
        s.replace('3',' ')
        int(s)
    ),
    (
        s.replace('3','789')
        int(s)
    )
    )[len(s)==5]
print(s)
```
```python
Syntax Error: invalid syntax
```
####コンマをつけてみる
```python
s='12345'
s=(
    (
        s.replace('3',' '),
        int(s)
    ),
    (
        s.replace('3','789'),
        int(s)
    )
    )[len(s)==5]
print(s)
```
```python
('1278945', 12345)
```

####s=をつけてみる
```python
s='12345'
s=(
    (
        s=s.replace('3',' '),
        int(s)
    ),
    (
        s=s.replace('3','789'),
        int(s)
    )
    )[len(s)==5]
print(s)
```
```python
Syntax Error: invalid syntax
```

#まとめ
`(False,True)[条件式]`をひとかたまりとして、条件分岐を表現できることが分かった。
複数処理をうまく実現できないが、ひとつの処理について条件分岐が必要な場合は、この書き方のほうがシンプル。
個人的に(False,True)の順番は少しキモチワルイ

---

#追記(20191030)
コメント欄のおふたりのおかげで、`(False,True)[条件式]`はタプルの添え字表記であることが理解できました。ありがとうございました。

>他にどのような使い方ができるか検索してみたが、なんと検索したらいいか分からず答えに辿り着かなかったため、いくつか実験してみた。

`(False,True)[条件式]`に気づいたときにはどういう検索をしたら情報が得られるか分からなかったのでいろんなパターンを試してみましたが、当時の私が使うべき検索ワードは「True False 振る舞い」だとか「添え字 True False」だとかになるのでしょうね。

追加で調べた[こちら](https://qiita.com/amedama/items/a03d4ae97af9aecd5966)の記事とその[元ネタ記事(アーカイブ)](https://archive.is/jKNp1)の「bool型はint型のサブタイプ」もとても参考になったので備忘として追記しておきます。

`(False,True)[条件式]`の書き方は個人的にとても気に入ったので、人に見せないコードの範囲でじゃんじゃん使っていきたいのですが、やはりifも条件式も明示してあげる方が読みやすいコードになるのかなと感じました。
macOS Catalina で phpenv のビルドエラーに対処する

先日、macOS CatalinaがリリースされたのでCatalinaにアップデートしました。

普段、phpを利用し開発を行っている為、phpenv(anyenv経由)でphpのバージョン管理を行っています。

## ビルドエラー

phpenvでphp7.3.9をインストールしようとしたところ、以下のzlibに関するエラーが出てしまいました。

```
$phpenv install -v 7.3.9

checking for ZLIB support... yes
checking if the location of ZLIB install directory is defined... /usr
configure: error: Cannot find zlib

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

-----------------------------------------
checking for OpenSSL version... >= 1.0.1
checking for CRYPTO_free in -lcrypto... yes
checking for SSL_CTX_set_ssl_version in -lssl... yes
checking for PCRE library to use... bundled
checking whether to enable PCRE JIT functionality... yes
checking whether to enable the SQLite3 extension... yes
checking bundled sqlite3 library... yes
checking for ZLIB support... yes
checking if the location of ZLIB install directory is defined... /usr
configure: error: Cannot find zlib
-----------------------------------------

The full Log is available at '/tmp/php-build.7.3.9.20191029190313.log'.
[Warn]: Aborting build.
```

Catalina以前は [[MacOS Mojave]pyenvでpythonのインストールがzlibエラーで失敗した時の対応](https://qiita.com/zreactor/items/c3fd04417e0d61af0afe) で対応出来たのですが、CatalinaではmacOS SDK headerが存在しない為、この方法は利用できません。

## 対処法

phpenvにはコンパイルオプションを指定するファイル `default_configure_options` が用意されています。

これを編集しzlib含む各ライブラリのディレクトリを指定してあげればコンパイルが通ります。

以下、私の場合の例

```
--without-pear
--with-gd
--enable-sockets
# jpeg-dirの変更
--with-jpeg-dir=/usr/local/opt/libjpeg
# png-dirの変更
--with-png-dir=/usr/local/opt/libpng
--enable-exif
--enable-zip
--with-zlib
# zlib-dirの変更
--with-zlib-dir=/usr/local/opt/zlib
# bz2-dirの指定
--with-bz2=/usr/local/opt/bzip2
--enable-intl
--with-kerberos
--with-openssl
--enable-soap
--enable-xmlreader
--with-xsl
--enable-ftp
--enable-cgi
# curl-dirの指定
--with-curl=/usr/local/opt/curl
# tidy-dirの指定
--with-tidy=/usr/local/opt/tidy-html5
--with-xmlrpc
--enable-sysvsem
--enable-sysvshm
--enable-shmop
--with-mysqli=mysqlnd
--with-pdo-mysql=mysqlnd
--with-pdo-sqlite
--enable-pcntl
# readline-dirの指定
--with-readline=/usr/local/opt/readline
--enable-mbstring
--disable-debug
--enable-fpm
--enable-bcmath
--enable-phpdbg
# 以下2行追加
--with-iconv=/usr/local/opt/libiconv
--with-libedit=/usr/local/opt/libedit
```

※ `#` の行は変更箇所が分かりやすいよう追記したので、ファイル編集時はコメント不要です

ただし、 `libedit` については↑で指定しても上書きされてしまう為、環境変数 `PHP_BUILD_CONFIGURE_OPTS` の指定が必要になります。

```
PHP_BUILD_CONFIGURE_OPTS="--with-libedit=$(brew --prefix libedit)" phpenv install -v 7.3.9
```

## PHP 7.3 向けの対応

PHP7.3は `--without-libzip` オプションが無いとmac内のlibzipライブラリを探す為、付属のlibzipを利用するようオプションを付与する必要があります。

参考: [PHP7.3.8をインストールしようとしたらハマった話](https://qiita.com/j-takasaki/items/fbbf4d733a0475322b71)

## まとめ

`default_configure_options` を各環境に合わせて修正しよう
【Java】配列から最大値・最小値を取得するプログラム例

<pre class="java">import java.util.*;

class Main{
public static void main(String[] args) {

    int[] data = {1,2,3,4,5,6,7,8,9,10};
    int Max = data[0];
    int Min = data[0];
    
    for(int i = 1 ; i &lt; a.length; i++){
        if(Max &lt; data[i]){
            Max = data[i];
            }else if(Min &gt; data[i]{
            Min = data[i];
             }
        }
        System.out.println(Max);
    }
}</pre>
Google Cloud Storageで画像操作したメモ

画像uploadするにはどうすればいいんだろう
いろいろな記事を参考にしてみた
うまくいかない？
ちょっと！よくよく見るとこの記事、[リファレンス](https://googleapis.dev/nodejs/storage/latest/Bucket.html#upload)に書かれてるものとオプション名違ってるじゃん！そりゃ期待通り動かないよ！
ってことがあったので、メモっておきます。

# 前提
- google cloud storage+nodejs
- キーを取得済み (<https://cloud.google.com/iam/docs/creating-managing-service-account-keys?hl=ja>)

# upload
```js
const {Storage} = require('@google-cloud/storage');
const storage = new Storage({
    projectId: 'hogehoge-project',
    keyFilename: './serviceAccountKey.json', // 前提で取得済みのキーファイル
});

const BUCKET_NAME = 'hogehoge-project.appspot.com';

storage
.bucket(BUCKET_NAME)
.upload('./image.png' /* uploadしたいファイル */, { destination: 'uploaded_image.png' /* storageにこのファイル名でuploadされる */ })
.then(() => { /* success */ })
.catch((err) => { /* error */ });
```

# delete
```js
storage
.bucket(BUCKET_NAME)
.file('uploaded_image.png')
.delete()
.then(() => { /* success */ })
.catch((err) => { /* error */ });
```

# リファレンス
バケットの操作周りをみれば良い
https://googleapis.dev/nodejs/storage/latest/Bucket.html
本番環境を人間から守るために

ある日のできごと。

```
1 モジュールの改修終わったから、開発用の環境でテストすっぞ！
2 よっしゃリリース完了！
3 あれ、開発環境でテストしても改修版反映されていない？
4 うげー、本番環境にリリースしてんじゃん orz...
5 ユーザーからめっちゃ問合せ来てるんだけど・・・(夜逃げじゃ)
```

システムの運用保守を経験したことがある人なら、コトの大小問わず1度は経験したことがあるのではないでしょうか。
クラウド環境全盛の現代でも、ほとんどの場合において、開発やテストを行う開発環境と、実際にサービスを提供する本番環境は分けているのが一般的かと思います。ややクラシカルな表現かもしれませんが、以下のようなイメージですね。

- ローカル環境：コーディング、単体テスト
- 開発環境/テスト環境：結合テスト
- ステージング環境：総合テスト、リリース準備
- 本番環境：実際にサービスを提供する環境

意図しないモジュールのリリースや、データベースの更新は「事故」です。事故を起こすのは、多分100%人間なのですが、なるべくなら事故は起こしたく無いのも人間です。自分も含めたそんな人のために、本番環境を自分たちから守る事故防止策をサクッとまとめてみました。

# リリース作業

## リリース手順書を用意する
人は間違うという前提でリリース手順書を用意しましょう。人の記憶など信用してはいけません。ポイントとして「誰にでもできる」レベルの手順書は目指さないことです。逆に細かすぎて手順を見落とす可能性大です。ある程度その仕事の常識を持っている前提で、簡潔に書いた方が良いでしょう。
防げる事故：事故全般

## 本番環境にリリースする際は2人以上で行う
たとえリリース手順書があっても間違うのが人間です。リリース手順書自体も間違っている可能性があります。
「リリースする人」と「リリース作業が正しいか確認する人」のペアで作業するようにしましょう。指さし確認も忘れずに。
防げる事故：リリース手順書の見落とし、作業の誤解

## 査証(エビデンス)を残す
画面キャプチャーやログなど、作業を正しく行った査証を残すようにしましょう。後で問題が発生したときに身を守ることができますし、査証を取る段階で過ちに気付ける場合があります。作業時刻も記録すると良いです。
防げる事故：リリース作業の誤り、えん罪防止

## 想定外の事態が発生したら、とにかく作業をストップする
現場判断で作業を進めると、取り返しのつかないことになりかねません。分からない状況になったらいったん作業をストップし、いわゆる「有識者」と共に理由を明らかにすることを優先しましょう。ここで有識者とは「何をもって有識者とするのか」という新たな課題が発生するのですが、いったん置いておきます。
防げる事故：環境の破壊など

## 事前準備を十分行う
ほとんどの場合、本番環境へのリリース作業を行える時間は限られていると思います。
時間が限られる->緊張する->ミスを起こす、というのは事故の王道です。
リハーサルや作業の準備をなるべく事前に行い、余裕をもってリリースできる状況を作りましょう。
防げる事故：緊張からのミス

## リリース作業の自動化
Jenkinsなど、いろんなツールがあると思います。時間が限られた状況での人間の作業はなるべく減らす用にします。
防げる事故：事故全般


# データベース

## 開発環境と本番環境でデータベース名(スキーマ名)を変える
ぼーっとして、開発環境で作業するつもりが本番環境を触っていた、という事故を防ぎます。
防げる事故：うっかりミス

## 開発環境と本番環境でログインできるユーザー名を変える
こちらも同様です。防止策は複数あった方が安心です。
防げる事故：うっかりミス


# システムへのアクセス
ブラウザから利用するWebシステムネタが中心です。

## 開発環境と本番環境でIPアドレスの体系を変える
こちらもぼーっとして、開発環境で作業するつもりが本番環境を触っていた、という事故を防ぎます。
防げる事故：うっかりミス

## 開発環境と本番環境でURLのパスを変える
本番環境が
 http://1.1.1.1/service
だったら開発環境は
 http://100.100.100.100/dev_service
にする、というような対策です。IPアドレスだけ違うと、開発環境のIPアドレスを入力したつもりが本番環境にアクセスしていたという幻のようなことが起きます。
防げる事故：うっかりミス

## ブラウザのオートコンプリート機能を無効にする
オートコンプリートで補完されたアドレスを無意識に使ってしまうミスを防止します。
普段使う端末では無いと不便なので、開発環境や本番環境のブラウザなど限定になると思います。


# セキュリティ

## 開発環境から本番環境への疎通を防ぐ
データベースの接続先を書いたリソースファイルを取り違えたり、更新を忘れたりするミスは「あるある」です。OSのファイアウォールで開発環境から本番環境へ疎通できないように、特定のIPやポート番号の信号をブロックします。間違えてメンテナンス用のリモート接続もブロックしてしまわないよう注意しましょう。
防げる事故：設定ファイル、リソースファイルの取り違え

## 開発環境と本番環境のネットワークのセグメントを変える
上記と同様です。ファイアウォール機器で通信制御をかけるのもありです。

## 開発環境と本番環境でOSにログインできるユーザーIDを変える
リモート接続やターミナルで、ぼーっとして接続先を間違うことはよくあると思います。IDを変えておくと、間違いに気付ける可能性が高まります。

# その他

## 壁紙やログインメッセージを変える
Windowsの場合はでかでかと「本番環境！」と書いた壁紙を用意しましょう。SSHなどのターミナルの場合はログインメッセージを変更しておきましょう。

## 先輩SEに聞く
そこそこ歴史のある会社だと「ミスを防ぐ７箇条」の類がきっとあるはずです。なんか社訓とか覚えるテイストがして嫌かもしれませんが、素直に受け入れると良いことがあると思います。あと、先輩SEに失敗談を聞くのも良いですね。飲み会の席だと長くなるのは温かい気持ちで受け入れましょう。

# 終わりに

いったん以上です。思いついたら追記しようと思います。そういえば、大昔のホストコンピューターの時代は、Enterキーと実行キーは別だったような気がします。実行キーはちょっと離れたところにあり赤くて押すのに緊張するようにできていました。1つの要素に複数の役割を持たせるのも事故の原因ですね。

いづれ事故は避けたいものです。人間がいる限りは。
Amazon CloudWatchでAWS API Gatewayのアラーム監視作成を行う

##ステップ1 メトリクスと条件の指定
CloudWatchから「アラーム」を選択し
「アラームの作成」→『メトリクスの選択』をクリックします。

![スクリーンショット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392337/52d8056a-8de6-8adc-7598-3ba88f1f2209.png)
『APIGateway』を選び、
『API名別』をクリックした後、
監視したいAPI名のメトリクス名を探してチェックボックスにチェックを入れます。
その後『メトリクスの選択』を選択します。

この画面で『期間』と条件のしきい値を定義します。
※「AWS/」名前空間のメトリクスでは、60秒を超える期間のみがサポートされます。

![スクリーンショット2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392337/5074ead3-06bc-3521-85bf-00de96be72e9.png)

『次へ』をクリックします。

##ステップ2 アクションの設定
この画面で通知を設定します
新しいSNSトピックを選択し、
『トピック名の入力』の名前を入力します。

![スクリーンショット3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392337/6c1d97a0-ecd3-2d7a-2f66-5532f30acf65.png)

通知を受け取るEメールエンドポイントに
メールアドレスを入力して『トピックの作成』をクリックします。

※すでにSNSトピックを作成済みなら、
『既存のSNSトピックを選択』にチェックを入れ、
通知の送信先：に作成した通知先を選択して『通知の追加』をクリックします。

『次へ』をクリックします

##ステップ3 説明の追加
『アラーム名』を入力します。
『アラームの説明』は任意なので入力しなくても構いません。

![スクリーンショット4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392337/5f8edcda-dee4-f2da-6791-b9dab6a58310.png)

『次へ』をクリックします。

##ステップ4 プレビューと作成
色々と設定した内容をプレビュー出来ます。

![スクリーンショット5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392337/0b33e5dd-50d0-e852-d712-2cc47cef3337.png)

一通り確認して問題なければ、『アラームの作成』をクリックして完了です。

##ちなみに
とりあえずメールが飛ぶかどうかを確認するなら、期間を1分でしきい値を1で設定してみると良いかもしれません。
タイミングや環境にもよると思いますが、
実際にメールが飛んでくるのには大体、5~10分ほど掛かりました。

##参考
静的しきい値に基づいて CloudWatch アラームを作成する
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/ConsoleAlarms.html
RSpec 1つのexpectで複数のchangeを検証する

表題通り1つのexpectに対して複数のchangeを検証したい


```ruby
it '複数のchangeを見るぞ' do
  expect { subject }.to change { x }.by(1).
                    and change { y }.by(2)
end
```

長くなるし見づらいのであまり使いたくない…

```ruby
it '複数のchangeの結果だけ見るパターン' do
  # x, y が 1, 2になることだけ確認するbyは使え無さそう
  expect { subject }.to change { [x, y] }.to [1 ,2]
end
```


#### 参考

[ruby on rails - RSpec: Expect to change multiple - Stack Overflow](https://stackoverflow.com/questions/29388777/rspec-expect-to-change-multiple)

RSpec3.1からの機能らしい
http://rspec.info/blog/2014/09/rspec-3-1-has-been-released/

※自ブログからの転載です
http://akinov.hatenablog.com/entry/2019/10/29/190642
PHP UUID version4 を生成する

## 環境

PHP 7.3.1

## コード

uuid_v4_factory.php

```php
<?php declare(strict_types=1);

/**
 * UUID version 4
 */
class UuidV4Factory
{
    const PATTERN = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';

    /**
     * UUID 生成
     * @return string
     * @throws \Exception
     */
    public static function generate(): string
    {
        $chars = str_split(self::PATTERN);

        foreach ($chars as $i => $char) {
            if ($char === 'x') {
                $chars[$i] = dechex(random_int(0, 15));
            } elseif ($char === 'y') {
                $chars[$i] = dechex(random_int(8, 11));
            }
        }

        return implode('', $chars);
    }
}
```

## 使い方

```shell-session
$ php -a
php > require('uuid_v4_factory.php');
php > echo UuidV4Factory::generate();
3c68e561-2281-40a1-bcf6-91fe971b00e8
php > echo UuidV4Factory::generate();
de527f63-8f60-4034-aa21-bc091db13d6d
php > echo UuidV4Factory::generate();
4d052519-cc4f-4a51-9910-ed3c2842e457
php > echo UuidV4Factory::generate();
b796ef1a-1bb9-4d37-97d4-45f2f6da742f
```

## 500万回のUUID衝突テスト

```php
<?php declare(strict_types=1);

ini_set('memory_limit', '-1');

require('./uuid_v4_factory.php');

$baseMemoryUsage = memory_get_usage();
$baseTime = microtime(true);

$data = [];
for ($i = 0; $i < 5000000; $i++) {
    $uuid = UuidV4Factory::generate();
    if (isset($data[$uuid])) {
        echo 'UuidDuplicateError' . PHP_EOL;
    }
    $data[$uuid] = true;
}

$maxMemoryUsage = (memory_get_peak_usage() - $baseMemoryUsage) / (1024 * 1024);
$processTime = microtime(true) - $baseTime;

echo sprintf('Max Memory Usage: %.3f [MB]', $maxMemoryUsage) . PHP_EOL;
echo sprintf('Process Time: %.2f [s]', $processTime) . PHP_EOL;
```

```
$ php uuid_v4_factory_test.php
Max Memory Usage: 736.000 [MB]
Process Time: 187.68 [s]
```

500万回を3回実行してみましたが、衝突はしませんでした。

## 参考

- https://ja.wikipedia.org/wiki/UUID#.E3.83.90.E3.83.BC.E3.82.B8.E3.83.A7.E3.83.B34
- https://www.ietf.org/rfc/rfc4122.txt
- https://qiita.com/psn/items/d7ac5bdb5b5633bae165
- https://www.php.net/manual/ja/function.random-int.php