OmniAICreator/Qiita-1.07M huggingface dataset:
【PSR-4 Autoload】第三回 composerからautoloadを利用

## この記事でできるようになること :muscle: 
【第一回】 autoloadの概要を理解
【第二回】 仕組みを理解したうえでautoloadを実装
**【第三回】 composerからautoloadを利用**
## そもそも... :thinking: 
### Composerとは :mag_right:
#### 概説
PHPのプロジェクトで使用するライブラリ・パッケージの管理（インストール・アップデート等）を単一のファイル（`composer.json`）から一括で行えるツールです。
ComposerにはAutoload機能も用意されており、自作Autoloadクラスを作らなくてもファイルの自動読み込みを実現することが可能になります。便利ですね。
>Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

>1. Enables you to declare the libraries you depend on.
>2. Finds out which versions of which packages can and need to be installed, and installs them (meaning it downloads them into your project).
>3. You can update all your dependencies in one command.

https://getcomposer.org/doc/00-intro.md 

#### 大まかな使い方
1. composer自体をインストール
1. プロジェクトの最上位の階層に`composer.json`ファイルを用意
1. `composer.json`ファイルに使用するライブラリやそのバージョンについて書き込む（今回はAutoloadの設定を書き込む）
1. ターミナルで`composer install`コマンドを実行

## ComposerからAutoloadを利用 :twisted_rightwards_arrows:
### composer.json
composerがマシンにインストールされている前提で話を進めます。
下記の場所に`composer.json`を用意します。
```:ディレクトリ構成
./Qiita
    ├ App
    ├ Autoload.php
    ├ index.php
    └ composer.json ← NEW!
```
```composer.json
{
    "autoload": {
        "psr-4": {
            "App\\": "App/"
        }
    }
}
```
`"App\\"`が**名前空間プレフィックス**で、
`"App/"`が**ベースディレクトリ**に当たります。

`"App/"`以前のパス（私の環境では`C:/Users/yanmy/OneDrive/Desktop/Qiita`）はcomposerが勝手に取得してくれます。（autoload_psr4.php参照）
``` vendor > composer > autoload_psr4.php
<?php

// autoload_psr4.php @generated by Composer

//この2行でベースディレクトリを取得
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'App\\' => array($baseDir . '/App'),
);
```
用意ができたらターミナル上で`Qiita`ディレクトリにいることを確認し、以下のコマンドを実行してください。
```:Terminal
~\Qiita>composer install
```
`vendor`ディレクトリと`composer.lock`ファイルが生成されれば成功です。

`index.php`を書き換えます。
読み込むAutoloadファイルを自作のものから`vendor/autoload.php`に書き換えます。
```index.php
<?php
require_once "vendor/autoload.php";

use App\Greeting\English as En;
use App\Greeting\Japanese as Ja;

$en = new En\Hello();
$ja = new Ja\Hello();
```
ターミナル上で`index.php`を実行し出力が変わらないことが確認できれば成功です！
```:Terminal
~Qiita> php index.php
Hello World!
こんにちは世界。
```
お疲れ様でした！！:tada:

## まとめ :ribbon:
今回はComposerからAuotoloadを利用する方法について学びました。
作業内容は以下の通りです。

1. composerをインストール（割愛）
1. `composer.json`を用意する
1. 利用したい名前空間プレフィックスとベースディレクトリの組み合わせを書き込む
1. ターミナル上で`composer install`を実行
1. Autoload機能を使用したいファイルで`vendor/autoload.php`を読み込む
1. 動作が変わらないことを確認する


以上でAutoloadについての解説は終了です。
**最後まで読んでいただきありがとうございました！！**
【NeosVR】おなじ数字を色々な場所に書き込みたい。コンポーネントだけで。【LogiX】

## Relation/ValueMultiDriver\<T>で解決
好きな値をどこか別の場所にコピーしたい時は、Transform/Drivers/ValueCopy\<T>を使いますが、いろんなところにコピーして使いたい時はValueMultiDriverというコンポーネントがおすすめです。

・「コンポーネントをアタッチ」を押し、Relationsを開いたところにあります。

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/4a5c3d98-50a1-f494-f1cd-9e2a30700dd3.png) |
| --- |
| Value(Reference)MultiDriverは、Transform/Drivers内ではなく、Relations内にある。 |

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/856910c7-5508-317d-a634-c5a034eb5523.png) |
| --- |
| クリックすると、ズラっといっぱい出てくる。例えば、文字だったら「String」、ブレンドシェイプであれば「float」、座標やスケールであれば「float3」を選ぼう。 |
### つかいかた

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/bc3955e2-9fd3-e04e-7dff-cc6d8b9076bc.png) |
| --- |
| コンポーネントを置いた直後の様子 |

コンポーネントはこんなかんじです。『Add』ボタンを押すと「コピーして上書きしたいとこリスト」が増えます


| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/67912198-af2e-c18b-27a0-e344870d63bd.png) | 
| --- |

| 要素名 | 説明 | 型 |
| --- | --- | --- |
| persistent | <details><summary>基本的にTrue(☑)のままでいい。詳細は折り畳み。</summary>インベントリ等に保存、あるいはワールドをしたときにこのコンポーネントを残すかどうか。True(☑)だとそのまま残り、False(☐)にすると保存時に消えてくれる。</details> | bool |
| UpdateOrder | <details><summary>基本的に0のままでいい。詳細は折り畳み。</summary>NeosVRが1フレームごとに行う処理の中の順番を指定できる。数字が小さければ先に、数字が大きければ後になる。 | int |
| Enabled | コンポーネントが動作するかを設定できる。False(☐)にすると動作しなくなる……はずだが、なぜかこのコンポーネントに関しては、どうあがいても動く。 | bool |
| Value | <details><summary>コピーして描き込みたい数値を入れる。</summary>ValueCopyコンポーネントは「『ここの中身』を『ここの中身』にコピーしてね」みたいに使うが、ValueMultiDriverは「『この数字』を『ここの中身』にコピーしてね」みたいに使う。数字を直接描き込まないといけない。 | ユーザーが指定した型<br>この記事ではfloat |
| Drives(list) | コピー先（後述）を入れる。 | IValueField |

- コピー先の入れ方
    - コピー先の数字を入力するところの左にある文字にレーザーを当ててグラブして「掴む」（↓例の場合Valueをつかんでいる）
    - 掴んだままDrives(list)のnullとなっている場所にレーザーを当てて離す

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/f1c83d13-437e-3040-3416-72e34e26053c.png)|
| --- |
| コピー先の数字を入力するところの左にある文字にレーザーを当ててグラブして「掴む」 |

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/fc0be615-f3f8-0adc-25da-ad7b3b9032f9.png) |
| --- |
| 掴んだままDrives(list)のnullとなっている場所にレーザーを当てて離す。 |

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/4d678d9e-d04f-d83b-1d5e-a28a7cbf2975.png) |
| --- |
| コピーされるとピンクになる。この状態のことを「Drive（ドライブ）されている」と言う |

# 実践的な使いかた


コンポーネント単体を説明されても「これは何に使えるの」ってなりますが、いくつか楽しい使いかたができます。

- コントローラーの入力をLogiXでドライブさせ、ValueMultiDriverでシェイプキーに書き込む。
    - トリガーを押し込むと、目を閉じて、口角を上げる、等ができる。
- 設定したマテリアルの色（明るさ）を複数のマテリアルに書き込む。
    - コンテクストメニューのボタンを押すと、服に紋様(Emission)が浮かぶようになる。

これらは一例です。ぜひ皆さんで楽しく使ってください。

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884654/b76be143-cd1c-ef71-b8a8-efc5a2fb8976.png) |
| --- |
| トリガーの押し込み具合をValueMultiDriver\<float>に書き込むLogiX。NeosVRで表情を自力で設定する場合はこのようにLogiXを組むことが多い。 |
NovelAIが動く環境をDockerで構築

## はじめに

「GPU に対応する CUDA やら cuDNN やら PyTorch やらのバージョンを調べて、手動でインストールしたけど、なぜか上手く動かない...」  
というトラブルがあると禿散らかりそうになるので、環境構築の類は全て Docker で済ませた方が楽だと思っています(個人の感想です)  

