OmniAICreator/Qiita-1.07M huggingface dataset:
CRUD図の基礎と活用法

# 1. はじめに
CRUD(クラッド)図に触れる機会があり、調べた内容を記事としてまとめます。

# 2. CRUD図とは
CRUD図とは、システムの各機能がどのテーブルに対してどの操作を行うかを整理したものである。操作の種類にはデータの生成(CREATE)、読み取り（READ）、更新（UPDATE）、および削除（DELETE）が含まれます。

|  | ユーザ作成 | 注文票作成 | 在庫棚卸し | 商品検索 |
| ------------ | ------------- | ------------| ------------- | ------------- |
| 顧客テーブル     | C | R |  |  |
| 商品テーブル         |  | RU | UD | R |
| 注文テーブル         |  | C |  |  |

# 3. CRUD分析
CRUD図は主にCRUD分析をするために用いられます。CRUD分析の目的は以下のとおりです。

## 3.1 データの操作の整理と可視化
各データ操作(CRUD)がどのデータに対して行われているのかを一覧化することで、システムの機能を理解しやすくするため。
## 3.2 データのライフサイクルの理解
データがどのように生成され、利用や更新を経て、最終的には削除されるかといったデータのライフサイクルを理解するため。
## 3.3 障害時のリカバリー戦略
システムが障害に見舞われた際に、データを元の状態に戻すためのリカバリー戦略を構築するため。