というわけで、  
[リークされたNovelAIのデータでローカル実行してみた](https://qiita.com/A_Akira0803/items/3aef413312548d3c33ac)  
の環境構築までを Docker で行いました  
(そういうGitHubのリポジトリがあるので、ほとんどそれの紹介みたいなもん)  
上の記事にも書かれてるけど、リークされたモデルをダウンロードして動かすのはかなりグレーな行為なので、やっちゃダメだよ  
本記事はあくまで環境構築の一例を紹介するだけだよ  

### 環境の一例

```txt
OS  : Ubuntu 22.04
GPU : NVIDIA GeForce RTX 2060
RAM : 24GB
```

※Windows の人も WSL2 を使えばほぼ同じ手順で動くはず

## 1.A【Ubuntuユーザ向け】Docker と NVIDIA Container Toolkit をインストール

<https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html>  
の通りにすれば、Docker と NVIDIA Container Toolkit をインストールできる  

## 1.B【Windowsユーザ向け】Docker と NVIDIA Container Toolkit をインストール

<https://learn.microsoft.com/ja-jp/windows/ai/directml/gpu-cuda-in-wsl>  
<https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started-with-cuda-on-wsl>  
の通りにすれば大丈夫なはず、しらんけど  

## 2. [Stable Diffusion WebUI Docker](https://github.com/AbdBarho/stable-diffusion-webui-docker) を動かす

Docker で全部やってくれるリポジトリが既にある  
感謝しながら使わせて頂く  

```sh
git clone https://github.com/AbdBarho/stable-diffusion-webui-docker.git
cd stable-diffusion-webui-docker
docker compose --profile download up --build
docker compose --profile auto up --build
```

上記の手順で起動して、デフォルトのモデルが正常に動作することを確認する  
※元の記事ではbatを編集してxformersが云々...と書かれてたけど、Ubuntuだとどうなるのかよく分からんのでやってない。Ubuntuでも動くの？  

## 3. モデルを追加する

[リークされたNovelAIのデータでローカル実行してみた](https://qiita.com/A_Akira0803/items/3aef413312548d3c33ac)  
の手順でできる  
ソースを修正しなくても、Hな画像はちゃんと出力される  
Web上のプルダウンから選択することで、モデルを切り替えられる  
※モデルロード時に落ちる人はRAMが足りてないかも、ckptだけでもかなりデカいからね

## おわりに

環境構築の一例を紹介しました  
環境構築をしてみたい！と思った人は、環境構築をしてみてください  

## 関連リポジトリ

- <https://github.com/CompVis/stable-diffusion>
- <https://github.com/AUTOMATIC1111/stable-diffusion-webui>
- <https://github.com/AbdBarho/stable-diffusion-webui-docker>
- <https://github.com/AbdBarho/stable-diffusion-webui-docker/wiki/Setup>

## 参考になりそうなサイト

- [としあきdiffusion Wiki* - 高速化できるXformersを導入しよう](https://wikiwiki.jp/sd_toshiaki/%E9%AB%98%E9%80%9F%E5%8C%96%E3%81%A7%E3%81%8D%E3%82%8BXformers%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%97%E3%82%88%E3%81%86)
【Power Automate】Teamsのステータスメッセージを自動で設定する

Exchange Online（Outlook）に登録されている予定をPowerAutomateでTeamsのステータスメッセージに反映する話です。

# きっかけ
会社でExchange Online（Outlook）とTeamsを使っています。
在宅勤務の予定はOutlookに登録することになっているのですが、個人的にTeamsのステータスメッセージにも毎朝登録していました。
（Outlookの予定を見てくれない人でもTeamsなら気づいてくれるパターンがあるので）

これを自動で出来たら便利かも？という思い付きからです。

「Microsoft 365で自動化といえばAutomate」ということで、Power Automateで実装したので、ポイントを書いていきます。
余談ですが、Automateは本当に色々できますよね。個人的にはかなり楽しんで使っています。

# ステータスメッセージとは
Teamsの右上にあるこんなやつです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/53c6face-589e-7080-126e-ecdf2e322ad1.png)

設定時に「他のユーザーが自分にメッセージを送るときに表示する」にチェックを入れるとアピール力が強まります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/27649fa6-594c-fef4-c493-6aa25358f0cf.png)

# ステータスメッセージの設定
実はステータスメッセージの取得・設定自体はAPIで可能です。

https://masamitsu-murase.blogspot.com/2022/01/api-microsoft-teams-microsoft-teams.html

ただ、このAPIを呼び出すようなアクションがAutomateにはありません。
（Graph APIでもないので、[別記事](https://qiita.com/hiro_ef/items/29e660ae431269802c2a)で紹介した「「Office365 Groups」コネクタも使えない）

困ったなと思ったら海外のブログに解決策が載っていました。

https://www.damobird365.com/teams-status-update-via-power-automate/

読んでいただくとわかるんですが、要は「Sharepoint」コネクタでAPIを呼び出せるそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/c5dda15e-cddb-f3df-dbfc-9595e531554c.png)
※画像の例では`出力`には「M/d」で日付が、`EndTime`には「yyyy-MM-ddT14:59:00」が設定してあります。
　こうすることで冒頭の画像のように「10/19 在宅勤務」を当日の23:59まで表示することができます。

:::note warn
注意
Sharepointコネクタを使う方法は正規の手順ではないため、突然使えなくなる可能性があります。
:::

# ステータスメッセージを取得する時に気をつけること
ステータスメッセージの設定は上に書いた通りでいいのですが、取得については少し仕込みが必要です。
APIを呼び出す際のBodyにIDを含める必要がありますが、
Micorosft 365のログインIDではなく、内部で自動採番されたID（例：01234567-89ab-cdef-0123-456789abcdef）になります。

これは`マイプロフィールの取得`アクションで取れますので、Automateで実装する場合は以下のようにすると楽です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/d5e15403-8dfa-ecc6-70d9-a12d27020364.png)


# 完成したフロー
フローの全体像がこちらです。
設定済みのステータスメッセージがある場合は何もせず、デフォルトの予定表に「在宅勤務」の終日予定があった場合はステータスメッセージを設定するというものになっています。
このフローが毎日8時に実行されるので、在宅勤務を漏れなくアピールできるようになりました（笑）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919396/df93cef6-8477-f942-9d78-c01e9f0e5ce7.png)


# おわりに
展望というほどではないですが、対象にしている予定が「件名＝在宅勤務」の完全一致なので、そのあたりは改良の余地があると思っています。

皆さんもステータスメッセージを活用してみてください！
Git Submodule 覚え書き

Git Submoduleを使うときに毎回ググっているので、各コマンドとその意味をまとめておく。