# 4. おわりに
CRUD図について理解を深めていただけたら幸いです。
【ハンズオン・セミナー】STM32マイコンとGUI設計ツールTouchGFXで簡単にグラフィック設計を体験！(2023年12月13日）を受講したよ

__STマイクロエレクトロニクス__ の __ハンズオンセミナー　【ハンズオン・セミナー】STM32マイコンとGUI設計ツールTouchGFXで簡単にグラフィック設計を体験！__ を受講しました。
![IMG_5678.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/794c6d54-d051-651f-9c40-00370edea669.jpeg)
2019年のセミナーに続いて2回目の受講となります。
STM32マイコン体験セミナー（グラフィック描画編）TouchGFX Designer 2019年5月13日実施

https://qiita.com/usashirou/items/b51ec6f8a061290d153c
# 2019年と2023年での違い
前回と異なりIDEがIARから __STM32CubeIDE__ になっており、コード制限もなくなりました。
![IMG_5680.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/57bd2609-5ae1-aa65-4b1f-b96e3319f79c.jpeg)
ハンズオン資料もしっかりしており、ボリュームも増えており、面食らう位の内容です。
# 使用デバイス
デバイスは __STM32H735G-DK__ です。
__STM32H735G-DK__ は __STM32H7__ に __4.3インチ　480×272ピクセル__ の __液晶ディスプレイ__ を搭載しています。
搭載マイコンの __STM32H735IG__ は周波数 __550MHz__ の __H7マイコン__ かつ __1MBの内蔵フラッシュにより、多機能なプログラムも動作可能ですし、リッチなUIも可能です。
![IMG_5683.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/c71440d0-c5e4-66be-cbb8-fde306121c58.jpeg)

https://www.st.com/ja/evaluation-tools/stm32h735g-dk.html
# ハンズオンの様子
セミナー内容が以前に比べて1.5倍くらいのボリュームになっていました。
このため、駆け足になった部分がありました。（この辺はSTのセミナーの恒例ですね）
どうせならば、セミナーで途中までを行い、残りを自習とした方が良い気がしました。
その分の説明を詳しくやって頂いた方が私は良いと思います。
特に写経となる部分は限られた時間を浪費するため
![IMG_5684.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/7ae72cea-3451-73a9-5bd8-cec2097a6838.jpeg)
# 感想
久々に使用したTouchGFXですが、STマイクロの純正ハードウェアでは十分に機能するデバイスだけに機能をしっかり理解できるかが大きな違いになると思いました。
TouchGFXドキュメントが参考になると思います。
## TouchGFXドキュメント

https://support.touchgfx.com/ja/docs/introduction/welcome
## STM32CubeIDEでTouchGFXを使おう1
https://qiita.com/usashirou/items/4fdbeec5c7c154872b19
M5StackをLoRaのルーターにしてみる

この記事は、2023年の [M5Stack アドベントカレンダー](https://qiita.com/advent-calendar/2023/m5stack) の 17日目の記事です。

# はじめに

前回は、SenseCAP T1000 が送っているデータを M5Stack で覗いてみました。今回は、受信したデータを LoRa のゲートウェイのように AWS IoT Core for Wireless に転送してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/c3c448db-76e7-cc23-a6d3-85961ccaa59a.png)

# 処理概要

デバイスは Grove-Wio-E5 を使用します。このデバイスでは周波数を指定して1つのチャンネルを受信することができます。LoRaWAN の仕様では、ゲートウェイは2つのチャンネルでデバイスから送られる電波を待機するようになっており、SenseCAP T1000 はゲートウェイ向けに2つのチャンネルでランダムに（実際にはチャンネルの空きを判断しながら）送信します。今回はそのうちの片方を決め打ちで受信するようにします。

受信したデータは LoRaWAN の仕様で暗号化されています。LoRaWAN のゲートウェイでは、通常暗号化されたペイロードはそのままサーバーに転送します。今回のプログラムでは受信確認のため、前回同様 util-lorawan-packets を使って復号化してログを表示しています。

AWS IoT Core for Wireless は、LoRa の [BasicStation](https://github.com/lorabasics/basicstation) と互換性のあるデータを送受信します。認証はサーバー認証、クライアント認証の両方を使用し、ヘッダーに余計なヘッダーが指定されていると認証が切断されます。WebSocket 通信は [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) を使用しましたが、上記対応を行うため一部修正しています。

# ソースコード

ソースコードは [こちら](https://github.com/fukuen/M5Stack-LoRa-Router) をご覧ください。

以下の値を設定します。

- WiFi SSID/パスワード
- 利用するデバイス（SenseCAP T1000など）のアドレス
- キー（nwkSKey/appSKey）
- ルーター（本機）のアドレス
- AWS IoT Core for Wirelessのエンドポイント
- AWS IoT Core for Wirelessの証明書3つ（サーバー、クライアント）

# 実行例

M5Stack から送信された情報を AWS IoT Core for Wireless で受信することができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/aa09b495-59ba-beb0-b26f-6968a36d9e2b.png)

# さいごに

広いエリアをカバーするゲートウェイとしては使えないものの、M5Stack がゲートウェイの動作の検証用に活用できました。

電波は国民の財産ですが、ほぼすべての帯域は既得権者が保有していて、アマチュアが遠距離通信できる帯域は限られています。デジタル化も進んでいて気軽に触れられなくなってしまっています。かつて同じ周波数帯にパーソナル無線という通信がありましたが、今回同じ周波数帯ということで懐かしさも感じました。
Svelteに「Carbon Components Svelte」導入

# インストール
```shell
npm i -D carbon-components-svelte
```

# CSS読み込み
```src\routes\styles.css
+ @import "carbon-components-svelte/css/white.css";
```

# 使い方

```html
// ボタン
<script>
  import { Button } from "carbon-components-svelte";
</script>

<Button>button</Button>
```

# 参考
<a href="https://carbon-components-svelte.onrender.com/components/Slider" >
【MLX】 Macbook M1 Pro 16GB で MLX の Whisper と Mistral を動かしてみたw


# はじめに

今朝届いた中島聡さんのメールマガジンで MLX のことを知って興味を持ったので、早速触ってみました。
限られたリソースでの検証となりますが、このスペックで使ってる方も多いと思うので、参考になればと思い記事にしました。


MLXとは・・・（以下、中島聡さんのメールマガジンより引用）

> MLXは、タイトルにある通り、Numpy(Python上で行列計算などを行うためのライブラリ)に似たライブラリで、ニューラルネットワークの学習や推論をAppleのシリコン上で効率良く計算するために作られたライブラリです。人工知能計算用にNumpyの上に作られたPyTorchからもアイデアを拝借しており、MacBookやiPhone上で人工知能のアプリケーションを開発する開発者・研究者向けにオープンソースの形で提供されています。

> オープンソース化されたMLXには、llama, mistral, mixtral, whisper, stable diffusion などをMLX上で実行するサンプルコードが提供されており、アップルシリコンを搭載したMacBook上で簡単に動かすことが出来ます。私も試しに、mistralとstable diffusionを動かしてみましたが、とても快適に動きます。


#### 関連リンク
以下のリンクで参考に、検証をしました。

* Whisperについて
    * [Whisperの概要と使用方法](https://note.com/nyosubro/n/nb9b3f1c4505b)
* Mistralについて
    * [Mistralの詳細と実装例](https://qiita.com/huiping192/items/f41e365bce5209e3af7d)


## 検証環境

この実験は以下の環境で実施

- Macbook Pro
- CPU: M1 Pro
- メモリ: 16GB

## 検証内容

### Whisperの実験

Whisperは、音声の書き起こしに特化した深層学習モデルです。
この検証では、12秒の音声データの書き起こしを試みました。実験の詳細は以下の通りです。

- 使用したコマンド:
    ```sh
    % python script.py --model large
    ```
- 実行結果:
    ```
    100%|█████████████████████████████████████| 2.88G/2.88G [04:02<00:00, 12.7MiB/s]
    さて、早くに創業再開された尼崎の工場なんですけれども、その後の防災対策ということでは、どのようなことを勧められたんでしょうか。
    経過時間: 278.5 秒
    ```
- 感想: 
    12秒の音声に対して、4~5分かかってしまいました。
    1時間の音声だと、完了するまでに約1日かかってしまうのでしょうか。
    精度については問題なさそうなので、比較的短い音声の書き起こしであれば、放置してる間に完了するという使い方はできそうです。

### Mistralの実験
Mistralは、自然言語生成に特化した深層学習モデルです。
この実験では、特定のプロンプトからテキストを生成する能力をテストしました。
実験の詳細は以下の通りです。

- 使用したコマンド:（200トークンで出力するように指示）
    ```sh
    python mistral.py --prompt "It is a truth universally acknowledged," --temp 0 -m 200
    ```
- 実行結果:
    ```
    It is a truth universally acknowledged,　that a single man in possession of a good fortune, must be in want of a wife.

    So begins Pride and Prejudice, one of the most famous novels in the English language.

    The story of the Bennet family, five unmarried daughters and their mother, is a classic tale of love and misunderstanding.

    The novel was first published in 1813, and has been adapted for film and television many times.

    The most recent adaptation, Pride and Prejudice and Zombies, is a mash-up of the original novel and the zombie genre.

    The novel has been translated into many languages, and is still popular today.

    ## What
    ```
- 経過時間: 3時間30分（66%で断念しました。）
- 感想: こちらは使い物にならないことが判明しました。（16GBのメモリでは厳しい）

## まとめ

今回の検証環境だと使い物にならないことがわかりました！！w
生成AIをローカルで使用する場合には、チップ性能よりメモリ量が重要ということを身をもって体験できましたww

次にMacを買うときはメモリ盛り盛りで購入しよう・・・

以下、中島聡さんのメールマガジンより引用

> 参考までに書いておくと、今からMacBookを開発用に購入するのであれば、チップの性能よりも、メモリ量を重視してマシンを選ぶことをお勧めします。最低限でも64GBはあるマシンを購入すべきだと思います。


中島聡さんのメールマガジンに興味がある方はこちらから↓↓↓（PRではなく、5年購読してるファンとしておすすめです。）

https://www.mag2.com/m/0001323030



## 参考
* [Whisperの概要と使用方法](https://note.com/nyosubro/n/nb9b3f1c4505b)
* [Mistralの詳細と実装例](https://qiita.com/huiping192/items/f41e365bce5209e3af7d)

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/
PowerQuery: Sharepoint パスからの Site/Folder URL 認識

# 背景

Sharepoint においた Excel を Power BI で処理しようとした。
パスを設定みたいにして扱えるようにしたかった。

が・・パスの取得方法は以下三種類ある
- リンクのコピー
    - その名の通り、「リンクのコピー」を利用
- アドレスからのコピー
    - アドレスバーから直接コピー
- パスのコピー
    - 「詳細」で、パスの横にあるアイコンからコピー<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/e11fa9cd-21cf-77f3-f499-0be5c633c5d7.png)

で、いずれからでも、以下を取得出来ないか？・・と試した記録

- Site URL
- Folder URL

# 結論

リンクのコピーは、Folder URL を取得出来なかった（Rest API たたけば取れそうだけど）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/6ee701d7-c628-9cb0-252f-d1194f4c97e8.png)

```perl: Site/Folder URL 取得例
let
    ソース = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("rZHPTsJAEMbfpUlvhKWLrZSEkBogihA0VpAgh6UsdJOW1s4C9mh78mjiE3g0Rg+cTHybfRG3VeO/gyHx9OXbmf3ml5nRSHE5D6GKELjMJ0VwSUTDgC140Ql8VJ1VESCbAm8FkU2JD6gJen8wMIeAeXfu9jtn7QvO2t29OPbIatYLhvbRvta2eoflVXxs1WnNqsQaJUpBEem9SDcieRJXjyLZiPRWpC/KuPAHAzBOfzCcZB1TFZcagbP06YIDkjVZsDzvgFMfigTCy/qCrm0SzSnvMOCnkVdTcStPk/o1T9rfiefLUgkbK0bXIeHuVl/lY44j9QNIxc0M6S2UTbeO84J11vcJJTN2HWxUJrqj4oZj6hUpOxrOnKnNcjcpG7qpm7hs5OtP7kR6LdIHkTz///rfAfMz32QTvt14/Ao=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Site URL" = _t, コピー種類 = _t]),
    変更された型 = Table.TransformColumnTypes(ソース,{{"Site URL", type text}, {"コピー種類", type text}}),
    DecodedURL = Table.AddColumn(変更された型, "DecodedURL", each Uri.Parts("https://contso.com?url_1stParam=" & [Site URL])[Query][url_1stParam], Text.Type),
    uri = Table.AddColumn(DecodedURL, "uri", each (Uri.Parts([Site URL])), Text.Type),
    Query = Table.AddColumn(uri, "Query", each [uri][Query]),
    id = Table.AddColumn(Query, "id", each ([uri][Query][id]?), Text.Type),
    viewid = Table.AddColumn(id, "viewid", each ([uri][Query][viewid]?), Text.Type),
    SiteURL = Table.AddColumn(viewid, "SiteURL", each ([uri][Scheme] & "://" & [uri][Host] & "/sites/" & 
 Text.BetweenDelimiters([DecodedURL], (if Text.Contains([DecodedURL], "/sites/") then "/sites/" else "/:f:/s/"), "/") 
 & "/"), Text.Type),
    FolderURL = Table.AddColumn(SiteURL, "FolderURL", each ([uri][Scheme] & "://" & [uri][Host] & (if [id] is null then "/sites/" & (if Text.Contains([DecodedURL], "?") then Text.BetweenDelimiters([DecodedURL], "/sites/", "?") else Text.AfterDelimiter([DecodedURL], "/sites/")) else [id]) & "/"), Text.Type)
in
    FolderURL
```

## 説明

- Decode をまずはする
- Uri.Parts() で取れるなら取る
- ダメな場合は、頑張ってテキスト抽出・・

必要な関数は以下辺り

https://learn.microsoft.com/ja-jp/powerquery-m/uri-parts

https://qiita.com/baku2san/items/26d21df0f8a7fd5f5540

https://learn.microsoft.com/ja-jp/powerquery-m/text-betweendelimiters

https://learn.microsoft.com/ja-jp/powerquery-m/text-afterdelimiter

# あとがき

まぁ、Site URL だけは取れたから良しとする

正規表現使えばもっと簡単に出来た・・？

正直・・こんなことするだけ無駄なんじゃないのか、という思いの方が強い・・ :sweat:
Azure ADをIdPにしてGppgle Workspaceにログインしてみた

# はじめに

この記事は、Qiita Advent Calendar 18日目の記事です。
以下の記事より、すべての記事をご覧になれます。

https://qiita.com/0_terarin_0/items/0f367210e2465f7772c9

# 筆者について

- 現在高校２年生
- 水泳部マネージャー
- 生徒会長
- とある団体の代表

# 本編

本日は、Google WorkspaceにAzure ADをIdPとして利用してログインする設定を試みたいと思います。（備忘録）

## ちなみにドメインが…。

僕の環境ではGoogle WorkspaceとAzureで利用しているドメインが違います。Google Workspaceで利用しているのを```example.com```とすると、Azure ADでは```ad.example.com```を利用していますので、この変換処理も必要です。

## Azure AD　設定

まず、Entra IDのところからアプリケーションの登録を行います。新しいアプリケーションから「G Suite」と検索すると、今回利用するものが出てきます。

![Screenshot 2023-12-19 at 23.10.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/2f640d8b-97f8-fcdb-e92d-217f4b0f5b41.png)

![Screenshot 2023-12-19 at 23.11.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/0ce32cf0-1a59-f844-2dee-99b196bb9a24.png)

![Screenshot 2023-12-19 at 23.11.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/cc4c97ba-cf70-f520-3ed8-3c3e171c35c9.png)

![Screenshot 2023-12-19 at 23.12.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/468e98e7-4446-1df4-b5ff-a5e3f2ce82b2.png)

![Screenshot 2023-12-19 at 23.12.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/a5d439a3-4a54-a6aa-db38-1365dda8af93.png)

ここでサインオンURLについてなのですが、基本的に ```https://www.google.com/a/<domain>/ServiceLogin?continue=<Page>``` という形になります。他とえば、うちの場合は ```https://www.google.com/a/terarin-iw.net/ServiceLogin?continue=https://workspace.google.com/dashboard/``` という形になります。（ちなみに```https://workspace.google.com/dashboard/```のページはGoogle Workspaceのダッシュボードです。

![Screenshot 2023-12-19 at 23.12.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/c44be384-0ce5-2c94-b413-2c3eaa1bd28c.png)

ここでドメイン変換です。属性マッピングのところで変換を設定します。例えば僕のこの設定は、「```ad.terarin-iw.net```を```terarin-iw.net```に置き換える」というようなことになっています。来れなかったらエラーが出るのでお気をつけtください。

![Screenshot 2023-12-19 at 23.14.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/37912650-e3b8-f548-bef1-90e0080c4098.png)

赤枠内のURLは押さえておきましょう！

![Screenshot 2023-12-19 at 23.14.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/04202ac3-4b26-9e20-2b27-bb082d202b6c.png)

ちなみに、記載漏れなのですが、証明書はBase64のものをダウンロードしておいておきます。

## Google Workspace 設定

画像のように設定します。なお、パスワードの変更URLは```https://account.activedirectory.windowsazure.com/changepassword.aspx ```などを設定することをお勧めします。　また、証明書のアップロードもお忘れなく！

![Screenshot 2023-12-19 at 23.06.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/c1613bf0-8fbe-464d-a0f5-a3362b2f2069.png)

以上です！テストしてみてください。

# 最後に

今回は軽く、昨日の引き続き備忘録がてらAzure ADを利用したGoogle Workspace のSSOでのログインについて記述しました。ということで、今後のアドカレ 23 の記事をお楽しみにしてください。

https://qiita.com/0_terarin_0/items/0f367210e2465f7772c9
Pythonで上限を気にせずに再帰

## はじめに
Pythonでは、再帰呼び出し回数の上限が決められています。私の環境だと、1000回が上限となっていました。
```python
>>> import sys
>>> sys.getrecursionlimit()
1000
```
こちらの上限を超えた再帰呼び出しを行いたい場合に使える方法を紹介します。

## 方法
こちらの再帰関数を例に説明します。
```python
def factorial(n):
    if n < 2:
        return 1
    return factorial(n - 1) * n
```
まず、再帰呼び出し箇所の周りを、`(yield ～)`で囲みます。上記コードの場合、`factorial(n - 1)`を`(yield factorial(n - 1))`に変更します。
```python
def factorial(n):
    if n < 2:
        return 1
    return (yield factorial(n - 1)) * n
```
あとは、以下の関数を用意して、`trampoline(factorial(10))`のように呼び出すだけです。
```python
def trampoline(f):
    stack, ret = [f], None
    while stack:
        try:
            stack.append(stack[-1].send(ret))
            ret = None
        except StopIteration as e:
            stack.pop()
            ret = e.value
    return ret
```

## 原理
トランポリンとジェネレータの組み合わせで実装しています。

トランポリンについては、「トランポリン 再帰」あたりで検索すれば情報が出てきます。関数を直接呼び出すのではなく、いったん呼び出し元に返してから呼び出すことで、再帰呼び出しの回数を増やさないようにするテクニックです。

ジェネレータは、通常`yield`で順番に値を生成するのに使いますが、実は外部から`send`で値を渡すと`yield`の戻り値になる、という機能があります。

https://docs.python.org/ja/3/reference/expressions.html#generator.send

あとは、再帰呼び出しするはずだった関数を`yield`で呼び出し元に返してから呼び出し、結果が求まったら`send`で続きの計算を行うようにしてあげれば、再帰呼び出しの回数を増やさずに再帰を行うことができます。

## 注意
もとからジェネレータとなっている関数には使用できません。
例外には対応していません。

## 他の方法との比較
### `sys.setrecursionlimit`を使う
再帰呼び出しの上限を増やします。こちらで事足りる場合はこちらを使うべきです。
ただし、プラットフォームがサポートしている範囲を超えた値を設定するとクラッシュするので、別の方法を検討する必要があります。

https://docs.python.org/ja/3/library/sys.html#sys.setrecursionlimit

### `sys.setrecursionlimit`と`resource.setrlimit`を使う
`resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))`とすることで、再帰呼び出しの上限をさらに増やせます。ただし環境によっては使用できない場合があります。

https://docs.python.org/ja/3/library/resource.html#resource.RLIMIT_STACK

### 再帰をループに書き換える
再帰関数は、ループに書き換えることができます。ただ、見た目が大きく変わるので、後から修正するのが困難になります。汎用の方法については、昔以下のような記事を書いています。

https://qiita.com/actorbug/items/f08e330de8c3361ce528

## 最後に
昔、AtCoderでこちらの問題に引っ掛かっている人を何人か見かけたので、記事にしてみました。一応、AtCoderで使えることは確認しています。余計なことをしているので遅くなりそうですが。

実は、同じことをするライブラリがすでに公開されています。実際に使うなら、こちらの方が良いでしょう。こちらは例外にも対応していますし、末尾再帰を最適化することも可能です。

https://pypi.org/project/trampoline/
【SAM】APIGatewayのオーソライザーによる認証をAPIごとに個別設定する

# やりたいこと
APIGatewayでオーソライザーを設定しているが、一部のAPIでは認証が不要なのでオーソライザーを通さずに処理を実行できるようにしたい。

# 最終的なコード

```yaml
Api:
    Type: AWS::Serverless::Api
    Properties:
    Auth:
        DefaultAuthorizer: CognitoAuthorizer        
        Authorizers:
          CognitoAuthorizer:            
            UserPoolArn:
              Fn::GetAtt:
                - CognitoUserPool
                - Arn
            Identity:
              Header: Authorization
（中略）
SigninFunction:
    Type: AWS::Serverless::Function
      Events:
        SigninApi:
          Type: Api
          Properties:
            Path: /signin
            Method: post
            RestApiId:
              Ref: Api
            Auth:
              Authorizer: NONE # これで無効化
              OverrideApiAuth: true # これを指定しないと上書きされない
```
# 解決までに試したこと

まずはオーソライザーを下記のように設定した
```yaml
Api:
    Type: AWS::Serverless::Api
    Properties:
    Auth:
        DefaultAuthorizer: CognitoAuthorizer        
        Authorizers:
          CognitoAuthorizer:            
            UserPoolArn:
              Fn::GetAtt:
                - CognitoUserPool
                - Arn
            Identity:
              Header: Authorization
```
ここでDefaultAuthorizerを指定することで全てのAPIでこのオーソライザーで認証チェックが実施されるようになる。

このままだとサインインのような認証が必要のないAPIでも認証チェックが実施されてしまうので下記のように設定をした。
```yaml
SigninFunction:
    Type: AWS::Serverless::Function
      Events:
        SigninApi:
          Type: Api
          Properties:
            Path: /signin
            Method: post
            RestApiId:
              Ref: Api
            Auth:
              Authorizer: NONE
```
Authの部分でAuthorizer: NONEとすることでデフォルトのオーソライザーを無効化して、認証チェックなしでAPIが実行されるはず・・・と思ったが、これだと認証チェックが実行されてしまい、401が返ってしまった。

![スクリーンショット 2023-12-19 23.13.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476743/e7956248-27dc-9693-38fa-144436ac7f5c.png)

改めてリファレンスを調べると、下記のような記述を発見

> AWS::Serverless::Api リソースの DefinitionBody プロパティを使用して API を記述する場合は、Authorizer で OverrideApiAuth を使用してグローバルオーソライザーをオーバーライドする必要があります。詳細については、「OverrideApiAuth」を参照してください。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-property-function-apifunctionauth.html#sam-property-function-apifunctionauth-properties

ということで、下記のようにOverrideApiAuthをtrueに設定した。

```yaml
SigninFunction:
    Type: AWS::Serverless::Function
      Events:
        SigninApi:
          Type: Api
          Properties:
            Path: /signin
            Method: post
            RestApiId:
              Ref: Api
            Auth:
              Authorizer: NONE
              OverrideApiAuth: true
```

これで無事に認証無しで実行できるようになった

![スクリーンショット 2023-12-19 23.18.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476743/94924133-d463-55fb-73d8-bd9a3ddefc85.png)
ignition gazebo + Rviz2の開発環境構築

# 概要
この記事は、[学ロボAdvent Calendar 2023](https://adventar.org/calendars/8926)の20日目の記事です！
ROS2を使う上で重要な可視化ツールのgazeboとrviz2について、その基本的な使い方を実際の実装を追いながら説明します。
今回扱うignition gazeboには少々厄介なバージョンの問題がありますが、それについて調べて分かったことなども記事内で説明しています。
自作ロボットのCADファイルを取り込んでgazebo上に表示するところから、slam toolboxとnavigation2を用いてマッピングと自律走行をシミュレーター上で行うところまでを解説します。
分量がかなり多くなってしまったので複数の記事に分割して書き、この記事はそれらの集積場としたいと思います。
また、[私のgithub](https://github.com/OsawaKousei/2DRoboPrac_ws)にてステップごとの実装を公開しているので、そちらも併せてご覧ください。
また、記事の内容に誤りや不備等ございましたら、ご指摘いただけると幸いです。


# 動作環境・バージョン
### 動作環境
Ubuntu22.04　：デュアルブートした実機PC
VScode　：コーディングとターミナルの操作は基本的にVScodeで行った

### バージョン
ROS2 : humble　：幣チームで一般的に用いられているROS2のバージョン
gazebo : ignition fortless　：ROS2公式がhumbleとの連携相手として推奨するバージョン

# コンテンツ
## 自作ロボットのCADファイルを読み込んでgazeboに表示する
自作ロボットのCADファイルを生成し、blenderを用いてdaeファイルを作成します。それらをgazeboで読み込んで、自作ロボットをgazeboに表示しましょう。
また、gazeboの最低限の使い方―特にworldの作り方、も説明します。
[自作ロボットのCADファイルを読み込んでgazeboに表示する](https://qiita.com/N622/items/c875035b387105ae4d95)
## gazeboとROS2を連携してロボットを操作する
前回作成した自作ロボットに動力を付けた上で、ROS2から操作できるようにしていきましょう。gazeboとROS2の連携はメッセージのブリッジがやや面倒ですが、比較的効率の良いyamlファイルを用いたやり方を紹介します。
キー入力の検出は既存のパッケージを使うので、それらをプロジェクトで使用する方法も説明します。
さらに、gazeboとROS2を一つのコマンドで起動できるようにするlaunchファイルの書き方も紹介します。
[gazeboとROS2を連携してロボットを操作する](https://qiita.com/N622/items/9d89f77d85d9da0af29e)
## Rvizを使用してロボットの状態を可視化する(gazebo)
gazeboと並ぶ可視化ツールのRvizを使ってみましょう。前回使用したロボットにLidarを付けて、取得データをRvizで表示してみます。
さらに、ロボットのtfやodometryといった基本情報をgazeboから送信して、Rvizに表示する方法を説明します。
[ Rvizを使用してロボットの状態を可視化する(gazebo)](https://qiita.com/N622/items/f1c5dffa37e095116119)
## slam toolboxとnavigation2を用いた自律走行を行う(gazebo)
slam toolboxとnavigation2を用いて、gazebo上でSLAM(地図生成と自己位置推定)とナビゲーション(経路生成と経路追従)を行ってみましょう。
slam toolboxとnavigation2をlaunchファイルから起動し、予め用意したパラメータを読み込ませてから起動する方法も説明します。
[slam toolboxとnavigation2を用いた自律走行を行う(gazebo)](https://qiita.com/N622/items/9f372fcd4568342ad8f0)
1000円以下のマイコンだけで物体形状の学習と推論を行う

## はじめに

まずは下記の動画をどうぞ

>1000円以下のマイコンだけで物体形状の学習、推論が動いた。
>
>緑色のは8×8ピクセルのToFセンサVL53L7CX、マイコンはSeeed XIAO RP2040、学習アルゴリズムはSEFR(multi-class)、学習時間は約12ms。
>前半で、無、👊、✌️、✋を学習して、後半は推論。推論時間は1ms以下ですが、ToFセンサ読み出しに時間がかかっています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">1000円以下のマイコンだけで物体形状の学習、推論が動いた。<br><br>緑色のは8×8ピクセルのToFセンサVL53L7CX、マイコンはSeeed XIAO RP2040、学習アルゴリズムはSEFR(multi-class)、学習時間は約12ms。… <a href="https://t.co/ccJQ7fDE0A">pic.twitter.com/ccJQ7fDE0A</a></p>&mdash; ミクミンP/Kazuhiro Sasao (@ksasao) <a href="https://twitter.com/ksasao/status/1737090723437322425?ref_src=twsrc%5Etfw">December 19, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

2023年現在、1000円以下の低価格帯マイコンモジュールは動作周波数100MHz以上、Flash 1MB以上、SRAM 100KB以上などが普通になってきています。また、マイコン向けの低消費電力、省メモリな機械学習アルゴリズムも開発がすすんでいます。これらを利用して物体の学習と推論を行ってみました。
## 構成

### 形状計測
物体形状の計測には、ToFセンサ[VL53L7CX](https://www.pololu.com/product/3418)を利用しています。このセンサは、8x8ピクセルと解像度は低いものの約3.5m程度の距離の深度情報を1mm単位で取得することができます。このセンサは60Hzで動作するはずなのですが[SparkFun_VL53L5CX_Library.h](https://github.com/sparkfun/SparkFun_VL53L5CX_Arduino_Library)を利用したところ、フレームレートは1～2FPS程度でした。計測したデータは、秋月電子通商にて850円(2023年現在)で売られている[Seeed XIAO RP2040](https://akizukidenshi.com/catalog/g/gM-17044/)をつかって取り込みました。

### データセット作成
計測結果は、8x8=64個の数値データとして取得できるので、これにラベルをつけてデータセットを作成します。上の動画の前半では、白、白点滅、赤、赤点滅、緑、緑点滅、青、青点滅のようにLEDの色が変化していきますが、1回点滅するごとに、マイコン上で数値データとID(色)を対応付けていきます。動画では、各色5回ずつ点滅しているので、全部で20組のデータが生成されます。こういったデバイスでは、いかに簡単にアノテーションするかというUI設計も重要です。

### 学習
機械学習アルゴリズムには、[SEFR: A Fast Linear-Time Classifier for Ultra-Low Power Devices](https://arxiv.org/abs/2006.04620)というアルゴリズムを利用しました。これは、リソースが限られたマイコンで機械学習を行うアルゴリズムの一つで、論文によると、データセットによってはXGBoostやSVMなどと同程度のAccuracyで消費電力が非常に少ないことが示されています。今回の場合には、データセット作成直後に64次元×20個のデータに対して学習しています。12ms程度で学習することができました。

なお、多少使いやすく実装したコードを[作成しました](https://gist.github.com/ksasao/0d552ed637f302a0c990d751157abd03)のでご利用ください。以下のような感じで使います。1クラス当たりのデータセット数はクラスごとに異なっても問題ありませんが便宜上定数にしています。

```cpp
#include "SEFR.h"

#define FEATURES 64     // 特徴量の次元数
#define LABELS 4        // 分類したいクラスの数
#define SAMPLES_PER_CLASS 5  // 1クラス当たりのデータセット数
#define DATASET_MAXSIZE (SAMPLES_PER_CLASS*LABELS)  // 全データの最大値

SEFR sefr;

...

void setup(){
   // 初期化
   sefr.setup(FEATURES, LABELS);

   // データセットの準備
   float** X_train = new float*[DATASET_MAXSIZE];
   uint8_t* y_train = new uint8_t[DATASET_MAXSIZE];
 
  ...
   // X_train, y_train に値を設定
  ...
  
   // 学習
   sefr.fit(X_train, y_train, DATASET_MAXSIZE);
   
}

void loop() {

    ...
  
    // センサデータを current に入れて
    // 推論
    uint8_t c = sefr.predict(current);
  
}
```

### 推論
学習が終わると推論モードに移行します。推論は1ms以下となっています。推論結果をLEDの色で表しています。手の動きが変わってからLEDの色が変化するまで多少時間がかかっていますが、これはToFセンサからのデータ読み取りに時間がかかるためです。

## まとめ
マイコンとセンサの組み合わせで、物体形状の学習と推論が簡単にできることをご紹介しました。このようにうまく工夫することで、機械学習対応デバイスが簡単に作れますので是非皆さんも試してみてください。計測結果をif文で分類したくなったら機械学習に頼ってみましょう。
【Go言語】日時の取り扱い

Goでは`time.Time`を使用して日時とタイムゾーンを扱う

### `time.Time`の取得

#### 現在時刻の取得
```go
now := time.Now()
fmt.Println("現在時刻:", now)
```

#### 特定の日時の取得
```go
tz, _ := time.LoadLocation("America/Los_Angeles")
future := time.Date(2015, time.October, 21, 7, 28, 0, 0, tz)
fmt.Println("指定日時:", future)
```

#### タイムゾーンの指定
```go
jst, _ := time.LoadLocation("Asia/Tokyo")
jstTime := time.Date(2021, time.June, 1, 12, 0, 0, 0, jst)
fmt.Println("JST:", jstTime)
```

### `time.Duration`の扱い

#### 時間の加算
```go
fiveMinutes := 5 * time.Minute
tenSeconds := time.Duration(10) * time.Second
now := time.Now()
fmt.Println("5分後:", now.Add(fiveMinutes))
fmt.Println("10秒後:", now.Add(tenSeconds))
```

#### 日時の比較
```go
now := time.Now()
past := time.Date(1955, time.November, 12, 6, 38, 0, 0, time.UTC)
duration := now.Sub(past)
fmt.Println("過去から現在までの差分:", duration)
```

### データ変換

#### 日時フォーマット変換
```go
now := time.Now()
formattedTime := now.Format(time.RFC3339Nano)
fmt.Println("フォーマット変換:", formattedTime)
```

#### 文字列から`time.Time`への変換
```go
timeString := "2021-06-01T12:00:00+09:00"
parsedTime, _ := time.Parse(time.RFC3339, timeString)
fmt.Println("パース結果:", parsedTime)
```

### 翌月を計算するときの注意点
Goで日付の計算を行う際は、時に翌月の計算に注意が必要になる。
`AddDate()`で日付や月、年の加減算を計算できる。

#### 基本的な翌月の計算
`AddDate(0, 1, 0)`で計算できる
```go
jst, _ := time.LoadLocation("Asia/Tokyo")
now := time.Date(2021, 6, 8, 20, 56, 0, 0, jst)
nextMonth := now.AddDate(0, 1, 0)
fmt.Println("翌月:", nextMonth) // 翌月: 2021-07-08 20:56:00 +0900 JST
```

#### 正規化について
日付を計算した結果、存在しない値になった時に「**正規化**」と呼ばれる変換が行われる。
例えば、6/31という値を格納すると、7/1に変換される。
```go
jst, _ := time.LoadLocation("Asia/Tokyo")
endOfMonth := time.Date(2021, 5, 31, 0, 0, 0, 0, jst)
nextMonthEnd := endOfMonth.AddDate(0, 1, 0)
fmt.Println("月末の翌月:", nextMonthEnd) // 月末の翌月: 2021-07-01 00:00:00 +0900 JST
```

#### 月末を考慮しながら翌月を計算する実装例
```go
package main

import (
	"fmt"
	"time"
)

func NextMonth(t time.Time) time.Time {
	year1, month2, day := t.Date()
	first := time.Date(year1, month2, 1, 0, 0, 0, 0, time.UTC)
	year2, month2, _ := first.AddDate(0, 1, 0).Date()
	nextMonthTime := time.Date(year2, month2, day, 0, 0, 0, 0, time.UTC)
	if month2 != nextMonthTime.Month() {
		return first.AddDate(0, 2, -1) // 翌月末
	}
	return nextMonthTime
}

func main() {
	jst, _ := time.LoadLocation("Asia/Tokyo")
	endOfMonth := time.Date(2021, 5, 31, 0, 0, 0, 0, jst)
	nextMonthEnd := NextMonth(endOfMonth)
	fmt.Println("月末の翌月:", nextMonthEnd) // 月末の翌月: 2021-06-30 00:00:00 +0000 UTC
}

```


## 参考
https://www.oreilly.co.jp/books/9784873119694/
SAP Analytics Cloud（SAC）のストーリー移送方法について

# はじめに
SAP Analytics Cloud（以下SAC）のデータソース先としてSAP Datasphere（以下DSP）を導入されるケースは多いと思います。その場合、移送作業ってどうやってすれば良いのか気になるところではないでしょうか。
例えば本番環境と開発環境が分かれている場合、SACとDSPはそれぞれの本番環境、開発環境に接続しているかと思います。
上記の環境の場合、開発環境⇒本番環境に移送する場合、接続情報も含め移送可能でしょうか？
結論から言いますと可能でした。但し何点か注意する箇所があるので、今回はDSPを接続先としているSACストーリーの移送方法をご紹介したいと思います。

（イメージ図）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/25ba8a01-2a8f-ddf0-dd04-0de63ef9be8a.png)

# 前提事項
移送する前に以下の内容を実施しておく必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/e999c68e-81b1-7dcf-6c41-45beada76d2b.png)

＜前提事項１＞
　公開フォルダ上にストーリを作成します。
 　※移送手順に記載しているコンテンツネットワークでのエクスポート時に公開フォルダ配下にないと選択できない為です。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/6b79aa59-c392-ffca-4f86-2a97ee822913.png)

＜前提事項２＞
　DSP接続情報の名前を開発環境と本番環境で一致させる必要があります。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/391b2d96-c217-3121-9347-6a48716a2e8c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/66d77292-70ca-9a46-89cf-342e428a2919.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/8c408e30-51e3-62ce-c66a-2f8654aadbdc.png)


＜前提事項３＞
　DSP側のビュー（分析データセット）の名称を開発環境と本番環境で一致させる必要があります。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/7185ccaa-b3ad-7121-3364-d3672d66d760.png)

# 移送手順
＜エクスポート手順＞
※移送対象の選択にて依存関係のあるものは自動でチェックされます。（基本は外さない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/b8841930-3ee1-4288-2061-3637237b2810.png)
※オブジェクトタイプに「接続」にチェックがある場合は外してください。接続情報が上書きされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/cad9981a-8c9b-8c1a-a1bd-9d3a3a9c39d2.png)



※説明欄についてインポート側の画面で一覧表示されるので、説明欄は詳しく書くことをお勧めします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/28fb5a75-f22a-2e27-c39a-4f6e638f915e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/fb7846fb-0dd0-4030-60ff-8f1483351e2b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/0f041db8-62a3-3b09-2a42-758089258de7.png)
＜インポート手順＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/3f56bb4f-1f17-478a-bc96-b9fb9d60d03d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/c6fe28a3-757b-a2a7-908e-f5265e577412.png)
※エクスポート時点でデータを移送対象にしていない場合、[オブジェクトとデータを上書きする]を選択してもデータは上書きされません。またデータ元がDSPの場合、移送対象とするモデルが無いので更新の際は[オブジェクトとデータを上書きする]を選択で問題ありません。データ元がモデルの場合は必要に応じて設定が必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/dd90bb10-bb0a-cf12-f7fe-6228a69047ca.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705941/b66a4176-8e86-fee4-1bf9-0c4edc908e61.png)
以上で移送完了です。

# おわりに
接続情報を開発環境と本番環境で別々の名称にして区別したくなりがちですが、今回紹介した通り名称が異なると移送できません。後から気づいて名称だけ変更しようとしても接続先は一度、登録すると名前を変更する事が出来ないので、新たに接続先を作成する必要になります。そうなるとストーリーの参照先を変える必要があり一からストーリーの設定をし直すという悲しい結果となるので、そうならない為に事前に決めておきましょう。
kotlinx.serializationでOptionableなPropertyを定義する

ちょっとだけ躓いてしまったので、備忘録を残します。

## OptionableなPropertyってなんのこと？
下記のようなjsonが送られてくる場合を考えます。

```json
{
    "parameter_1": 123,
    "parameter_2": 456,
    "parameter_3": "sample string",
    "optional_parameter": "optional string" // ←入ってないこともある
}
```
状態によって含まれたり、含まれなかったりする
Property(`optional_parameter`)のことを指しています。

「このようなPropetyをKotlinのdata classはどう記述すれば良いか？」
が本記事の内容です。

## 結論
__optionalのパラメータは、デフォルト値を設定する必要があります。__

下記に2つ例を示します。

``` kotlin:例1_optionalがない場合はnullとする場合
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class SampleData(
    @SerialName("parameter_1")
    val parameter1: Int,
    @SerialName("parameter_2")
    val parameter2: Int,
    @SerialName("parameter_3")
    val parameter3: String,
    @SerialName("optional_parameter")
    val optionalParameter: String? = null
)
```

``` kotlin:例2_optionalがない場合は空文字とする場合
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName

@Serializable
data class SampleData(
    @SerialName("parameter_1")
    val parameter1: Int,
    @SerialName("parameter_2")
    val parameter2: Int,
    @SerialName("parameter_3")
    val parameter3: String,
    @SerialName("optional_parameter")
    val optionalParameter: String = ""
)
```

## 参考
- [公式ドキュメント](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#optional-properties)
- [StackOverFlowでの同様の質問](https://stackoverflow.com/questions/68192794/make-a-field-optional-in-kotlinx-serialization)

## 最後に
自分は「データが入っていなかったこと」を表現したいので、
例1を採用しています！

その他、別の良い方法があれば教えていただけると助かります！
2023年12月19日 ついに:has()が主要ブラウザで使えるようになった!?

## これはなに？

`:has()` というCSSの擬似クラスが、2023年12月19日から、FirefoxもSupportされるようになりました。
[Firefox 121 for developers - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/121)

詳しい挙動や使い方は、他の素晴らしい記事にお任せし、この記事では `:has()` の細かい挙動などではなく、ついに主要ブラウザで使えるようになった喜びをただ噛み締めたいと思います。

Chrome、Safari、Firefoxを最新版にして、デモをご覧ください :pray: 

## `:has()` とは

[:has() - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/:has) いわく

>:has() は CSS の疑似クラス関数で、引数として渡される相対セレクターのいずれかが、その要素から辿ってアンカーとして少なくとも一つの要素とマッチする場合に、その要素を表します。この疑似クラスは、相対セレクターリストを引数として取ることで、参照している要素に関して親要素や前の兄弟要素を選択する方法を提供します。

誤解を恐れず簡単に言いますと、今までCSSは子要素にしか指定ができませんでしたが、親要素も指定できるようになります。


## デモ

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="YzgKXaq" data-user="gilly135" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/gilly135/pen/YzgKXaq">
  has</a> by gilly135 (<a href="https://codepen.io/gilly135">@gilly135</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## サポートブラウザ

[:has() - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/:has)を見ると、Firefoxだけ pre-release versionですが、2023年12月19日から主要なブラウザはサポートが来ました！

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215239/ca816296-bd06-b1c9-725f-366937d713ad.png)

これからのマークアップがどのように変化していくのか楽しみですね！ :tada: 

## 参考

- [:has(): ファミリー セレクタ  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/has-m105/?hl=ja)
- [:has() - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/:has)
- [Firefox 121 for developers - Mozilla | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/121#css)
お名前ドットコムでドメイン名を購入してみた


技術書典で購入した「DNSをはじめよう」が大変わかりやすく面白かったため、DNS関連の実証実験をするためにお名前ドットコムでドメイン名を購入してみました。

「DNSをはじめよう」のお名前ドットコムの画面は少し古いものです。
少し戸惑った部分があったので、私と同じようにドメイン名を初めて購入する方のために2023年12月19日時点の最新画面での手順を書き残したいと思います。

### この記事でわかること

- お名前ドットコムでドメイン名を購入する方法
- 自動更新を解除する方法
- 購入したドメインでwhoisした結果

### ドメイン名購入までの流れ

①まずお名前ドットコムにアクセスします。
https://www.onamae.com/

②ほしいドメイン名を入力して検索します。
![スクリーンショット 2023-12-19 224111.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/240821c7-9837-88b1-1747-d0fa4a36dbcc.jpeg)

③購入できる末尾付きドメイン名が一覧されるので、欲しいのをクリックします。
クリックすると右側の「選択した商品」に追加されるので「料金確認へ進む」を押します。
![スクリーンショット 2023-12-19 221436.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/271c76a3-573b-d9e9-ee8e-cdf07b52f8ea.jpeg)

④サーバの勧誘画面では「決めていない」を選択します。
これが唯一無料の選択肢です。
![スクリーンショット 2023-12-19 221543.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/283bd0c1-b966-09a2-5033-6781b53bfe47.jpeg)

⑤念のためWhois情報公開代行がついているか確認します。
自動でついているはずなので「次へ」を押します。
![スクリーンショット 2023-12-19 221729.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/04023ed7-916e-357b-602f-786eaabc227c.jpeg)

⑥住所入力とクレジットカードでの決済を済ませます。
Whois情報公開代行をお名前ドットコムは行ってくれるため、氏名以外の個人情報は公開されません。
個人情報を入力する画面なので画像は省略しました。

⑦決済を済ませると画像のような画面に切り替わり100%になるとドメイン名購入完了です。
![スクリーンショット 2023-12-19 222038.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/8dadc1a7-385d-d7fc-301b-7f66497798c0.jpeg)

⑧最後に、登録したメールアドレス宛に「verification-noreply」という件名でお名前ドットコムからメールが来ているはずです。
認証用のURLをクリックし、必ず認証しましょう。
2週間以上認証しない状態でいると、ドメイン名が使用停止状態になります。

### ドメイン名自動更新を解除する

無料で購入したドメイン名でも来年以降も使うならお金がかかります。
来年以降もドメイン名を使う予定がないなら、自動でドメイン名を来年以降も購入する設定「ドメイン名自動更新」を解除しましょう。

①管理画面へ移動します。
![スクリーンショット 2023-12-19 230113.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/1eca62f0-b563-f5ba-f4c0-d013120093a5.jpeg)


②「メニュー一覧」からドメイン名の自動更新を選択します。
![スクリーンショット 2023-12-19 230539.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/d0774b6d-e584-e3ae-b287-afaead11c5f1.jpeg)

③自動更新設定申込内容が「設定済み」になっているはずなので「確認画面へすすむ」を押します・
![スクリーンショット 2023-12-19 222501.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/6548d5a0-3732-c504-0f92-7652cfae247b.jpeg)

④自動更新設定申込内容が「解除する」になっていることを確認して「規約に同意し、上記内容を申し込む」を押します。
![スクリーンショット 2023-12-19 222515.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/c0427bae-e201-7478-2ffa-2ed82de71107.jpeg)

⑤自動更新設定申込内容が「未設定」になったことを確認します。
![スクリーンショット 2023-12-19 230305.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2636207/aac1a773-1349-0e5b-9ba5-42893b1030d5.jpeg)

これで来年以降はこのドメイン名にお金を払うことはありません。

### 楽しい実験

自分のドメイン名でwhooisできることを試してみましょう。
https://www.onamae.com/service/whois/

結果に自分の個人情報は　乗っていないはずです。

### 次の予定

AWS Route53を使っていろいろ実験する！
【Ruby Gold3.1詰まったシリーズ】instance_variableメソッドについて

# はじめに
今回はinstance_variableに関する内容です。Ruby Goldの試験でinstance_variableに関して自信を持って回答できなかったため、簡単にまとめてみます。

# irbで確認してみた
環境はruby 3.1.4です。
[こちら](https://docs.ruby-lang.org/ja/latest/method/Object/i/instance_variables.html)によると、オブジェクトのインスタンス変数名をシンボルの配列として返すメソッドらしいです。
実際に、インスタンスを作成し、instance_variablesメソッドを実行
~~~ruby
class Book
    def initialize(title, author)
        @title = title
        @author = author
    end
end

ruby = Book.new('Ruby', 'taro')
ruby.instance_variables
~~~
~~~
irb(main):001* class Book
irb(main):002*     def initialize(title, author)
irb(main):003*         @title = title
irb(main):004*         @author = author
irb(main):005*     end
irb(main):006> end
=> :initialize
irb(main):007> 
irb(main):008> ruby = Book.new('Ruby', 'taro')
irb(main):009> ruby.instance_variables
=> [:@title, :@author]
~~~
すると、rubyインスタンスの`@title`と`@author`をシンボルのハッシュで返されました。
Ruby gold受験される際は、返り値には@が含まれることとシンボルのハッシュの形となることに気を付けていただければと思います...!

# おわりに
確認した結果、instance_variablesメソッドはオブジェクトのインスタンス変数名をシンボルの配列として返すメソッドだということが分かりました。instance_variablesメソッド実行時、返り値がどんな形で返ってくるのか曖昧な部分があったので、今回整理できてスッキリしました！誤り等ありましたらご指摘いただけますと助かります！

# 参考
https://docs.ruby-lang.org/ja/latest/method/Object/i/instance_variables.html

https://qiita.com/wai-doi/items/b7ff1d62e2c530680946
機械学習モデルの評価：交差検証法とホールドアウト法

# はじめに
機械学習モデルの評価は、モデルの性能を正確に測定し、適切なパラメータチューニングやモデル選択を行うために非常に重要です。この記事では、機械学習モデルの評価において広く使用される2つの主要な手法、交差検証法とホールドアウト法について説明し、それらの違いを探求します。

# 1. ホールドアウト法
ホールドアウト法は、データセットを2つの部分に分ける方法です。通常、データセット全体の一部をトレーニングデータセット（Training Set）として使用し、残りの一部をテストデータセット（Test Set）として保持します。モデルはトレーニングデータで訓練され、その後、テストデータで評価されます。以下は、ホールドアウト法の基本的なステップです。

### ホールドアウト法のステップ
データセットをトレーニングセットとテストセットに分割する。
モデルをトレーニングデータで訓練する。
モデルをテストデータで評価し、性能指標（例：精度、F1スコア）を計算する。
```
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression

# データをトレーニングセットとテストセットに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# モデルを訓練（ロジスティック回帰（分類タスク））
model = LogisticRegression()　
model.fit(X_train, y_train)

# モデルの性能を評価
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)
```
# 2. 交差検証法
交差検証法は、モデルの性能評価においてより信頼性の高い結果を得るための手法です。データセットを複数の部分（フォールド）に分割し、複数の試行でモデルをトレーニングおよびテストします。最も一般的な方法は、k分割交差検証（k-fold cross-validation）です。以下は、k分割交差検証のステップです。

### k分割交差検証のステップ
データセットをk個のフォールドに分割する。
モデルをk回訓練および評価し、各試行で異なるフォールドをテストセットとし、残りをトレーニングセットとする。
各試行の性能指標を記録し、平均値を計算する。
```
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression

# モデルを定義
model = LogisticRegression()

# k分割交差検証を実行
scores = cross_val_score(model, X, y, cv=5)  # 5分割交差検証を使用
average_accuracy = scores.mean()
print("Average Accuracy:", average_accuracy)
```
# 両者の違いについて
ホールドアウト法と交差検証法の主な違いは次のとおりです：

### データの利用
ホールドアウト法は、データの一部をテストセットとして保持し、残りをトレーニングに使用します。
交差検証法はデータを複数のフォールドに分割し、複数の試行で訓練とテストを行います。

### 信頼性
ホールドアウト法はランダムなデータ分割に依存し、結果がデータの分割方法に影響を受ける可能性があります。
交差検証法は複数の試行を通じて性能を評価するため、より信頼性が高い結果を提供します。

### データ利用効率：
ホールドアウト法は一部のデータをテストセットとして保持するため、データの利用効率が低い場合があります。
交差検証法はデータを最大限に活用し、モデルの性能をより正確に評価します。

どちらの方法を選択するかは、データセットのサイズや利用可能な計算リソースに依存します。小さなデータセットでな場合、ホールドアウト法が適しているかもしれませんが、大規模なデータセットの場合は交差検証法がより適しています。

# まとめ
この記事を通じて、機械学習モデルの評価における2つの重要な手法である交差検証法とホールドアウト法について学びました。データサイエンティストとして、適切な評価手法を選択し、モデルの性能を適切に評価する能力は非常に重要です。
図書館で技術書を探すときのTips②　貸出編

# はじめに

 前回、こちら↓の記事で図書館で技術書を検索する際のコツを書きました。
 本記事では、実際に借りる前のコツをご紹介します。

https://qiita.com/awonohata/items/cbc205fa8ff55ae47a63

# 本を借りる前のコツ

## 新着図書コーナーをチェックする

 古典と呼ばれる、以前から一定の評価を受けていて現代でも通じる書籍があり、重宝することも多いです。
 ただ、技術書の場合はシステムやサービスのアップデートの速度が他の分野と比べて速いため、情報が陳腐化しやすくなります。

 図書館では出版から所蔵まで若干のタイムラグは発生しますが、ある程度新しい書籍が入ってきます。定期的に新着図書コーナーをチェックすることで、比較的新しい情報に触れることができます。

## 予約・取り寄せサービスを活用する

　同じ市内にはあるけれど、家の近くの図書館にはない、というときや、貸し出されているから待たないといけない…といった場合に、予約・取り寄せサービスを利用します。ほとんどの自治体図書館で利用できるかと思います。

　予約・取り寄せサービスを利用することで、以下のようなことができます。
 * 前の人が本を返却し、貸出できる状態になった時に通知される
 * 人気の本の順番待ちができる（予約時に予約人数や待ち人数が確認できます）
 * 遠方の図書館の蔵書でも借りることができる（自治体によって対応可能範囲が異なります）

　自治体によっては、県立図書館と市立図書館の相互貸借（お互いから貸出が可能）サービスを導入していて、取り寄せた本は、家の近くで借りることができます。

--------

 また、セルフの予約本受け取りコーナーや、図書館が閉館した後も受け取れるロッカー型の受け取りBOXなども導入されています。

### セルフ受け取りコーナー

　基本的には、予約した本は貸出カウンターで受け取る、というやり方が主流です。セルフ受け取りコーナーは、病気や法律の本など、「何を借りたか知られたくない」という気持ちに応えた、プライバシーに配慮したコーナーになっています。また、カウンターに並ばなくて済むのもメリットです。
 
 　セルフ貸出機とセットで設置されています。予約情報と貸出カードが紐づいているため、他の人が間違って借りようとしても、借りることができないようになっています。
 
**例）　石川県立図書館**

 https://www.library.pref.ishikawa.lg.jp/category/booksearch/1030.html

**例）　市川市立図書館**

https://www.city.ichikawa.lg.jp/library/info/1300.html

### 時間外受け取り

　日中仕事が忙しい方など、図書館が開館している時間に行くのが難しい方のために、時間外受け取りができるロッカーが設置されている図書館も多いです。

**例）　下関市立図書館**

　宅配ボックスや宅配ロッカーの技術を活かした、無人書籍受け取りロッカーを設置しています。

https://www.value-press.com/pressrelease/268206

**例）　西宮市立図書館**

　図書館だけでなく、JRの駅で受け取れるサービスも導入しており、通勤や通学のついでに本を受け取ることができます。

https://tosho.nishi.or.jp/guide/yoyakulocker.html

## 雑誌コーナーをのぞいてみる

　図書館は出版から所蔵まで若干のタイムラグがあると前述しましたが、雑誌コーナーは基本的に最新号に更新されていきます。雑誌１種類あたり1冊が原則のため、人気の雑誌はなかなか手に取ることが難しいかもしれませんが、気軽に数種類の雑誌を試せるのは強みです。特に大学図書館は、専門雑誌を所蔵しているので、その恩恵は大きいです。

 技術系の雑誌だけに関わらず、注目されている技術やトレンド情報はビジネス系の雑誌にも記事があることが多いので、要チェックです！　
 
　ちなみに、最新号は貸出不可になっていることが多いですが、バックナンバーは貸出受付していることも多いです。

## （番外編）「最近返却された本」の棚を探してみる

　図書館によっては、「最近返却された本」というコーナーやブックトラックに置かれた特集があります。これの面白いところは「直近で誰かが読んだ本」＝「興味を持った人がいる本」ということです。

　キーワード検索では見つけられない、掘り出し物の本と出会える場所でもあるので、あと1冊何か借りたいな、と思った時にのぞいてみてはいかがでしょうか。

# まとめ

 今回は図書館で本を借りる前のコツについて書きました。
 大学でも図書館は使っていましたが、むしろ社会人になってから図書館を利用することが多くなったと思います。自分なりに前述したやり方を模索して、ようやく図書館の面白さとありがたさが分かってきたような気がします。

 次の記事では、積読を減らすための工夫について書きたいと思います！
maya で接地点を取りたい

接地点を取りたい

接地点を取りたいって言うと、closestPoint~　をすすめられることがあるけども

最近接点なので
ほんとは青い点が取りたいのに赤い点が取れちゃったりするのでなんか違うんですよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/432371e9-3242-75e4-640e-a986585b354d.png)

API使えば任意方向にray飛ばしてぶつかった点とか取れるのですが、もっとお手軽に何とかしたい。

shrinkWrap
-
私が見つけたのはこれ、えすけーｔ　shrinkWrap

shrinkWrapはまぁ便利なやつですね。
検索すると色んな事例が出てきます。

plane から sphereに対してshrinkWrapを行い

projectionを parallel to axesに変更
axisReferenceをとりあえず target local にし
along X/Y/Z のYのみにチェック
※Yのチェックを on/offすると形状がリセットされます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/4d562070-9208-e4fe-35f9-b609d9e0d1d2.png)

sphereを-Y方向に移動させて、planeと接し始めるとshrinkWrapが働き
sphereがplaneにshrinkされます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/236eb2e5-1326-34ab-1d48-55e41255015b.png)


planeが平面でない場合も
設定した軸方向にrayを飛ばした感じになるので、良い感じに接地してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/f68d661b-69e4-4147-cc50-3b2746331d03.png)


接地点を取りたい
-

nurbsCurveだと楽なのですが、shrinkWrapは非対応なのでポリゴンで。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/8c27378c-13c8-b05d-2a71-606fdd59a388.png)

この辺は任意に調整しますが、一旦こんな感じで
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/f3705711-97fb-922a-1c81-8fd3163f2778.png)

一番下のvtxを選択して　constraint > rivet を実行
※特にrivetである必要はないので、vtx位置をトレースできれば他の方法でも可
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/1cec0356-ea4f-27c6-8c1d-a9f8120f84f9.png)


こんな見た目になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/faac4c0e-07d5-bf15-2da7-3297fcdcc863.png)

ひし形ポリゴンを動かすと
shrinkWrapでplaneにshrinkされて、
リベットされてるロケーターが接地点を教えてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/a2a42dca-54e0-8051-ea00-36d48caa9610.png)


接地判定するなら
-

一番下のvtx位置にロケーターを2個作成し、親子化（locator1 locator2
pinOutput から　locator2 に対して pointConstraint (offset = False)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/a69c022e-b5fb-4b9b-1c8f-d5de5c79fc8c.png)

floatLogicとかconditionとか適当につかって、locator2のtranslateYが　> 0 になった時が接地してる時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/91b1dd64-78c6-1af6-bd25-71c2511688c3.png)


判定結果をsphereのvisにつないでみたのがこちら
https://vimeo.com/896185642?share=copy
Selectタグに水平線が追加できるようになったので試してみた

## これはなに？

Chrome119とsafari17で `select` タグに `hr` を追加できるようになりました。
`optgroup` タグと組み合わせることで、よりグルーピングを見やすくすることができます。
`optgroup` タグはつけないでも利用することは可能です。

## デモ

試しに47都道府県のグルーピングをしてみました。
例えば「 `三重県` を選択してください」と言われた場合、グルーピングなしとありの `select` どちらが見つけやすいでしょうか？
大体の方がグルーピングありのほうが見つけやすいと思います。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="PoLYwMN" data-user="gilly135" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/gilly135/pen/PoLYwMN">
  select in hr</a> by gilly135 (<a href="https://codepen.io/gilly135">@gilly135</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

都道府県の分類は[全国の都道府県一覧（閲覧） | 国土地理院](https://saigai.gsi.go.jp/jusho/view/index.html)に基づいて行いました。

## サポートブラウザ

[要素の選択: 水平方向のルールを適用  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/hr-in-select/?hl=ja) によるとFirefoxがまだ対応していません。

- :o: Chrome 119
- :o: Safari 17
- :x: Firefox

## 参考

- [要素の選択: 水平方向のルールを適用  |  Blog  |  Chrome for Developers](https://developer.chrome.com/blog/hr-in-select/?hl=ja)
FastAPIを用いて音声ファイルをMongoDBに保存

## はじめに
備忘録として、MongoDBに音声ファイルをBase64に変換して保存する方法を残します。

### GitHub
https://github.com/Nyamadamadamada/animal_voice
動物の鳴き声をあつめたページを作りたかったのですが、未完です。
音声ファイルをMongoDBに保存するくだりは参考になるかと思います。

## メインのコード


```python

from fastapi import FastAPI, HTTPException, UploadFile, Form, status
from fastapi.responses import HTMLResponse, RedirectResponse
from typing import Optional
from pymongo import MongoClient
import traceback
import base64

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
async def read_items(message: Optional[str] = None):
    alert_script = ""
    if message:
        alert_script = f"<script>alert('{message}')</script>"

    return f"""
    <html>
        <head>
            <title>FAST API</title>
        </head>
        <body>
            <h1>FAST API</h1>
            <form method="post" action="/input/audio/" enctype="multipart/form-data">
            <p><input type="file" id="v_file" name="v_file" accept="audio/*" /></p>
            <button type="submit">送信</button>
            </form>
            {alert_script}
        </body>
    </html>
    """

@app.post("/input/audio/", response_class=HTMLResponse)
async def inputAudio(v_file: UploadFile = Form(...)):
    try:
      # MongoDBへの接続
      client = MongoClient('mongodb://root:example@mongo:27017/')
      db = client.fileData

      file_content = await v_file.read()
      # Base64でエンコード
      encoded_file = base64.b64encode(file_content)
      post = {
            "filename": v_file.filename,
            "file": encoded_file.decode()
        }
      db.audio.insert_one(post)
      return RedirectResponse("http://localhost:8000?message=アップロード完了", status_code=status.HTTP_303_SEE_OTHER)
    except Exception as e:
      # 例外情報をログに記録
      traceback.print_exc()
      # HTTPレスポンスとしてエラーメッセージを返す
      raise HTTPException(status_code=500, detail=str(e))
```

### 初めの画面

```docker
docker compsoe up -d 
```

Docker Composeコマンドを実行し、サーバーをたちあげたあと
`http://localhost:8000/`にアクセスすると次の画面が表示されます。

![スクリーンショット 2023-12-19 22.40.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1650886/eb6a2845-2eb4-8f2d-3bf7-0b7e56b6f22a.png)


## 終わりに
メモでした。

### 参考
base64 エンコード
https://cloud.google.com/speech-to-text/docs/base64-encoding?hl=ja#using_client_libraries
Building Real-Time Analytics Systems

『Building Real-Time Analytics Systems』は、Apache Pinotのコミッタの方が書かれたリアルタイム分析システムについての概要、チュートリアル的な本です。簡単なサンプルコードも用意されているので、モダンなリアルタイム分析システムを体験してみることもできます。

<a href="https://www.amazon.co.jp/Building-Real-Time-Analytics-Systems-Insights/dp/1098138791"><img src="https://m.media-amazon.com/images/I/81-LCYZ79eL._SL1500_.jpg" width="200" alt="Building Real-Time Analytics Systems書影"></a>

## リアルタイム分析の歴史

1990年代にCEP(Complex Event Processing)として誕生し、金融サービスにおける不正検知、センサーネットワークにおける異常の監視などでは使われたものの、標準化もされなかったので、広く普及はしませんでした。

2010年代にSamzaやStormが開発され、StormはTwitterのトレンドを表示するのに使われました。その後Storm開発者のMarzさんは、ラムダアーキテクチャを提唱しました。ラムダアーキテクチャはMarzさんの著書『Big Data』で詳しく語られています。

<a href="https://www.amazon.co.jp/Big-Data-Principles-practices-scalable-ebook/dp/B097835W6T/"><img src="https://m.media-amazon.com/images/I/71nAYOPW16L._SL1500_.jpg" width="200" alt="Big Data書影"></a>

そして『Building Real-Time Analytics Systems』では、このラムダアーキテクチャを前世代のものと位置付け、モダンストリーミングスタックでは、

* クラウドネイティブ
* サーバレス
* 開発者エクスペリエンス
* SQL

を、その柱と位置付けているようです。

## モダンストリーミングスタック

モダンストリーミングスタックは、以下のような構成であるとしています。

![モダンストリーミングスタック](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8475/95fb07d5-9b88-b55a-be23-aef55b5a76e8.png)

各構成要素の役割は以下のとおりです。

* **イベントプロデューサ**： ソースシステムの状態変化を検出し、下流のアプリケーションで使うイベントをストリーミングプラットフォームに投げ込む。これはソースシステムのアプリケーションで明示的にイベント発行することもあれば、CDCツールを使ってデータベースへの変更をキャプチャすることもある。
* **ストリーミングプラットフォーム**: 大量のイベントデータを溜め込み、それを複数のコンシューマにも並行して渡せる仕組み。すなわちKafkaが該当する。他の選択肢としては、Amazon KinesisやRedpanda、Apache Pulsar、Google Pub/Subなどがある。
* **ストリームプロセッサ**:  ストリーミングプラットフォームからデータを読みこみ、集計したり、変換したり、フィルタしたりする。Flink、Spark Structured Streaming、Apache Beam、Kafka Streamsなどが該当する。これらの処理をSQLを使って書けるものもある。
* **サービングレイヤ**: ストリーミングプラットフォームで生成されたリアルタイム分析を利用するためのアクセスポイント。分析に使う(ストリームプロセッサで一次処理された)イベントを溜め込み、フロントエンドアプリケーションからのクエリに高速にレスポンスを返せる必要がある。KVSやNoSQLデータベースでも良いし、Apache Pinot、Apache Druid、Rockset、ClickHouseのようなもの達が該当する。

## リアルタイム分析を何に使うか?

結局、分析系がリアルタイムであるのが必然な要求がないと、そこまで広まることがないわけですが『Building Real-Time Analytics Systems』では、以下のようなユースケースをあげています。

* パーソナライゼーション
* メトリクス
* 異常検知と根本原因分析
* ダッシュボード
* アドホック分析
* ログ分析

前世代でも問題なく出来ていたことが多く、モダンなスタックである必然性が薄いように思えます。

最後の13章には、以下の未来のユースケースが挙げられていて、こちらが本命のような気がします。

* エッジ分析
* コンピューティングとストレージの分離
* データレイクハウス
* リアルタイムデータ可視化
* ストリーミングデータベース
* サービスとしてのストリーミングデータプラットフォーム
* リバースETL

溜め込んでから分析するのではなく、分析に使ったら消えていくのでコスト削減のメリットがある、という点が、いくつかのユースケースでは共通してあります。これはStormが流行した時も言われていたと思うのですが、今やどんな企業でも使うかどうかわからないデータを、とにかく貯めるだけ溜めておいてある現状があるので、当時よりも響きやすいかもしれません。