もしGit Submoduleの概要を知りたくてこの記事にたどり着いた方は、[Git submodule の基礎](https://qiita.com/sotarok/items/0d525e568a6088f6f6bb)が非常によくまとまっているので、そちらをご覧になってみてください。

参照したリファレンスはこちら。
https://git-scm.com/docs/git-submodule/2.36.0

## add

* `.gitmodules`に新しいサブモジュールを追加する。
* デフォルトで後述する`init`も同時に行われる。

コマンドの基本形は次の通り。

```bash
git submodule add <repository> [<path>]
```

## init

* `.gitmodules`に書かれたサブモジュールを`.git/config`に登録する。
* `.git/config`を変更するコマンドなので、ローカルリポジトリごとに行う必要がある。

コマンドの基本形は次の通り。

```bash
git submodule init [<path>…​]
```

## update

* `.git/config` に登録されたサブモジュールをチェックアウトする。
    * どのコミットがチェックアウトされるかは `git ls-tree <tree-ish> [<path>…​]`で確認できる。
        * E.g. `git ls-tree main mySubmoduleA`
    * 現在チェックアウトされているコミットは`git submodule status`で確認できる。
        * もちろん、サブモジュール内で `git log -n 1`をしてもよい。
* updateは`.git/config` に基づいて実行されるので、予め[init](#init)を行っておく必要がある。
    * `--init`オプションを付けると、updateの実行前にinitをやってくれて便利。
* updateを行うと、`.git/modules/<submodule-path>`が作成あるいは更新される。
    * 中にはサブモジュール用の`.git`フォルダが入っている。

コマンドの基本形は次の通り。

```bash
git submodule update --init [<path>…​]
```

## deinit

* `.git/config`のサブモジュールの登録を削除する。
    * リポジトリからサブモジュールを削除するわけではない。
    * よって`.git/modules/<path>`の中身は削除されない。

## サブモジュールの削除

サブモジュールを削除するには、通常のファイル削除と同じように`git rm`を使う。

```bash
git rm <submodule-path>
```

これは`.git`フォルダ下にゴミを残すため、（例えば同じパスにサブモジュールを再追加したい場合など）必要に応じて手でゴミを削除してあげる必要がある。

参考: [[git] submoduleの削除、再追加について](https://qiita.com/k_yamashita/items/040c04f8798d2384806e)

ローカルリポジトリからサブモジュールを完全に削除するには、次の通りコマンドを実行する。

```bash
git submodule deinit <path>
git rm <path>
rm -rf .git/modules/<path>
```

### deinitする前にサブモジュールを消してしまった

`deinit`する前に`git rm`や`git revert`でサブモジュールを消してしまった場合には、`deinit`が通らなくなってしまう。

その場合、ローカルリポジトリからサブモジュールの痕跡を消し去るには、`deinit`がやっていることを手で実行する必要がある。

具体的には、`.git/config`をテキストエディタで開いて、該当する`[submodule "<path>"]`エントリを削除してあげればよい。
androidってkotlinのactivityからjavaのactivityって遷移できるんだ....

# 要約
気になって試したらタイトルのことができた。
# 本題

Android Studioでは、まず最初にアクティビティを作る際にkotlinかjavaかを選択すると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321651/2a05f77e-9ecf-bb4d-c438-e5e3d09dd25e.png)
*本名使っちゃっているので適当にPathは書き換えてます。

自分はここで選択しているため、Android開発ではできないのかと思っていましたが適当にButtonでkotlinとjavaのActivity間を遷移するアプリを作ってみたらできました(コード等は参考にしたQiita記事のコピペです。)
参考したQiita記事
https://qiita.com/sato-na/items/aebbe482bf40db1fd3b0

まぁkotlin自体はjava互換性があるから動くのは知っていたが、検索してもjavaのactivityが動くのはわからなかったのでメモ程度に残しておく。
モデルやコントローラーの命名規則

## はじめに
モデルのクラス名で誤った名前を気付かないうちにつけてしまい、エラー解消に時間がかかったので、同じミスはしないように記録に残す。

## モデル、コントローラーの命名規則
https://qiita.com/gakkie/items/3afcd505c786364aa5fa

これがシンプルにまとめられていた印象。
【Git】コミットメッセージを修正する

# はじめに
「やべっ、コミットメッセージ間違えた。。。」
こんなことありますよね

そんな時の対処法を紹介します。

# やりかた
```:ターミナル
git commit --amend -m "add: READMEを追加"
```

# おわり
これを知っていれば安心ですね
【Rails】development以外の環境のコンソールに入ったときのConnection refusedエラー

## 環境
Rails 6.0.1
Ruby 2.6.3
PostgreSQL 11.16

## development以外の環境のコンソールに入りたいとき
```
rails console -e production
```

## コンソールに下記のエラーで入れなかったとき

```
could not connect to server: Connection refused
```
```database.yml```内のhost名を```docker-compose.yml```で定義されているDB用コンテナ名と一致させる。

```docker-compose.yml
environment:
  DB_HOST: postgres
  DB_NAME: hoge_development
  DB_NAME_TEST: hoge_test
  DB_USERNAME: postgres
  DB_PASSWORD: password
  REDIS_HOST: redis
```

```config/database.yml
production:
  <<: *default
  host: postgres
  username: postgres
  database: hoge_development
  password: password
```


## 参考
https://regardie.dev/post-4288
ProcessingでCSVファイルの読み込み・書き込み

CSVファイルのデータを加工するツールとして、Excel や Google のスプレッドシートが挙げられますが、
加工するデータが膨大だったり、複雑な加工をしたいときは Processing が便利です。

# 読み込み


```InputSample.pde
void setup(){
  // CSVファイルを読み込み、行ごとにStringとして受け取る
  String[] lines = loadStrings("InputFileName.csv");
}
```

# 書き込み

```OutputSample.pde
void setup(){
  // 書き込み先となるCSVファイルを作成する
  PrintWriter outputFile = createWriter("OutputFileName.csv");

  // CSVファイルに書き込む（実際には出力する値を貯める、バッファに追加される）
  outputFile.println( "ABC,DEF,GHI" );

  // 書き込みを実行する（バッファに貯まった値をファイルに出力する）
  file.flush();

  // ファイル操作を終了する
  file.close();
}
```
gRPCを使ってあそんでみる　サービス起動編

# gRPCを使ってあそんでみるの目的
ログデータを永続化するgRPCサービスを立ち上げログを記録する。

## 今回のお題
ログを記録するgRPCサービスを構築し起動する。

## ■事前環境
・Windows10 バージョン21H2
・IntelliJ IDEA 2022.1.4(Community Edition)
・OpenJDK Runtime Environment Zulu17.36+17-CA (build 17.0.4.1+1-LTS)
・Gradle

## １．IDLのクラスをプロジェクトに追加
https://qiita.com/shigemax999/items/cc4a9f2c64e989ea428e

環境構築編の続きとなります。
生成されたIDLクラスをプロジェクトに追加します。
~~~ build.gradleの必要箇所を抜粋
sourceSets {
    main {
        java {
            srcDirs 'build/generated/source/proto/main/grpc'
            srcDirs 'build/generated/source/proto/main/java'
        }
    }
}
~~~

### ２．サービスクラスを実装
リクエストされた、ステータスとメッセージをログに書き込むサービスになります。
サーバにサービスとして登録することで利用できるようになります。
サービスは[環境構築編](https://qiita.com/shigemax999/items/cc4a9f2c64e989ea428e "環境構築編")でProtoファイルに記載したパッケージ名、サービス名の組合わせで生成される〇〇〇ImpBaseをimplementsして実装します。
 <B>[java_packag].[サービス名]Grpc.LoggingImplBase</B>


<details><summary>ソース詳細</summary><div>

~~~ LoggingService
package logging;

import io.grpc.stub.StreamObserver;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import logging.proto.Logging;
import logging.proto.LoggingServiceGrpc;

public class LoggingService extends LoggingServiceGrpc.LoggingServiceImplBase {

    private final Logger logger;

    LoggingService(final String path) {
        this.logger = Logger.getLogger(LoggingService.class.getName());
        try {
            FileHandler fileHandler = new FileHandler(path);
            fileHandler.setFormatter(new SimpleFormatter());
            this.logger.addHandler(fileHandler);
        }catch(Exception e){
            System.out.println(e.toString());
        }
    }

    @Override
    public void write(Logging.LoggingRequest request, StreamObserver<Logging.LoggingResponse> responseObserver) {
        Logging.LoggingResponse loggingResponse = Logging.LoggingResponse.newBuilder().setStatus("OK").build();
        // ログへメッセージの書き込み
        this.logger.info(request.getMessage());
        responseObserver.onNext(loggingResponse);
        responseObserver.onCompleted();
    }
}
~~~
</div></details>

### ３．サーバ起動クラスを作成
ポート8080で待ち受けしd:\\server.logにログを出力するようにしてます。
addServiceにてサーバに２で作成したサービスを登録しています。

~~~ ServerMain
package logging;

import io.grpc.Server;
import io.grpc.ServerBuilder;

import java.io.IOException;

public class ServerMain {

    public static void main(String[] args) throws IOException, InterruptedException {
        final String path = "d:\\server.log";
        final int port = 8080;
        Server server = ServerBuilder.forPort(port)
                .addService(new LoggingService(path))
                .build();

        server.start();
        System.out.println("start server on port:" + port);
        server.awaitTermination();
    }
}
~~~

### ４．サービスを実行
ServerMainクラスを実行します。
8080ポートで待ち受け実行されます。
~~~
> Task :ServerMain.main()
start server on port:8080
~~~

## 参考
今回、利用したソース一式です。
https://github.com/shigemax999/sample-grpc-logging

## 次回
【Vue】v-for で前の要素と比較した値を取得する方法

vueで次のような繰り返しにおいて、

```App.vue
<div v-for="(data, idx) in dataList" :key="idx">
名前:{{data.name}}
点数:{{data.score}}
</div>
```


前の要素の点数との差を出力したいときは　次のようにすればOKです。


```App.vue
<div v-for="(data, idx) in dataList" :key="idx">
名前:{{data.name}}
点数:{{data.score}}
前の点数との差: {{data.score - dataList[idx - 1].score}}
</div>
```
【Athena】大量のファイル群からのスキャン量をファイルパスをマッピングして削減する

# 大量のファイルからのスキャン量を減らしたい

Athenaの料金はスキャン量に比例するので例えば
```
1.csv.gz
2.csv.gz
3.csv.gz

99999.csv.gz
100000.csv.gz
```
といったファイルがあり、その合計が数テラバイト（とにかく大きいデータ）あるとします。
これらは例えば時系列データで2015年〜2022年のデータが入っているとします。

スキャン料を減らす方法として、parquet化やpartitionで分けるというやり方があるのですが、ここで問題が発生します。
時系列データならpartitionで分けるのが一番いいのですが、partitionに振り分けるには一度のスキャンでは100個のパーティションでしか分けられません。
なので最大でも日付単位でパーティションを分けると１年のデータを作るのに3ヶ月ごとにSQLを分けなければならないため、1年で4回のフルスキャンが発生します（今回はCSVなのでフルスキャンとなる）。例えば10TBのデータであれば10TB*4で40TBのスキャンが必要になります。
>1 TB あたり 5.00USD

https://aws.amazon.com/jp/athena/pricing/

という料金設定だと200USDつまり3万円程度かかります。これが大きいかどうかは個人差がありますが、減らせるに越したことはないでしょう。

# どのファイルにいつのデータが入っているかマッピングする

https://qiita.com/YujiHamada3/items/d9ef8707dcbba22bbaea

上記記事でも書いたのですが、Athenaのクエリで行のpathが取得可能です。
なのでどのファイルにいつのデータが入っているのか一度マッピングしておけばフルスキャンを避けることができます。
今回は `huge_data_csv_table` というテーブルにtimeというタイムスタンプが入っているとします。
これを以下のSQLでどのファイルにどのデータが入っているかをマッピングします

```sql
CREATE TABLE huge_data_date_mapping
    WITH (
    format='PARQUET',
    external_location='s3://example-backet/date_map'
) AS

SELECT DISTINCT
    "$path" as path,
    date_format(time, '%Y%m%d') as date,
    date_format(time, '%Y%') as year,
    date_format(time, '%m') as month,
    date_format(time, '%d') as day
FROM
    huge_data_csv_table
```

日付の持ち方はおまかせします…

で、この`huge_data_date_mapping` というのにどのファイルにいつのデータが入っているかが
```
"path","date","year","month","day"
"s3://example-backet/date_map/1.csv.gz","20200506","2020","5","6"
"s3://example-backet/date_map/2.csv.gz","20200505","2020","5","5"
"s3://example-backet/date_map/3.csv.gz","20200508","2020","5","8"
...
```
このように管理できるようになります。

# データを取り出す

データを取り出すときはこのmappingテーブルからファイルを絞ることでスキャン量が減らせます。

```sql
SELECT
    *
FROM 
    huge_data_csv_table
WHERE
    "$path" in (SELECT path FROM huge_data_date_mapping WHERE year = 2020 and month = 5)
```

こうすることでパーティションを分けるにしてもスキャンするファイルを絞りながらパーティションに分けていく作業が可能となりました。
Salesforce上級アドミニストレーター試験の公式サンプル問題解説してみた-04

こんにちは
本橋孝昭です

今回の内容は動画でも解説しています

https://www.youtube.com/watch?v=9KlyTbHxdp0

<iframe width="560" height="315" src="https://www.youtube.com/embed/9KlyTbHxdp0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


Salesforce上級アドミニストレーター試験の公式サンプル問題があるのはご存じでしょうか

こちらの受験ガイドからサンプル問題を確認することが可能です

SALESFORCE 認定上級アドミニストレーター

https://quip.com/dyBMAO1z6pJ9/SALESFORCE-

認定制度

https://tandc.salesforce.com

今回は
Salesforce上級アドミニストレーター試験の公式サンプル問題3問目をご紹介します

なお、
以下の問題は、Salesforce 認定上級アドミニストレーター試験のサンプル問題です。問題はあく まで出題形式に慣れていただくためのものであり、試験に合格するかどうかを判断するための問題ではありません。実際の問題の難易度は、以下のサンプル問題とは異なる場合があります。

と書いてありますのでご確認ください

問題はこちらです

4）
ユニバーサルコンテナ社の営業マネージャは、活動レコードが存在しない商談をすべて確認 したいと考えています。
この要件を満たすにはどのレポートタイプを使用すればよいですか？ 答えを 2 つ選んでください。

A. 主オブジェクトに商談、第 2 オブジェクトに活動を設定したカスタムレポートタイプ
B. クロス条件に活動のない商談を設定した商談レポート
C. ブロック A に商談レポート、ブロック B に ToDo 別に絞り込んだ ToDo と行動レポート を使用する結合レポート
D. 日付項目に最終活動日項目を設定し、範囲をカスタムで日付を空白に設定した標準商談 レポート

正解はBとDです


Dの最終活動日はこちらのヘルプをご確認ください

https://help.salesforce.com/s/articleView?id=000385365&type=1
【データ基盤構築/BigQuery】ベーステーブルを全量洗い替え更新してしまうと、Materialized Viewの利点が得られない

# 今回の課題
データポータルでBigQueryのMaterialized Viewをデータソースにしてレポートを作成すると、
Materialized Viewのベーステーブル更新後に下記のエラーが発生してしまうようになった。

### エラー内容
```
データセットの設定エラー
Looker Studioではデータセットに接続できません。

基になるデータセットからデータを取得できませんでした

BigQuery error: Materialized view ビュー名 references table プロジェクト名:ビュー名 which was deleted and recreated. The view must be deleted and recreated as well.
エラー ID: e74aa98e
```


# Materialized Viewとは
Materialized Viewとは簡単に言うと、
**ベーステーブルから差分のみ読み取って結果を返してくれる実体を持つビュー**のこと。

ベーステーブルにデータが追加された場合、
そのベーステーブルから生成されているMaterialized Viewにも増分のデータの変更が追加されるイメージ。

また、Materialized Viewに対してクエリを実行することもでき、
そのクエリは高速で消費リソースも抑えて実行される。
[マテリアライズド（実体化）ビューの概要（公式ドキュメント）](https://cloud.google.com/bigquery/docs/materialized-views-intro?hl=ja)



Materialize Viewを使用すると上記のようなメリットが得られるので、
データマートを作成してそこからデータポータルに繋げるよりも、
Materialized Viewを作成して、そこからデータポータルに繋げた方が、パフォーマンスが向上するのではと考えた。
しかし、エラーが発生してうまく活用できなかった。

## なぜ今回エラーが発生してしまったのか
タイトルの通り、**Materialized Viewのベーステーブルを全量洗い替え更新としていたから**だった。
公式ドキュメントに記載の通り、
ベーステーブルを全量洗い替え更新（テーブルを削除してから再作成）した場合、エラーとなるようだった。

**Materialized Viewも再作成する**とエラーは発生しないようだが、
Materialized Viewの**差分データを取得してパフォーマンスを上げる**というメリットを活かすことができないので、
いつも通りデータマートを作成してデータポータルに接続することにした。


# Materialized Viewの注意点を調べてみた
上記のような注意点が他にも無いか調べてみたところ、いくつかあった。

* Materialized Viewは、元になるテーブルと同じデータセット内に生成しないと、効果が得られない。
* WINDOW関数を使用できない
* COUNTでDISTINCTオプションを使えない
* スカラサブクエリを使用できない
* ベーステーブルを全量洗い替え更新にすると、効果が得られない。
└マテリアライズドビューを削除せずにベーステーブルを削除した場合、マテリアライズドビューに対するクエリは失敗し、更新も失敗する。
└マテリアライズドビューを再作成する場合は、マテリアライズドビューも再作成する必要がある。

[マテリアライズドビューの制限事項（公式ドキュメント）](https://cloud.google.com/bigquery/docs/materialized-views-intro?hl=ja#limitations)


# 今回分かったこと
**ベーステーブルにINSERTで行を挿入して更新（差分更新）**
する場合は、Materialized Viewの恩恵を受けられるが、全量洗い替えでは受けられない。
nginxで50xエラーを発生させてerror_pageの検証

# 概要

nginxのデフォルトのエラーページを返さないようにカスタムエラーページを設定する。
リバースプロキシとして使用する際、httpディレクティブとserverディレクティブのerror_pageの書き方によってどのようにエラーページを返すか、実際に50xエラーを発生させて確認したので内容のまとめ

# インフラの準備
* EC2でnginxを２台準備
* 1台はリバースプロキシとして使用
* 1台は転送先として使用
* 転送先にはphp-fpmを入れてFastCGIが利用できる様にしておく
※phpで意図的に50xを発生させるために必要

# nginxの設定
## リバースプロキシ
```shell:proxy.conf
upstream origin {
  server 1.2.3.4:80;

}
server {
  proxy_read_timeout 5;
  proxy_connect_timeout 5;
  proxy_send_timeout 5;
  listen 8080;
  server_name  localhost;
  location / {
    proxy_pass http://origin;
  }
}
```

# 50xのエラーコードを発生させる準備
## リバースプロキシでエラーを発生させる
![スクリーンショット 2022-10-19 16.04.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/2ad6c6fa-cc9c-1cf7-4cfb-484c8e80f766.png)

* 502エラー
転送先のnginxを停止させた状態でリバースプロキシから転送先にリダイレクトすると502エラーが発生する

* 504エラー
転送先に下記の様に10秒待機後、`Hello World`が表示されるphpを置いてここにアクセスする
nginxのタイムアウト設定を5秒にしているのでタイムアウトして504エラーが発生する
```php:/usr/share/nginx/html/phptest.php
<?php 
sleep(10); die( 'Hello World' ); 
?>
```

## 転送先でエラーを発生させる

![スクリーンショット 2022-10-19 16.05.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/860becad-3a32-7a25-7cb7-f2d0d7e1efea.png)

* 50xエラー
下記の様に該当のエラーコードを返すphpをエラーコードの分だけ作成する
ここにアクセスすると該当のエラーが発生する

```php:/usr/share/nginx/html/500.php
<?php
header("Status: 500 Internal Server Error");
?>
```

# error_pageの書き方

![スクリーンショット 2022-10-19 16.34.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/d44f1148-3f51-d21a-ca79-2b2513f07aae.png)

httpディレクティブで全体の設定を入れてリバースプロキシ毎のserverディレクティブで個別の設定を入れてみる
`custome-error.html`が用意したカスタムエラーページです

# 検証結果

![スクリーンショット 2022-10-19 16.41.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/6b33c2fe-0973-6fe3-e63d-f5c60a342693.png)

* serverディレクティブに書いた`error_page`で返す
* 転送先でエラーが発生した場合、serverディレクティブでエラーコードが定義されていればそれを返す
* 定義されていない場合は転送先のエラーを返す
* serverディレクティブ内の`error_page`を削除すると転送先の`custome-error.html`を見ようとして404になった
PHP 8.0 + Laravel 6.x でSoapClient使うとエラーになるみたい

# 概要
表題の通り、PHP 8.0 + Laravel 6.x でSoapClient使うとエラーになるみたい

# 詳細
[Laravelのサポートポリシー](https://readouble.com/laravel/8.x/ja/releases.html#support-policy)見る限り、Laravel 6.x でも PHP8.0使えるっぽいけど…

PHP 8.0 にしたら、SoapClient使ってるとこで
```
include(): Failed opening '/home/runner/work/hoge/vendor/composer/../phpunit/phpunit/src/Framework/ExceptionWrapper.php' for inclusion (include_path='.:/usr/share/php')
```
的なエラーが出ちゃう

# 対策
Laravel 7.x までアップしたら解決。
※ Laravel 7.x もサポート切れてるから…ちゃんとアップしようね
Ruby – カラーコードをRGBに変換する

```
gem install color
```

```rb
require "color"

Color::CSS[:red].to_rgb.to_a.map { |rate| (rate * 255).round }
# => [255, 0, 0]

Color::CSS[:orange].to_rgb.to_a.map { |rate| (rate * 255).round }
# => [255, 165, 0]
```

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
AWS Lambda 料金形態

# はじめに
AWS Lambda を使って何かを作ってみる。
だが、その前に課金の仕組みについて理解したいので記録する。

# AWS Lambda の料金
課金は以下に基づいて生じる
- 関数へのリクエスト数
- コードの実行時間

### 関数へのリクエスト数の条件
リクエスト数は以下の条件でカウントされる
- イベント通知トリガー 
(Amazon SNS, Amazon EventBridge など)
- AWS SDK経由などの呼び出し
(AWS Console からのテスト呼び出しを含む)

### コードの実行時間
- コードの実行が開始されてから、戻り値またはその他の方法で終了するまでの時間を計算される(1ms未満を切り上げる)
- 価格は、関数に割り当てるメモリの量に依存する。
**AWS Lambda では関数の必要なメモリ量を選択。CPUパワーやその他のリソースが比例して割り当てられる。**

# AWS Lambda の無料利用枠
- 月間 100 万件のリクエスト
- 月間 40 万 GB 秒の計算時間 


# 参考
[AWS Lambda 料金](https://aws.amazon.com/lambda/pricing/)
FirebaseのFunctionsをPython, Java, C#, GO等で使う

Cloud Functions for FirebaseでPythonを使うことはできません。Cloud Functions for Firebaseのドキュメントを見れば分かるようにNode.jsにしか対応していないからです。でも、ちょっとした裏技を使うとPythonなど他の言語を使うことができます。

# Cloud Functions for Firebase　とCloud Functionsの関係

簡単に言うと、Cloud Functions for Firebase　は、　Cloud Functionsのラッパーみたいなものです。裏側ではCloud Functionsが動いており、それを使うためのデプロイツールとAPIがFirebase側でも提供されているだけなのです。つまり、Cloud Functionsを直接使えばFirebaseとFunctionsを組み合わせることができます。

コマンドで言うと以下の通りで、これで検索すればそれぞれのドキュメントを見ることができるでしょう。

```sh
# Cloud Functions for Firebaseの場合
firebase deploy --only functions
# Cloud Functions の場合 
gcloud functions deploy 
```

ここで疑問に思うのは、Cloud Functions for Firebase　がラッパーとして何をしているか、それをラッパーなしで実現できるかということです。Cloud Functions for Firebase　は２つのことをしています。

1. firebase deployでは、プログラム内でメソッドの名称とトリガーを指定できる。gcloud functions deploy では、トリガーの種類、イベント名と実行するメソッドをコマンドラインの引数で指定する。Firebase独自のトリガーもすべてgcloud functions deployで利用可能なので、機能的な差異はない。（イベントリストはgcloud functions event-types listを参照）。以下はPythonの場合のサンプル。

    ```sh
    gcloud functions deploy hello_world --region=asia-northeast1 --runtime=python310 --source=./src --entry-point=hello_world --trigger-http
    ```
2. Cloud Functions for Firebase　ではonCallというトリガーがあり、これを使うとFirebaseからの呼び出し時の認証を自動で行ってくれる。これは、Python, Java, C#, GOで提供されているfirebase_adminツールを使えば対応可能。Ruby, PHPの場合、firebase admin apiは提供されていないが、JWTのパーサーを使えば対応可能と思われる。以下はPythonの場合のサンプル。

```python3

from typing import Union
from flask import jsonify, Request, Response
from flask_cors import cross_origin
from firebase_admin import auth, initialize_app

firebase_app = initialize_app()

def getToken(request: Request) -> Union[dict, None]: 
    authorizationHeader = request.headers.get("Authorization", None)
    encodedToken = authorizationHeader.removeprefix("Bearer ") if authorizationHeader else None
    token = auth.verify_id_token(encodedToken) if encodedToken else None
    return token

@cross_origin()
def hello_world(request: Request) -> Response:
    body = request.get_json()
    token = getToken(request)
    isMember = bool(token.get("role")) if token and type(token) == dict else False
    return jsonify({
        "data": {
            "token": token,
            "isMember": isMember,
            "body": body,
            "token": token
        }
    })
```

# まとめ

こう見ると、Cloud Functions for Firebaseがかなり薄いラッパーであり、Node.js以外の言語でFunctionsを書くのも、そんなに難しくないことが分かります。自分はバックエンドをNode.jsで書くことが多いのですが、データ分析や機械学習の用途でPythonを使う場合、わざわざLambdaを使ったりコンテナを立てるより、Cloud Functionsを直接使う方法を知っておいた方が便利ではないかと思います。
Go言語入門 学習メモ 5 type、for..range

# はじめに
船井総研デジタルのoswです。業務でGo言語を使うことになったのでこれから学習していきます。その備忘録です。参考になる方がいらっしゃれば幸いです。

# 対象読者
- これからGo言語を学習する方
- 既に他の言語で基本構文を学習されている方

# 学習環境
学習環境は次のようになっています。この環境の構築メモは下記記事にまとめてあります。ご興味がある方はご参照ください。
- Windows 11 Home / 22H2
- VSCode / 1.72.2
- go version go1.19.2 windows/amd64
- git version 2.38.0.windows.1

https://qiita.com/fsd-osw/items/0e569b0b828455cdef9e

# 前回までの学習
前回は ポインタ、コンポジット型(配列、構造体、スライス、マップ)を学習しました。

https://qiita.com/fsd-osw/items/b4e4d87cfe8f1ab100c5

# ユーザ定義型
typeは新規の型を作るためのキーワードで、Cでいうところのtypedefのようです。typeは構造体の学習でも出てきました。構造体自体はstructで宣言するでしょうし、typeは何者？と思っていましたが、新規で型を作成するキーワードということで謎が解決しました。

```go:構文
// 標準で既に定義されている型(基底型)に別名を付ける、構造体に型名を付与する
type ユーザ型名 型
```

```go:サンプルコード
package main

import "fmt"

func main() {
    // 型を新しく定義
	type SampleInt   int
	type SampleFloat float64

	var i SampleInt   = 100
	var f SampleFloat = 3.14

    // 書式指定に%Tを指定すると型を取得できる
	fmt.Printf("i: %4d %T \n", i, i)
	fmt.Printf("f: %.2f %T \n", f, f)
}
```

```go:実行結果
i:  100 main.SampleInt 
f: 3.14 main.SampleFloat 
```

書式は下記記事で解説してくださっています。

https://zenn.dev/mkosakana/articles/97466310534b4c

# for range
Goには繰り返し構文がforしかないようですが、rangeを使うと拡張for文のような書き方ができるようで便利です。データ取得の対象はコンポジット型ならとりあえず適用できるようです。サンプルコードでは配列、スライス、マップで試しています。

また、C++ / Javaなどと違い、要素を取り出してくるだけでなくインデックスも取得できるようです。インデックスが不要な場合は"_"で破棄します。
```go:構文
for インデックス格納用変数, 値用変数 := range データの取得元 {
    // 処理
}

// 変数を1つだけ用意するとインデックスのみ取得する
for インデックス格納用変数 := range データの取得元 {
    // 処理
}

// 不要な値は"_"で捨てることができる。"_"はPrint()などで中を出力できない
for _, 値用変数 := range データの取得元 {
    // 処理
}
```

```go:サンプルコード
package main

import "fmt"

func main() {

	array := [...]int{0, 1, 2, 3, 4}
	slice := array[:]
	mp    := map[int]string{
		0: "ichiro",
		1: "jiro",
		2: "saburo",
		3: "goro",
		4: "rokuro",
	}

	// 配列、スライス、マップに対しfor..rangeが適用できるか動作確認
	fmt.Println("配列の動作確認")
	for i, v := range array {
		fmt.Println("i:", i, ", v:", v)
	}

	fmt.Println("\nスライスの動作確認")
	for i, v := range slice {
		fmt.Println("i:", i, ", v:", v)
	}

	fmt.Println("\nマップの動作確認")
	for i, v := range mp {
		fmt.Println("i:", i, ", v:", v)
	}

	tmpArray := [...]int {10, 11, 12, 13, 14}

	// 変数を1つだけ用意するとインデックスのみ取得する
	fmt.Println("\nインデックスのみ取得")
	for i := range tmpArray {
		fmt.Println("i:", i)
	}

	// 不要な値は"_"で捨てることができる。"_"はPrint()などで中身を出力できない
	fmt.Println("\n値のみ取得")
	for _, v := range tmpArray {
		// fmt.Println("_:", _) "_"は出力できない
		fmt.Println("v:", v)
	}
}
```

```go:実行結果
配列の動作確認
i: 0 , v: 0
i: 1 , v: 1
i: 2 , v: 2
i: 3 , v: 3
i: 4 , v: 4

スライスの動作確認
i: 0 , v: 0
i: 1 , v: 1
i: 2 , v: 2
i: 3 , v: 3
i: 4 , v: 4

マップの動作確認
i: 4 , v: rokuro
i: 0 , v: ichiro
i: 1 , v: jiro
i: 2 , v: saburo
i: 3 , v: goro

インデックスのみ取得
i: 0
i: 1
i: 2
i: 3
i: 4

値のみ取得
v: 10
v: 11
v: 12
v: 13
v: 14
```

# おわりに
今回はここまでです。
wsl2を使ってMySQLに接続できないときにパスワードを確認する

wsl2とDocker Desktopを使ってMySQLの環境構築を行いました。
wsl2からMySQLへの接続の際に下記のエラーができました。

```ubuntu
$ mysql -u root -p -h 0.0.0.0 -P 3306
Enter password:
ERROR 2003 (HY000): Can't connect to MySQL server on '0.0.0.0' (111)
```

mysqlへのログインはubuntuのrootパスワードではなく、今回、rootでログインを試みているためmysqlのrootパスワードを使用します。
mysqlのrootパスワードの確認はDocker Desktopの該当のコンテナーのInspectに記載されています。

![スクリーンショット 2022-10-19 165412.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/e1688bc5-5d56-57e4-5632-021c1f1df039.png)

mysqlのrootパスワードを使ったところログインできました
```ubuntu
$ mysql -u root -p -h 0.0.0.0 -P 3306
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 38
Server version: 8.0.31 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

参考
https://qiita.com/T-Ooshio83/items/517c905410d07bfe4f71#%E5%AE%9F%E6%96%BD%E7%92%B0%E5%A2%83
初心者が線形判別分析(LDA = Linear Discriminant Analysis)を解説してみた


### 解説の前に
筆者は学生であり専門で機械学習を学んでいるわけでもなく完全に趣味なので専門性の高い解説を求めている方は他の記事を読んだほうがてっとり早いと思われます。
この記事は初学者による初学者のための記事であり、同じ目線に立って解説することを求めています。
もし間違っていることがあればコメントでご指摘いただけると幸いです。

# Linear Discriminant Analysisとは？

日本語に直すと線形判別分析といいます。日本語の解説も少なかったので英語の記事を読みました。

## 早速解説
まず前提としてロジスティック回帰アルゴリズムは2クラスのみに制限されていますが、今回扱う線形判別分析(LDA)では3クラス以上の分類問題に適用できます。
LDAは次元を削減するときに使われる技術であり2クラス以上の分類問題を解決するために使われることが多いです。また主に機械学習の前処理やパターン識別を行う際に使われることが多いです。


### なぜ次元を減らす必要があるの？
皆さんも御存知の通り世の中は3次元の世界で作られています。そのため人間は3次元以上のものを認識することは難しいと言われています。
しかし機械学習を実行すときに次元が4つ以上であるとグラフ化したときに人間がそれを認識することができなくなってしまうので、次元数を減らし可読性を向上させることが必要になってきます。

#### 次元の呪い
次元の呪いとはデータの属性が増えると指数関数的にデータの組み合わせが増加してしまう現象のことを指します。
すなわちデータの次元が増加すると爆発的にデータの計算量が増えてしまうことです。
このような理由から一般的に機械学習では高次元のデータは次元を減らす工夫が施されています。

## 概要:LDAの仕組み
![linear-discriminant-analysis-in-machine-learning3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728563/03596369-7b20-ee01-9461-ba6785419e37.png)
上図のようなXY軸を持つ平面に2つのクラスがありそれらを分類するときを例に出します。

このような場合にLDAを使うとデータポイントの2つのクラスを完全に分離できる直線を引くことができます。今回は青い直線がそれになります。
さらにLDAはデータポイントを直線で分離し、点線のような新しい軸を引くことでクラス間の分離を最大化をし2次元平面を縮小することができます。

### 新しい軸を作成するための基準
- 2つのクラスの平均間の距離を最大化する
- 個々のクラス内の分散を最小限に抑える

上記のように、2つのクラスの平均間の距離を最大化し各クラスの分散を最小化することを条件に新しい軸を作成します。

## LDAを採用する理由
- ロジスティック回帰では分散の大きい複数クラスの分類問題の場合はうまく機能しません。一方LDAはこのような条件下でも効率的に処理することができるためです。
- LDAは計算コストを大幅に削減し、特徴量の数を減らすこともできるためです。

# まとめ
線形判別分析(LDA = Linear Discriminant Analysis)は機械学習の次元削減で用いられる手法であり、分散の大きな場合でも処理が可能。
GASからNotionのPage Propertyの内容を変更

# 目的
- GASからNotionのPage Propertyの内容を変更すること

# 本記事で紹介しないこと
- NotionAPI取得方法
- GASプロジェクトの作成方法
- GASの書き方　※そもそも、筆者は初めてGASを使用したので書き方に不慣れです
- ページの追加・削除

# 環境
- GASプロジェクト：2022-10-19 最終操作日
- Notion-Version：2022-06-28

# 本記事で使用する題材
- 筆者が日常で利用しているNotionのタスク管理データベース 図のようなものです
![Taskのテンプレート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691844/08d4732e-2cc3-038b-d8e7-3f78d56a2c80.png)

# 内容
```javascript
const ENDPOINT_CHANGE_STATUS_PAGE = "https://api.notion.com/v1/pages/";
const URL = ENDPOINT_CHANGE_STATUS_PAGE + getTaskId(task);
const NOTION_TOKEN = PropertiesService.getScriptProperties().getProperty("notion_token");

const payload = {
  properties: {
    Status:{ 
      id: '%5EOE%40',
      type: 'select',
      select: { 
        name: 'Doing', 
      } 
    }
  }
}
const options = {
  method : "PATCH",
  headers: notionHeader(NOTION_TOKEN),
  payload : JSON.stringify(payload),
}

UrlFetchApp.fetch(URL,options)
```
#### ENDPOINT_CHANGE_STATUS_PAGE
- エンドポイントになります　
- Databaseの情報を操作するわけではないので最後はpagesになってます

#### URL
- fetchに渡すためのurlです　
- エンドポイントの後にPropertyに操作を加えたいページのidを記述することで特定のページの操作を可能にします
- 自分はgetTaskId(task)というメソッドを作って他のところでもid取得操作の再利用をしてます

####  getTaskId
- APIを用いて取得したNotionのページObjectは以下のようなJSONで管理されています
- 直下にあるidを取得してエンドポイントの末尾にくっつけます 
- プログラム的にはtask["id"]などで取得できます
```text
{ 
  object: 'page',
  id: 'ここの値をエンドポイントの後ろにくっつける',
  created_time: '2022-10-18T14:02:00.000Z',
  last_edited_time: '2022-10-18T14:05:00.000Z',
  created_by: { object: 'user', id: '見せられないよ' },
  last_edited_by: { object: 'user', id: '見せられないよ' },
  cover: null,
  icon: null,
  parent: 
   { type: 'database_id',
     database_id: 'データベースのid' },
  archived: false,
  properties: {  },
  url: 'PageのURL' }
}
```

#### NOTION_TOKEN：
- Notionのトークンです

#### payload：
- 更新させたい内容をNotion ObjectのJSON形式で渡します
- 今回はPageのproperty情報を更新したいので PageObjectの直下"properties"を操作します
- propertyに使える項目は[参考文献2](https://developers.notion.com/reference/property-item-object)にのってます今回はSelect property valuesを例にします
- Statusの部分は自分でpropertyを作成したときの名前です日本語の場合はダブルクォーテーションで囲みます　
- Statusの中に行くとselectがあり name color のoption情報が変更できます　
- 変更する際には　対象：”変更後の値” color: "red"　と書くことで情報の更新ができます

#### options
- methodの値をPATCHにすることで情報の更新ができる
- POSTなどだと失敗します



# まとめ
- Notaion property itemのリファレンスを見てpropertiesのJSONを組み立てる
- JSONに送る際にmethodをPATCHにする

# 参考文献
1. NotionDevelopers Update Page https://developers.notion.com/reference/patch-page (Access 2022-10-19).
1. Notion property-item-objec https://developers.notion.com/reference/property-item-object (Access 2022-10-19).
Google Colaboratory で Python の基礎を学ぶ

こんにちは。[東北TECH道場 郡山道場](http://koriyama.tohokutechdojo.org/)でスタッフをしています。今年度（第7期）は [Google Colaboratory](https://qiita.com/Aster_ideatech/items/3ae6ea5e5a011ebcd0b7)を使用して、Python の基礎を学ぼうということで月に1回活動しています。そこで、Pythonの基礎を学習するための資料をGithubに公開しました。「Open In Colab」ボタンを押すと Google Colaboratory で開いて実行することができます。「Python の基礎」を勉強してみたいという方がいましたらご利用ください。

# 「Python の基礎」
https://github.com/tomato-sugar/python_lesson/tree/main/python_basic

# 講座の内容

## python_basic1.ipynb
- Pythonの特徴
- コメントの書き方
- 変数とデータ型
- 画面出力とキー入力
- 演算子

## python_basic2.ipynb
- コレクション

## python_basic3.ipynb
- 制御文

## python_basic4.ipynb
- 関数

## python_basic5.ipynb
- ファイル入出力
- 例外処理
- ちょっとだけオブジェクト指向

※「_ans」が付いていないファイルは文法説明と例文と練習問題です。各自、文法事項を抑えながら練習問題を解いてください。
※「_ans」が付いたファイルは、練習問題の解答例が記されています。練習問題の解答を確認するときに使用してください。
M5Burner v3とesptool.pyを使ってプログラムとファイルシステムを含むflashイメージを簡単に焼く方法


## My Story

こんな長いコマンドでbinファイルをフラッシュに焼きたくない！（渡辺謙さん風）

```
> esptool.py --chip esp32 --port COM14 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 boot_app0.bin 0x1000 bootloader_qio_80m.bin 0x10000 WatchdogTimer.ino.bin 0x8000 WatchdogTimer.ino.partitions.bin
```

これぐらいの長さのコマンドラインでフラッシュを焼きたい。

```
>　esptool.py --port COM14 -b 1500000 write_flash 0x00000 image4M.bin
```

## I noticed

M5Burner v3を使うとESP32の内蔵flashに書き込み済みflashイメージをシリアル経由でダウンロードしてファイルにできるのね。。。

## Solution

次の方法はいかがでしょうか？

1. Arduino IDEやPlatformIO IDEを使ってESP32にプログラムとファイルシステムをアップロードする
1. M5Burner v3のFirmware ExporterでESP32内のフラッシュイメージをPCへダウンロードする(esp32fw.bin)
![2022-10-19_203353.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103572/3abdb671-8fd5-8632-f3a5-5c4a3ee54234.png) 
M5Bunerの右上のバーガーメニューを選択する
![2022-10-19_160741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103572/af6718c2-0c82-63db-24d9-b9688460745f.png)
Sizeはお使いのESP32のフラッシュサイズに合わせてください
1. esptool.pyの次のコマンドを使ってflashイメージを他のESP32へ書き込む
```
>　esptool.py --port COM14 -b 1500000 write_flash 0x00000 esp32fw.bin
```

なんということでしょう。プログラムが動いたではありませんか（棒）

## Conclution

長文コマンドをなんとか入力しないで済むにはどうしたらよいのか、考えたあげくになんとなくひらめいたことをやってみたら、たまたまうまくいきました。

いくつものESP32に同じファームを焼く人におすすめします。いかがでしょうか？

どこかのどなたかのなんらかに貢献できましたら幸いです。

## See Also

https://lang-ship.com/blog/work/esp32-esptool-py

https://zenn.dev/saitotetsuya/articles/m5stack_m5burner_v3
IPM逆透視マッピングよりBEVを得る

逆透視マッピング(IPM)で画像のBird's Eye View(BEV)を得る方法を紹介致します。
<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668082/e2def706-9b2a-b64c-3dcc-9519ef9bcfb7.jpeg">
から
<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668082/84dd9d73-4a01-3ec5-7313-4b80058e5eb9.png">

# BEV（Bird's Eye View）
BEV画像とは、上空から斜めに見下ろしたような形式のものを言います。鳥瞰図、俯瞰図、パノラマ図とも言えます。
<img width="700" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668082/342010aa-a9c6-77da-f02a-7747fd13608a.png">
# IPM（Inverse Perspective Mapping）
カメラで撮影された画像は、透視投影なので、本来平行だったものは画像内で交差します。IPMでこのような透視投影をBEVに変換できます。そのため、逆透視変換とも呼ばれます。IPM変換方法として、OpenCVの関数を利用します。理論知識について、これを参考ください：https://towardsdatascience.com/a-hands-on-application-of-homography-ipm-18d9e47c152f
##### １．利用するOpenCVの関数：**cv2.getPerspectiveTransform**と**cv2.warpPerspective**
**cv2.getPerspectiveTransform**（変換するマトリックスをゲット）
- 書き方： cv2.getPerspectiveTransform(src,dst)
- src: Source画像の四角形の頂点の座標
- dst: 生成される画像内の対応する四角形の頂点の座標

**cv2.warpPerspective**（Mを用いて変換する）
- 書き方： cv2.warpPerspective(src, M, dsize)
- src: Source画像
- M: 変換マトリックス
- dsize: 出力画像のサイズ

##### ２．手順
以下の画像を例として使います。
<img width="600" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668082/e2def706-9b2a-b64c-3dcc-9519ef9bcfb7.jpeg">
１).４つの点を選びます。タブレットの頂点にします。Matplotlibを利用して、座標を求めます。
```py:get_coordinates.py
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Qt5Agg')

img = cv2.imread("tablet.jpg")
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

plt.imshow(img)
plt.show()
```
座標を入手します。
結果：
```coordinates.
p1 = [58,579]
p2 = [208,758]
p3 = [720,580]
p4 = [520,445]
```
２).出力画像の長さと高さを求めます。出力画像はソース画像中の選んだ４つの点からなるので、４つの点の間の距離を算出します。
```py:get_H_W.py
p1 = [58,579]
p2 = [208,758]
p3 = [720,580]
p4 = [520,445]

import numpy as np
import math

def getDist_P2P(PointA,PointB):
    distance=math.pow((PointA[0]-PointB[0]),2) + math.pow((PointA[1]-PointB[1]),2)
    distance=math.sqrt(distance)
    return distance

d14 = getDist_P2P(p1,p4)
d23 = getDist_P2P(p2,p3)
d12 = getDist_P2P(p1,p2)
d34 = getDist_P2P(p3,p4)

max_w = max(int(d14),int(d23))
max_h = max(int(d12),int(d34))

print(max_w,max_h)
```
これで**cv2.warpPerspective**関数のパラメーター**dsize: 出力画像のサイズ**の値を決定しました。（max_w，max_h）です。

３).OpenCV関数**cv2.getPerspectiveTransform**と**cv2.warpPerspective**を利用して変換します。
```py:ipm.py
import cv2
import matplotlib.pyplot as plt
import numpy as np

from get_coordinates import img
from get_H_W import p1,p2,p3,p4
from get_H_W import max_w,max_h

src = np.float32([p1,p2,p3,p4])
des = np.float32([[0,0],[0,max_h],[max_w,max_h],[max_w,0]])

M = cv2.getPerspectiveTransform(src,des)

output = cv2.warpPerspective(img,M,(max_w,max_h))

plt.imshow(output)
plt.show()
```
結果：
<img width="700" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668082/84dd9d73-4a01-3ec5-7313-4b80058e5eb9.png">
Streamlitを用いたWebアプリ制作入門

こんにちは。[東北TECH道場 郡山道場](http://koriyama.tohokutechdojo.org/)でスタッフをしています。今年度（第7期）は [Google Colaboratory](https://qiita.com/Aster_ideatech/items/3ae6ea5e5a011ebcd0b7)を使用して、Python の基礎を学ぼうということで月に1回活動しています。そこで、Webアプリケーションを作成するためのフレームワーク「Streamlit」を学習するための資料をGithubに公開しました。「Open In Colab」ボタンを押すと Google Colaboratory で開いて実行することができます。「Streamlit」を勉強してみたいという方がいましたらご利用ください。

# 「Streamlitの基礎」
https://github.com/tomato-sugar/python_lesson/tree/main/streamlit_basic


# 講座の内容
##  streamlit_basic1.ipynb
- Webフレームワークとは
- Streamlitとは
- Streamlitの実行手順
- いろいろな形式で文字列を表示
- 画像の表示
- マークダウンの表示

## streamlit__basic2.ipynb
- ウィジットの紹介
- インタラクティブにウィジットを使う
- レイアウトを整える

## streamlit__basic3.ipynb
- Pandasで表を作成
- Streamlitで表を表示
- グラフの表示
- 地図の表示

## streamlit__basic0.ipynb
この講座では、localtunnelという、ローカルで実行中のアプリを外部公開するサービスを使用する予定でした。ところが、当時、このlocaltunnelが使えなくなっており、公式サイトにもアクセスできないようになっていました。そのため、急遽、「ngrok（https://ngrok.com ）」を使用する手順を書きました。なお、2022.10.19現在は、localtunnelは復旧しています。
MYSQLでカーソルを使ってjson_arrayの各要素を別テーブルの各カラムにループでINSERTする。

### 環境
MYSQL8.0.30とMYSQL Workbench8.0を使いコードを書きました。

### 作業内容
テーブル（table_1）にjson型のカラム（json_col)があり各レコードの各要素を別テーブル（table_2）のカラム（col_1~3）ごとに分けて挿入する為のプロシージャー（insert_json_porc）を作成して、実行する。

table_1
![rapture_20221019153710.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529194/0bbf9cbc-3afb-c935-49a6-ec5745fc18fd.jpeg)
table_2（ﾃﾞｰﾀ挿入前）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529194/bea92ada-5848-00fe-019b-8f346c7ef537.png)
完成形 table_2（ﾃﾞｰﾀ挿入後）
![rapture_20221019154947.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529194/04b321b4-a38f-da0a-f601-b7135e93b380.jpeg)


### プロシージャー（insert_json_porc）作成コード

```mysql:mysql_code
DROP PROCEDURE IF EXISTS insert_json_porc;
DELIMITER //
CREATE PROCEDURE insert_json_porc()
BEGIN
	-- 変数を宣言(カーソル定義前に行う必要あり) 
	-- カーソルから読み出した値を格納する変数を宣言
    DECLARE col_1_data VARCHAR(10);
    DECLARE col_2_data VARCHAR(10);
    DECLARE col_3_data VARCHAR(10);

	-- カーソルがデータセットの最後に達したか判定するための変数を宣言
	DECLARE done INT DEFAULT FALSE;
    
	-- カーソルを定義
	DECLARE myCursor CURSOR FOR
		select json_col ->> '$[0]', json_col ->> '$[1]', json_col ->> '$[2]'
		from table_1;
	
    -- カーソルがデータセットの最後に達したときの動作を制御
	DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

	-- カーソルをオープン
	OPEN myCursor;
    
	-- ループで1行ずつ処理
	read_loop: LOOP
		-- カーソルから1行読み出し
		FETCH myCursor INTO col_1_data, col_2_data, col_3_data;
        
        -- カーソルからの読み出しが最後に達していればループを抜ける
		IF done THEN
			LEAVE read_loop;
		END IF;
        
		-- 別テーブルにインサート
        INSERT INTO table_2(col_1, col_2, col_3) VALUES( col_1_data, col_2_data, col_3_data);
	END LOOP;
    
    -- カーソルを閉じる
	CLOSE myCursor;   
END //

DELIMITER ;
```
### プロシージャー（insert_json_proc)を実行します。
```mysql:mysql_code
call insert_json_porc();
select * from table_2;
```
完成形 tabel_2（挿入後）
![rapture_20221019154947.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529194/04b321b4-a38f-da0a-f601-b7135e93b380.jpeg)
### 以上で完成です。
### 免責事項
コンテンツや情報において、必ずしも正確性を保証するものではありません。また合法性や安全性なども保証しません。
掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。
GASでスプレッドシートに自動採番を追加する

# 概要
[バックエンド開発1](https://qiita.com/minimumskills/items/66514e324bcf6f4729eb)

バックエンド開発で作成したGASに、自動採番を追加する方法です。
uuidを追加する方法と通し番号を追加する方法を記述します。

# uuid追加
uuidを振る方法です。
> *8e96cb4c-9177-4ffe-83c9-53f4ccb53ba2* といった形式のidが振られます。

uuidは`Utilities.getUuid()`で取得できます。

```diff_javascript:コード.gs

const doPost = (e) => {

    //値の受取り
    const name = e.parameter.name ? e.parameter.name : "";
    const email = e.parameter.email ? e.parameter.email : "";
    const body = e.parameter.body ? e.parameter.body : "";

    //エラー処理
    const email_exp = /^[a-z0-9.]+@[a-z0-9.]+\.[a-z]+$/;
    const body_exp = /^.{1,10}$/;
 
    //問題があればエラーを返す（なければ処理を継続）
    if(name == ""|| !email_exp.test(email) || !body_exp.test(body)){
      return ContentService.createTextOutput(JSON.stringify({ message: "validation error!" }));
    }

+    //UUID取得
+  const uuid = Utilities.getUuid();

    //スプレッドシートの準備
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName("シート1");

    //シートの一番下の行に追加
-   sheet.appendRow([name, email, body, new Date()]);
+   sheet.appendRow([uuid,name, email, body, new Date()]);

    //応答
    return ContentService.createTextOutput(JSON.stringify({ message: "success!" }));
}
```

# 通し番号
1から通し番号を振る方法です。
追加する前の値を確認して、+1した値を追加していくという方法です。
最初は数値がないので、三項演算子を使って一つ前の値が数字でなければ1を入れるというロジックにしています。

```diff_javascript:コード.gs
    const doPost = (e) => {

        //値の受取り
        const name = e.parameter.name ? e.parameter.name : "";
        const email = e.parameter.email ? e.parameter.email : "";
        const body = e.parameter.body ? e.parameter.body : "";

        //エラー処理
        const email_exp = /^[a-z0-9.]+@[a-z0-9.]+\.[a-z]+$/;
        const body_exp = /^.{1,10}$/;
        
+       //id判定
+       const num = /^[0-9]+$/;
      
        //問題があればエラーを返す（なければ処理を継続）
        if(name == ""|| !email_exp.test(email) || !body_exp.test(body)){
          return ContentService.createTextOutput(JSON.stringify({ message: "validation error!" }));
        }

        //スプレッドシートの準備
        const ss = SpreadsheetApp.getActiveSpreadsheet();
        const sheet = ss.getSheetByName("シート1");

        //row
+       const lastCell = sheet.getRange(sheet.getLastRow(), 1)
+       const value = lastCell.getValue()
+       id = num.test(value) ?  value+1 : 1;

        //シートの一番下の行に追加
-       sheet.appendRow([name, email, body, new Date()]);
+       sheet.appendRow([id,name, email, body, new Date()]);

        //応答
        return ContentService.createTextOutput(JSON.stringify({ message: "success!" }));
    }
```

```javascript:コピペ用
const lastCell = sheet.getRange(sheet.getLastRow(), 1)
const value = lastCell.getValue()
id = num.test(value) ?  value+1 : 1;
```