OmniAICreator/Qiita-1.07M huggingface dataset:
私のnode古すぎ？node10.から最新にした話

#はじめに
環境
OS：ubuntu20.04
npmは8.3.1
　
　これまで使っていたnodejsが古すぎて使えないとnpmから言われてしまったので、新しいnodeを導入することになりました。aptやnpmじゃないインストール方法を全く知らなかった筆者が、右往左往しながらnodeを追加するまでの備忘録を書きます。

#npmコマンドを使ったある日
![npminstallreact.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/574155e8-f720-b13c-bc93-6abf0b171713.png)

```shell
npm does not support Node.js v10.19.0 
```

ある日突然、nodejsが古いと指摘された。これまで普通に使えた気がするんだけどな。ただ調べてみると最新版がv17であるのに、今使ってるのがv10では確かに古いか。

#普通にnodeをアップデートしようとする
nodeはaptコマンドからインストールしていたので、apt updateなりで一応アップデートを確認。他にも再インストールなどしたが無駄だった。
この日、私はようやく、aptではnodeの更新が止まっていることに気づいた。

#npmのnパッケージをインストールすれば良いらしい

```shell
sudo npm install -g n
#その後 
sudo n latest
```
https://www.trifields.jp/how-to-install-node-js-on-ubuntu1804-3544
こちらのサイトを参考にしました。

npmコマンドがあれば、nパッケージをインストールして、こいつからnodeの最新版がインストールできるらしい（「n」一文字って凄いな）。これで完了すればなんて難しいことではなかった。しかし。。
![npminstalln.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/d1a72937-5e77-1ae7-cf3f-446bf7aa2e8b.png)

nodeを入れるために、nodeが古いと言われる始末。(/・_・＼)

#じゃあ、公式サイトからダウンロードするっか

色々悩んだが、無事この発想にたどり着くことができた。
https://nodejs.org/ja/
![nodeDownload.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/068ec9fb-a9de-8c03-b1b3-9308830c7e1a.png)

ゲットしてきたnodeを解凍して...
![unpackedFiles.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/16a7af64-5d57-80fd-3b5d-078625a1dc6d.png)

#####これ、どうすれば良いのだ？　((ヽ(´Д`；)ﾉ))
この状態で使えるわけないけど、インストーラー的なやつはないし、ここから「何か」しないと使えない。
aptみたいなパッケージ管理コマンドか、インストーラーしか使ってない自分の素人っぷりを実感した。

#/usr/localへ統合すれば良いらしい(?)
一応成功したので書いておく。よく見たら上のディレクトリたち、linuxのディレクトリ構成と似ているので、このファイルたちをどこかに配置されば動くはずだ！。
案の定、/usr/localを覗いてみると、解凍したディレクトリと同じ構成をしたディレクトリがあった。cpコマンドでダウンロード先にあるnodeをusr/localにセットしたら、無事コマンドとして使えるようになった。

```shell
sudo cp -rp ~/Downloads/node-v16.14.0-linux-x64/bin/* /usr/local/bin
sudo cp -rp ~/Downloads/node-v16.14.0-linux-x64/include/* /usr/local/include
sudo cp -rp ~/Downloads/node-v16.14.0-linux-x64/share/* /usr/local/share
sudo cp -rp ~/Downloads/node-v16.14.0-linux-x64/lib/* /usr/local/lib
#こんな感じで4つのディレクトリをコピー（移動）した
#Downloads/node-v16.14.0-linux-x64/　は人によって違う
```
その後、
sudo npm install -g n　もsudo n latestも成功して無事にnodeを最新にできました。
【受験記録】AWS Solutions Architect - Professional (SAP-C01) 

## 前提
- 4年目エンジニア
- アプリケーション開発メイン
- インフラはあんまり触らない
- AWS　CLF,　Associate3つは保持
- 受験日 2022/2/19

##試験結果
点数:808点/750点/1000点(取得スコア/合格ボーダー/満点スコア)
スコアパフォーマンス: いづれのセクションも"コンピテンシーを満たしている"

##試験感想
試験問題が長く、時間が足りない、本番だと転がっている問題集より難しいとインターネット上の至る箇所で言及されているため、"早く回答しなきゃ、１周見てわからないところに戻ろう"ということでハイペースに回答を進めた結果、75分程度で一通りの回答が完了してしまいました。
この時点で8割当たってるであろう問題数が50問、フラグをつけたよくわからないけど回答している問題数が２０問程度でした。フラグ付与した問題を再度確認して、怪しいけど選んだ選択肢でよさそうな問題数が１０問、考えてもわからない問題が１０問という感じになりました。
75%ということで50*0.9+10*0.7+10*0.5=57問くらいでいけそう + １５問は採点対象外の問題があり、比較的わからない問題の割合に当たる可能性が高いという算段で、試験開始から　95分程度で試験を終了しました。結果の点数が返ってくるとおおよそ想定通りの点数でした。

問題見て、Route53　ResolverやFSxに関しての問題が出題されていたのは意外でした。（いろんな模擬問題でも出てきていなかった認識）

普段インフラをメインにしていないため、ネットワーク、移行、マネジメント・ガバナンス、セキュリティといった問題では勉強した類題でなければわからないなぁということは再認識したので、3年後までの再試までにそれらを改めて勉強する、もしくは試験前に同じく詰め込む必要があるというのは再試を受ける自分に向けての申し送り事項となりました。

## 勉強方法
学生時代に（それぞれの教材の完成度はぼちぼちで）参考書とか問題集いっぱい持っているやつ、いませんでしたか？
自身が上記に当てはまる典型的な人なので、AWS SAPに向けてはいろいろ購入、受講していました。
半年前くらいから勉強したりしなかったり期間があり、受けようかと思ってから約１か月半でしっかり勉強していました。

- 参考書、クラス

    - [AWS認定ソリューションアーキテクト－プロフェッショナル～試験特性から導き出した演習問題と詳細解説～](https://www.amazon.co.jp/dp/B08F9CQ6LT/)
        - 表題通り、シナリオに対応した問題とその要点の解説を中心とした参考書。内容を通読し、模擬試験は試験前の総復習で活用。レファレンスとしても活用。

    - [AWS認定資格試験テキスト＆問題集　AWS認定ソリューションアーキテクト - プロフェッショナル](https://www.amazon.co.jp/dp/B09DKZWX7N/)
        - 各試験観点で必要な知識の解説を中心とした参考書。内容を通読し、模擬試験は試験前の総復習で活用。レファレンスとしても活用。
    
    - [Advanced Architecting on AWS](https://www.trainocate.co.jp/reference/course_details.aspx?code=AWC0059V)
        - [所属会社](https://qiita.com/organizations/beex-inc)のお陰で受講。試験全体で必要な知識に関する講義やハンズオンを実施。当時使用した教材についてリファレンスとして活用。

- 問題集
    - [AWS　Web問題集で学習しよう](https://aws.koiwaclub.com/)
        - 通称koiwaclub。日本語で上位試験の問題集としては唯一無二のクオリティの問題集かと思います。クイズ形式、7問単位で解答できるため、スキマ時間等を中心に約２周程度学習。
    - [whizlab](https://www.whizlabs.com/)
        - サブスクリプションを購入すればさまざまな資格の模擬問題、ハンズオン、講義動画が使用できる。５回分の模擬問題、トピック別問題について１周ずつ実施。英語サイトだが、Google翻訳が使用でき、クイズモードで利用もできるので、短時間しか確保できない英語苦手民でも使いやすいかと思います。（解答後の復習でGoogle翻訳がうまく当たらないことが多発しましたが、クイズモードは問題なく解答まで翻訳してくれました。）模擬問題について全て１周分学習。
    - [Udemy](https://www.udemy.com/) 
        - [こちら](https://www.udemy.com/course/aws-solutions-architect-professional-practice-exams-amazon/)の問題集を使用しました。Google翻訳はうまく当たるのですが、クイズモードがなく、全てを解き切らないといけないので、時間がしっかり確保できる時に使用していました。 模擬問題について全て１周分学習。
Homebrewのインストール(Intel / M1 両対応)

# 概要
M1 macではHomebrewが非対応の時期がありましたが、今現在M1 macでも普通につかえるように対応されています。

# 最初に
ターミナルを開いてコマンドを打ちます

```terminal
brew -v
```
バージョンが表示されるならHomebrewはインストール済みなので、この記事を読む必要はありません
# 本文
Homebrewの公式HPに用意されているコマンドをコピーして、ターミナルに貼り付けるだけでインストールされます
![スクリーンショット 2022-02-21 15.31.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/6f161b75-1e85-9215-18cb-284449c693d5.png)
インストールが終わるとPATHを通してくださいとメッセージが表示されます。
![スクリーンショット 2022-02-21 15.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/b73f4a30-6051-122c-97c2-45cef9033d5f.png)
この2行「echo ~」と 「eval ~」を順番にコピペするだけでPATHの設定が完了します。
最後に正常にインストールできたことを確認します。以下のコマンドを入力してHomebrewのバージョンが表示されたら完了です！

```terminal
brew -v
```
EC2インスタンスのメタデータ（IMDSv1）をPowerShellで一括出力してみる

[インスタンスメタデータの取得
](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html)


EC2インスタンスの中で169.254.169.254にアクセスすると、EC2インスタンスのメタデータを参照することが出来ます。

exsample

- `169.254.169.254/latest/meta-data/instance-id` でインスタンスID
- `169.254.169.254/latest/meta-data/instance-type`　でインスタンスタイプ

が参照できる。

本記事ではそんなメタデータをPowerShellをつかって一括出力してみます。

## 実行した環境

- Windows Server 2016
- Windows PowerShell 5.1

## PowerShellで一括出力サンプル

```PowerShell
function expand-awsmeta([string]$base , [string]$leaf) {
    if ($leaf.EndsWith("/")) {
   (Invoke-RestMethod "$base$leaf" ) -split "`n" | ForEach-Object { expand-awsmeta "$base$leaf" $_ }
    }
    else {
        try {
            $value = Invoke-RestMethod "$base$leaf"
            Select-Object @{n = 'uri'; e = { "$base$leaf" } }, @{n = 'value'; e = { $value } } -InputObject ""
        }
        catch {
            # public-keysだけ
            # 169.254.169.254/latest/meta-data/public-keys/0/openssh-key といった形なので妥協
            Select-Object @{n = 'uri'; e = { "$base" } }, @{n = 'value'; e = { $leaf } } -InputObject ""
        }
    }
}

expand-awsmeta "169.254.169.254/latest/" "meta-data/"
expand-awsmeta "169.254.169.254/latest/" "dynamic/"
```

上記コマンドをPowerShellプロンプトにコピペして実行すると下記のように一括でメタデータが出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/be6f49c5-8713-c8a7-e2a7-7169919d1554.png)

## やっていること

AWSのメタデータのURIについては、`169.254.169.254/latest/meta-data/`にアクセスすると項目の一覧が取得でき、`169.254.169.254/latest/meta-data/`+`<<項目名>>`でアクセスすると値が取得できます。

データを眺めてみると、末尾が`/`で終わっている項目名は項目名のリストになっているようなので、function　expand-awsmetaを定義して、末尾が`/`だったら再帰的に読み込んでます。

`169.254.169.254/latest/meta-data/public-keys/`項目だけは、取得した項目をそのまま後ろにつけるのではなく、`169.254.169.254/latest/meta-data/public-keys/0/openssh-key`のようにする必要があるようで、これは今回取得を妥協しています。（今回は項目名（キー名）だけ取得している）

## 総評

一覧で出力してみると、こういう項目もあったのかといった感があります。
[Kotlin]Room関連のエラー

# 概要
Kotlinでこんなエラーが出ました
![スクリーンショット 2022-02-21 11.24.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/32f27f79-8086-4723-7205-3c0c35f062d3.png)
`> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction`
`> java.lang.reflect.InvocationTargetException (no error message)`

# 本文
検索してみるとRoomがらみで発生するようですが、原因はさまざまのようです。

今回の場合は、build.gradle(Module:)内のroom関連を最新のものにするだけで解決しました
![スクリーンショット 2022-02-21 11.32.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/b2ffa0ea-3131-b302-b003-f2a72c44912d.png)
↓
![スクリーンショット 2022-02-21 11.32.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/989cc320-f34d-2ac1-4c99-a7cfc3afa1be.png)

# 補足
もしバージョンアップしても解決しなかったら、ここら辺が参考になるかもしれないです
https://stackoverflow.com/questions/62131564/a-failure-occurred-while-executing-org-jetbrains-kotlin-gradle-internal-kaptexec
【超初学者向け】MacでSTSが起動しない時の２つの対処法


PC再起動時やSTSを閉じると起動しない現象が結構な頻度で起きてキレそう（キレてる）だった時に行った対処法。
初めてのmacOSでうきうきで開発しようとしていた時に頻発していたので、macにしたばかりの方で同じ状況の方の参考になれば＆記憶を失った未来の自分へのメモです。


# 対処法①クリーン起動

下記ページで手順が詳しく載ってます。
https://liginc.co.jp/532678

# 対処法②再インストール

PC内にインストーラーさえあれば脳死でできるので自分はこっちでやってます。

１. Finder→「アプリケーション」フォルダを開き、STSを削除
２. STSのインストーラで再インストール　（STSアイコンをApplicationsフォルダにドラッグアンドドロップ）
※mac新規ユーザーはだいたいダウンロードフォルダに放り込んで放置してる（多分）

![スクリーンショット 2022-02-21 19.42.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2187218/1bb6afe4-402a-be92-a58b-a3ca79f49e29.png)


３. 新しくインストールしたSTSを起動
４. ワークスペース選択時に、常時使用しているワークスペースを選択

これで元通りです。
【GAS】エクセルを自動でスプレッドシートへ変換＆アーカイブしといてくれるフォルダ

## 概要
書類としてのエクセルシートが溜まって鬱陶しい。

一旦通したら二度と見返すこともないはずだけど、何かあった時のために取っておいた方が良いだけの、サイズの小さなエクセルファイルが無数に増えていく。

ドライブにアップしてPCからは消してしまおう。ドライブにアップするなら、スプレッドシートに変換すれば便利かも？

と言うわけで、エクセルファイルをアップしておくと、自動でスプレッドシートに変換して、特定のフォルダにアーカイブしておいてくれる魔法のフォルダをドライブ上に実装した。

## 流れ
①アップロードフォルダにエクセルファイルを保存
　↓
②毎日トリガーでスクリプトが作動
　↓
③フォルダ内のファイルをスプレッドシートへ変換
　↓
④アーカイブ用のフォルダへ保存
　↓
⑤元のファイルは削除

①以外は全て自動。
GASさんパねーっす！

## コード
次のスクリプトを、就業時間外にトリガーセットし毎日実行。

```javascript
function extoss() {

  //アップロードフォルダとアーカイブフォルダのIDを指定
  var srcFolderId = 'アップロードフォルダのID';
  var dstFolderId = 'アーカイブフォルダのID';

  //アップロードフォルダのファイルを全て格納
  var xlsFiles = DriveApp.getFolderById(srcFolderId).getFiles();

  //本日の日付（ファイル名が被らないよう変換後のファイル名に日付を入れたい）
  var date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd_');

  //ファイル総当たり
  while(xlsFiles.hasNext()) {

    //順番にファイルを取り出す
    var file  = xlsFiles.next();

    //新しいファイル名は「日付_元のファイル名」
    var title = date + file.getName();

    //ファイル名, マイムタイプ, 保存先など指定
    options = {title: title,
               mimeType: MimeType.GOOGLE_SHEETS,
               parents: [{id: dstFolderId}]};

    //変換してドライブへ保存 ※DriveApiの有効化が必要
    Drive.Files.insert(options, file.getBlob());

    //元のファイルをゴミ箱へ移動
    file.setTrashed(true);
  }
}
```

## 補足（してない）
フォルダIDの調べ方や、トリガーの設定、DriveApiの有効化についてはググると丁寧なサイトが出てくるので割愛。

ファイルを順番に取り出してくれる`hasNext` `Next`は、`for_of`みたいなメソッドで便利^^

## 検討
同じフォルダにPDFや画像を放り込んでおけば、ドキュメントに変換してくれる（画像はOCR処理付き）。記事のタイトル間違えたか？
変換処理の部分を`try_catch`しておけば安全なんだろうけど、どうせ自分しか使わないので横着。
元ファイルについて、厳密には削除ではなくゴミ箱への移動。

### おしまい
yukicoder No.485 方程式のお勉強

今回も [yukicoder](https://yukicoder.me/) の問題紹介・解説を行っていきたいと思います！

$4$ 問目に紹介する問題はこちら！

## 概要
問題 ： [No.485 方程式のお勉強](https://yukicoder.me/problems/no/485)
レベル ： ★
実行時間制限 ： 2000 ms

問題文へのリンク↓↓

https://yukicoder.me/problems/no/485

<hr>

## 問題文

> 九郎くんは、方程式を解くことにハマりはじめています。

> 今日は $Ax = B$ 型の方程式をたくさん解きます。

> 九郎くんは学校で小数や分数を習っていないので、解 $x$ が整数にならない場合には、問題を解くことが出来ません。

> 家庭教師のあなたの仕事は、九郎くんが問題を解く前に答えを作ることです。

> 九郎くんが解ける問題ならば解 $x$ を、解けない問題ならば "NO" を出力して下さい。

## 制約

- $-100 \leq A \leq -1$ または $1 \leq A \leq 100$
- $-100 \leq B \leq -1$ または $1 \leq B \leq 100$
- A, B は整数

## 出力

> $Ax = B$ の解 $x$ が整数になる時は解 $x$ を、そうでない場合は "NO" を出力して下さい。

> 最後に改行してください。

## サンプル

### サンプル1

```Bash:入力
3 12
```

```Bash:出力
4
```

$3 \times x = 12$ となる $x$ は $4$ です。

$4$ は整数なので、九郎くんが解ける問題です。

### サンプル2

```Bash:入力
7 10
```

```Bash:出力
NO
```

九郎くんが解けない問題です。

## 解説

ここから解説を行っていきます。

自力で解いて見たい方はここで一旦止めてください！

### 解が存在する場合を考える

解が存在する場合を考えます。

まず $A \neq 0$ であれば、方程式の両辺を A で割ることができます。

```math
Ax = B \\
x = \frac{B}{A}
```

今回は制約が優しく A, B どちらも $0$ の場合が除かれているのでこの式変形はどんな入力に対しても行うことができます。

次に整数解かどうかを判定します。整数解であるとは、 B を A で割った余りが $0$ であることです。

この場合、 $x$ を整数で表すことができます。

そうでない場合は整数解は存在せず、答えは "NO" となります。

したがって、このような構築を考えることでこの問題を解くことが出来ました。

## ACコード

```python
a, b = map(int, input().split())

if b % a == 0:
    print(b // a)
else:
    print('NO')
```

## 最後に

最後まで読んできただきありがとうございました！

yukicoder は ★1 でもコーナーケースがある問題が多かったりして良い対策になる気がします。

今後もどんどん記事書いていきます！

リクエストなどがあればぜひ声かけて下さい！ありがとうございました！
RSpecの書き方２(before do)

# はじめに
今回はRSpec内でテスト前の事前処理をまとめたいときに使用するbefore doについてまとめます
そもそも使ったことがない、1から知りたい場合は下記の記事を合わせて参照してください

https://qiita.com/sekkenn1102/items/9f87748876bbb85f43ac

今回のテストは **「ログイン処理があるwebページでログイン後の処理(マイページ確認など)をテストする」** ことを想定して記載します
# 何も考えずにテストを書くと…
```request_spec.rb
describe 'マイページ確認' do
  context 'ログインしている場合' do
    it '「ログインID」の項目があること' do
      post '/login_to_application' params:{id: 'test_user', password:'password'} # ログイン処理
      get '/mypage'
      expect(response.body).to include 'ログインID：'
    end

    it '「ニックネーム」の項目があること' do
      post '/login_to_application' params:{id: 'test_user', password:'password'} # ログイン処理
      get '/mypage'
      expect(response.body).to include 'ニックネーム：'
    end

    it '「ひとこと」の項目があること' do
      post '/login_to_application' params:{id: 'test_user', password:'password'} # ログイン処理
      get '/mypage'
      expect(response.body).to include 'ひとこと：'
    end
  end

  context 'ログインしていない場合' do
    it 'エラーページに遷移すること' do
      get '/mypage'
      expect(response.body).to include 'お探しのページは見つかりませんでした。'
    end
  end
end
      
```
"/login_to_application"というパスにユーザー名とパスワードを渡すとログインできると仮定して記載していますが、上記のようにテスト項目ごとに毎回ログイン処理を記載しないとテストが通りません。こんな時に役に立つのが共通化するためのbefore doです。

# before do
```request_spec.rb
describe 'マイページ確認' do

  context 'ログインしている場合' do
    before do
      post '/login_to_application' params:{id: 'test_user', password:'password'} # ログイン処理
    end

    it '「ログインID」の項目があること' do
      get '/mypage'
      expect(response.body).to include 'ログインID：'
    end

    it '「ニックネーム」の項目があること' do
      get '/mypage'
      expect(response.body).to include 'ニックネーム：'
    end

    it '「ひとこと」の項目があること' do
      get '/mypage'
      expect(response.body).to include 'ひとこと：'
    end
  end

  context 'ログインしていない場合' do
    it 'エラーページに遷移すること' do
      get '/mypage'
      expect(response.body).to include 'お探しのページは見つかりませんでした。'
    end
  end
end
      
```

before do を用いることで上記のようにログイン処理をまとめることができます。
また、今回はログインしている場合としていない場合でテストを分けているため'ログインしている場合'のcontext内に書いてますが、describe直下に書くことで'ログインしていない場合'のcontextにも反映させることができます。
 

# 参考資料
https://qiita.com/jnchito/items/42193d066bd61c740612
AWS認定資格　無料WEB問題集解説(一部)

### 問題29 クラウド導入をサポートするサービス
- [AWS認定資格 無料WEB問題集＆徹底解説](https://aws-exam.net/clf/clf_q.php?q_id=29&q_list_flg=1)から引用

お客様は、AWSクラウドで新しいワークロードを設計して構築したいが、AWS関連のソフトウェア技術の専門知識を社内に持っていない。 次のAWSプログラムのうち、お客様がその結果を達成するために利用できるサービスはどれですか？
- AWSパートナーネットワーク(APN)
AWSの世界的なパートナープログラムです。APNパートナーがAWSベースのビジネスやソリューションの構築に成功するよう支援することに重点を置いています
https://www.fenet.jp/aws/column/aws-beginner/262/
- AWSコンサルティングパートナー
ビジネス構築リソース、プログラム、マーケティングリソース、資金調達のためのリソースがコンサルティングパートナーの利点です。レジスタード、セレクト、アドバンスト、プレミアというtierあり。https://aws.amazon.com/jp/blogs/psa/aws-premier-nhntechorus/
 - AWSテクノロジーパートナー  
ビジネス構築のための様々なリソースにアクセスできます。加えて、様々なプログラム、マーケティングリソース、Fundingリソースも、セレクトティア以上で利用可能
- AWSマーケットプレイス
AWS 上で実行されるソフトウェアやサービスを見つけて購入し、すぐに使用を開始することができるオンラインソフトウェアストア 　
    - https://aws.amazon.com/jp/blogs/news/additional-pricing-options-for-aws-marketplace-products/
    - https://aws.amazon.com/jp/partners/aws-marketplace/
- AWSサービスカタログ
AWS での使用が承認された IT サービスのカタログを作成および管理
no such identity: key-name.pem: No such file or directory

# 事象 : 接続がなぜか切れたら、その後鍵があるのにないって言われた

```bash
# ポートフォワードしながら多段階接続したらぶちっと接続が切れた
$ ssh old-host
Enter passphrase for key '/path/to/fumidai.pem': 
Enter passphrase for key '/path/to/id_rsa': 
sign_and_send_pubkey: no mutual signature supported
ponsuke@10.0.x.xx`s password:
[ponsuke@ip-10-0-x-xx ~]$ # ここでぶちっと接続が切れた

# 再び接続しようとするとなぜか鍵がないって言われ始めた
$ ssh old-host
no such identity: /path/to/fumidai.pem: No such file or directory
ponsuke@10.0.x.xx: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

# 鍵はあるのに
$ ls -la /path/to | grep fumidai
-rw-r--r-- 1 ponsuke 1049089 1743  2月 18 18:00 fumidai.pem
```

## 原因 : 不明
いろいろ試行錯誤したがさっぱりだった。

1. やったこと
    1. 鍵の場所を.sshに変えてみる
        - 参考 : [SSHにてAWSにあるインスタンスにログインできない時の対処法 - Qiita](https://qiita.com/vinaka/items/1d8b20fdf8a9234bc86d)
    1. 鍵の権限を変えてみる
        - 参考 : [SSHでUNPROTECTED PRIVATE KEY FILEと言われたら](https://note.affi-sapo-sv.com/ssh-unprotected-key.php)
    1. known_hostsにある接続先の情報を消してみる
        - 参考 : [WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! - Qiita](https://qiita.com/ponsuke0531/items/9497895fe9217e5b40b3)

### 対応 : 鍵を.ssh配下に移動してknown_hostsを消す
試行錯誤の中で偶然できた。

1. 鍵を.ssh配下に移動する
1. .ssh/configの鍵のパスを変える
1. .ssh/known_hostsを消す(ホストを選んだりせずファイルごと削除してみた)

```bash
# 鍵を.ssh配下に移動して、.ssh/configの鍵のパスを変える
$ cp /path/to/fumidai.pem ~/.ssh/
$ cp /path/to/id_rsa ~/.ssh/
# .ssh/known_hostsを消す
$ rm  ~/.ssh/known_hosts
# もう一回接続してみる
$ ssh old-host
Enter passphrase for key 'fumidai.pem': 
Enter passphrase for key 'id_rsa': 
Last login: Mon Feb 21 09:20:36 2022 from 10.0.3.182
[ponsuke@ip-10-0-x-xx ~]$ 
```

やっと接続できて気が付いたことは、`sign_and_send_pubkey: no mutual signature supported`というメッセージが出なくなったこと。
[configにHostkeyAlgorithmsオプションを追加しても出力され続けたのに](https://qiita.com/ponsuke0531/items/6c522c56cce6b86e72ad)、なんかこの辺に原因があるのかしら？
scriptタグの書き方【同期的読み込みと非同期的読み込み】

以下のコードを実行するとコンソールには何が表示されるでしょうか？

```html
<head>
  <script src="index.js"></script>
</head>
<body>
  <h1 id="test">script実行</h1>
</body>
```

```js:index.js
const test = document.getElementById('test');
console.log(test);
```

結果は`null`です。

DOM構築はHTMLの記述順に行われます。

その間にscriptタグを見つけるとDOM構築を一旦停止し、設定されたJavaScriptファイルのダウンロードと実行を行います。

JavaScriptの実行後にDOM構築を再開します。

つまり上記のコードでは、index.jsを実行した時点で、まだ`id=“test”`のh1タグは構築されていないため取得ができず`null`になる、ということです。

また、DOM構築を停止するということは、利用者はその間コンテンツを見ることができません。

今回はこれを回避するscriptタグの書き方を紹介します。

## 同期的読み込み（bodyタグ内）

```html
<body>
  <h1 id="test">script実行</h1>
  <script src="index.js"></script>
</body>
```

headタグ内ではなく、`</body>`の直前に記述する方法です。

DOM構築が終わったあとでJavaScriptのダウンロードと実行が行われるため、要素の取得ができます。

ただ、全てのDOM構築が完了してからJavaScriptのダウンロードを開始するため、実行までに時間がかかるかもしれません。

表示速度も考慮するなら次の非同期的読み込みが有効です。

## 非同期的読み込み（async属性）

```html
<head>
  <script src="index.js" async></script>
</head>
```

scriptタグにasync属性を付与する方法です。

async属性を付与すると、非同期にJavaScriptファイルのダウンロードと実行をします。

つまりDOM構築を停止せずに、並行してダウンロードします。

実行のタイミングはダウンロード完了後すぐです。

そのため、async属性付きのscriptタグが複数あった場合、scriptタグの記述順に実行されるわけではありません。

## 非同期的読み込み（defer属性）

```html
<head>
  <script src="index.js" defer></script>
</head>
```

scriptタグにdefer属性を付与する方法です。

async属性と同じく非同期にJavaScriptファイルのダウンロードと実行をするため、DOM構築を停止しません。

async属性との違いは以下が挙げられます。

- 実行タイミングはDOMContentLoadedイベントの直前（DOM構築が完了したとき）
- defer属性付きのscriptタグが複数あった場合、scriptタグの記述順に実行される

## まとめ

以上の書き方であれば、意図通りに要素の取得ができます。

ただ、表示速度のことを考慮するとDOM構築と並行してJavaScriptのダウンロードができる非同期的読み込みが良さそうです。

非同期的読み込みのasync属性とdefer属性の違いとして、実行タイミングが挙げられます。

async属性の場合、ダウンロード後すぐに実行されます。

一方defer属性の場合、必ずDOM構築完了後、scriptタグの記述順に実行されるため扱いやすいと思います。
Discord.js(v13系)で指定チャンネルにメッセージ送信メモ


呼びかけてリプライさせるなどではなく、別のプログラムから

Webhook使う形でも良いのですが、毎回Webhook URLを発行するのも手間になってきたので、調べてみました。

## client.channels.cache.get().send()


こちらの記事にある記載で出来ました。

https://scrapbox.io/discordjs-japan/%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E3%81%AB%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E9%80%81%E4%BF%A1%E3%81%97%E3%81%9F%E3%81%84


`client.channels.cache.get('送信するチャンネルのID').send('メッセージ')`

なかなか長いメソッド

## 送信のサンプルコード

以前書いたメモのサンプルコードがそのまま使えました。

https://qiita.com/n0bisuke/items/ba3ed0a43642a966f635

```js
const { Client ,Intents } = require('discord.js');

const client = new Client({
    intents: [
        Intents.FLAGS.GUILDS,
        Intents.FLAGS.GUILD_MESSAGES
    ]
});

client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});

client.on('messageCreate', message => {
    if (message.content === 'ping') {
        message.channel.send('ぽんぐ!');
        
        //ID指定でメッセージ送信
        client.channels.cache.get('yyyyyyyyyyyyyyyyyy').send('指定チャンネルにメッセージ送信!!!');
    }
});

client.login(`アクセストークン`);

```

## チャンネルのIDはどこで調べるのか

チャンネルIDは数字になります。

Web版のDiscordだと部屋ごとでURLが表示されていると思うので、以下のようなURL形式の1番末尾部分が
IDとなります。

> `https://discord.com/channels/xxxxxxxxxxxxxxxx/yyyyyyyyyyyyyyyyyy` 

`yyyyyyyyyyyyyyyyyy`の箇所がチャンネルIDになります。

また、メニューから取得も可能です。

> ![スクリーンショット 2022-02-21 18.55.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/5759ce5b-584a-0de5-d26e-52adb4570cd2.png "スクリーンショット 2022-02-21 18.55.34.png")

チャンネルの名前のあたりからコンテキストメニューを開くと`IDをコピー`という項目もあるのでここからコピーもできます。


## 試す

二つ以上のチャンネルにボットを入れて試すと分かりやすいです。

- STEP1. 片方のチャンネル(IDを指定してない方)でpingを投げます。

> ![スクリーンショット 2022-02-21 19.00.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/8a52ad5e-7c55-64dc-1f16-10450b975c6e.png "スクリーンショット 2022-02-21 19.00.42.png")

- STEP2. もう片方のチャンネル(IDを指定した方)にボットが投稿します。

> ![スクリーンショット 2022-02-21 18.59.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/eb8e5f6c-40f8-da6c-eaed-c967af9ccfc4.png "スクリーンショット 2022-02-21 18.59.50.png")


これでリプライではなく指定チャンネルに送信出来ていることが確認出来ました！

特に詰まらずに成功 :)
【WP】記事投稿時、別のカスタム投稿を自動生成する

カスタム投稿で、記事投稿時に別のカスタム投稿記事を自動生成したかったのでwp_insert_postを使って自動化してみた。

## 環境
WordPress： 5.9
PHP：7.4
MySQL：5.7.32


## サンプルコード
 	// 投稿オブジェクトを作成
	function add_diary(){

		// 投稿オブジェクトのパーマリンクを取得
		$recent = get_posts('post_type=diary');
		if (count($recent) > 0) {
			$recent_id = $recent[0]->ID;
			$recent_url = get_permalink($recent_id);
			echo $recent_url;
		}
		// 投稿パラメータを指定
		$my_post = array(
			'post_title'    => '「日記」を更新しました',
			'post_type'    	=> 'news',
			'post_content'  => $recent_url,
			'post_status'   => 'publish',
			'post_name' => 'news-'.$recent_id,
			'post_author'   => 1,
			'post_category' => array(8,39)
		);
		// 投稿をデータベースへ追加
		wp_insert_post( $my_post );
	}
	add_action('publish_diary', 'add_diary' ,10, 1);

## 解説

#### 1、カスタム投稿に記事が保存されたタイミングをフックとして自動投稿を実行。

```
add_action('publish_diary', 'add_diary' ,10, 1);
```
※publish_〇〇にしないとカスタム投稿タイプ認識してもらえないので注意

#### 2、最近の投稿からパーマリンク、記事IDを取得

```
		// 投稿オブジェクトのパーマリンクを取得
		$recent = get_posts('post_type=diary');  //カスタム投稿タイプdiary指定
		if (count($recent) > 0) {
			$recent_id = $recent[0]->ID;
			$recent_url = get_permalink($recent_id);
			echo $recent_url;
		}
```

#### 3、自動生成する記事の基本設定

```
    // 投稿パラメータを指定
    $my_post = array(
        'post_title'    => '「日記」を更新しました',  //投稿記事タイトル
        'post_type'     => 'news', //カスタム投稿タイプ、
        'post_content'  => $recent_url, //記事内容 (リンクさせたい投稿のパーマリンクを出力)
        'post_status'   => 'publish', 
        'post_name' => 'news-'.$recent_id, //投稿記事のスラッグ
        'post_author'   => 1,
        'post_category' => array(8,39)
    );
```
※wp_insert_post()で自動生成した記事のスラッグが何故か全部0になって前の記事・次の記事が機能しなかったのでとりあえずスラッグを変更で対策。

#### 4、投稿をデータベースへ追加


```
wp_insert_post( $my_post );
```


### ひとりごと
カスタムフィールドで紐付け出来ないか試したけど、これが結局一番早かった


### 参考
https://www.mt-megami.com/article/460955029
https://lpeg.info/wordpress/new_post.html#m3

https://webdrawer.net/wordpress/wp_insert_post.html
特徴量の選択について勉強したことをまとめてみた

# 分散（variance）が小さい特徴量の削除
分散がかなり小さい場合、その特徴量は一定に近いため、どのモデルに対しても特徴量として加える価値がない（まあ、当たり前）。scikit-learnに実装されている`VarianceThreshold`で実現できる。

[公式チュートリアル](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.VarianceThreshold.html#sklearn.feature_selection.VarianceThreshold)

```python
import pandas as pd
from sklearn.feature_selection import VarianceThreshold
 
# データフレームの作成
data = pd.DataFrame([[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]],
                     columns=['col_0', 'col_1', 'col_2'])
 
sel = VarianceThreshold(threshold=(0.8 * (1 - 0.8))) # threshold: 閾値
sel.fit_transform(data)
mask = sel.get_support() # 各特徴量を選択したかどうか（True/False）
 
print(data.columns) # Index(['col_0', 'col_1', 'col_2'], dtype='object')
print(mask) # [False  True  True]
```

# 相関の高い特徴量を削除
異なる量的変数間の相関を計算するには、ピアソンの相関係数（Pearson correlation）やスピアマンの順位相関係数が使える。相関係数はnumpyの`corrcoef()`関数、スピアマンの順位相関係数はscipy.statsモジュールの`spearmanr()`関数が使える。pandasの`corr()`関数を使う方法も便利である。

# 単変量特徴量選択（univariate feature selection）
単変量特徴量選択とは、与えられた目的変数の情報を用いて各特徴量を評価することである。**相互情報量（Mutual information）**、**分散分析（ANOVA F-test）**、**カイ二乗検定（chi2）** などが最も一般的な方法である。scikit-learnで使用するには2つの方法がある。

`sklearn.feature_selection.SelectKBest`：上位k個の特徴量を保持。
[公式チュートリアル](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html)

`sklearn.feature_selection.SelectPercentile`：ユーザが指定した割合で上位の特徴量を保持。
[公式チュートリアル](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectPercentile.html)

カイ二乗検定を使用できるのは、非負の性質をもつデータに限られる。

[scikit-learnで単変量特徴量選択をやってみた](https://qiita.com/insilicomab/items/d844db7c507bdf3953d6)

# 貪欲法（greedy feature selection）
最初にモデル・損失・評価指標を選択する。また、「有望な特徴量」の空のリストを作成しておく。その後、各特徴量を繰り返し評価し、損失や評価指標が改善された場合に「有望な特徴量」のリストに追加していく。この手法では、特徴量を評価するたびにモデルを学習する。そのため、計算量が大きくなり、特徴量選択に時間がかかる。

# 再帰的特徴量削減（recursive feature elimination, RFE）
すべての特徴量から始めて、反復するたびにモデルに最も寄与しない特徴量を1つずつ削除していく。各反復で特徴量の重要度が小さく、係数が0に近い特徴量を除去する。ロジスティック回帰のようなモデルを二値分類に使用する場合、特徴量の係数は正のクラスにとって重要であれば正、負のクラスにとって重要であれば負の値になる。scikit-learnで提供されている`sklearn.feature_selection.RFE`で実装が利用できる。

`sklearn.feature_selection.RFE`：[公式チュートリアル](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html)

# 組み込み法（モデルベース）
モデルから出力される特徴量の係数や重要度を用いて一挙に特徴量を選ぶことができる。ランダムフォレスト・XGBoost・LightGBM・CatBoostのようなモデルから特徴量の重要度を可視化し、選択することができる。scikit-learnには`SelectFromModel`クラスがあり、与えられたモデルで直接特徴量を選べる。

`sklearn.feature_selection.SelectFromModel`：[公式チュートリアル](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectFromModel.html)


# L1（Lasso）罰則項付きモデルを使った特徴量選択
正規化にL1罰則項を用いた場合、ほとんどの係数は0（または0に近い値）となり、0ではない係数を持つ特徴量を選択する。


# 参考
[scikit-learn: Feature selection](https://scikit-learn.org/stable/modules/feature_selection.html)
[Kaggleで勝つデータ分析の技術](https://gihyo.jp/book/2019/978-4-297-10843-4)
[Kaggle Grandmasterに学ぶ機械学習 実践アプローチ](https://book.mynavi.jp/ec/products/detail/id=123641)
lower_bound

lower_bound(a+l, a+r, x) - a　　(a は 小さい順にソートされている配列)

l≤i≤r−1 の中で、x≤ai となるような最小の i(ポインタ) の値を返す

計算量は O(log N)
ソートは O(N log N)
```c++
sort(a, a + N);
cout << lower_bound(a, a + N, x) - a << endl; //x以下の個数
```


ソート済みの配列に対して～以上の数が初めて現れる場所はlower_bound
ソート済みの配列に対して～より大きいの数が初めて現れる場所はupper_bound
PowerDNSをRaspberry Pi側で構築する

# 概要
[前回](https://qiita.com/amax/items/286d8f246af65f2ca0b8)の続編じゃないけど、前回はESXi上に構築したubuntu（amd64 CPU）上に各種サーバを構築しました。
今回は、Raspberry Pi4（arm64）上に構築します。
使うOSはUbuntu Server 20.04.3 LTS（Raspberry pi版）です。

# イメージの作成
前回のイメージは、amd64しか準備されていません。
そのため、ラズパイ上で構築します。
まずはgitから必要なファイルを取ってきます。

## PowerDNS本体

```
git clone https://github.com/PowerDNS/pdns.git
cd pdns
git clone https://github.com/PowerDNS/pdns-builder.git builder
```

```Dockerfile-auth```（権威サーバ） 、```Dockerfile-dnsdist```（ロードバランサ）、```Dockerfile-recursor```（リゾルバ） がそれぞれのdockerfileなので、このDockerfileをもとに```docker build```をする。

```
docker build -t pdns-recursor-master:raspi -f Dockerfile-recursor .
docker build -t pdns-auth-master:raspi -f Dockerfile-auth .
```

結構時間がかかるのでお茶でも飲みながらお待ちください。

## PowerDNSadmin

```
git clone https://github.com/PowerDNS-Admin/PowerDNS-Admin
cd PowerDNS-Admin
docker build -t powerdns-admin:raspi -f docker/Dockerfile
```

これは高速回線だったらそこまで時間かかんないと思う。

#準備
## .confファイルの作成
[前回](https://qiita.com/amax/items/286d8f246af65f2ca0b8#%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%AD%89%E3%81%AE%E4%BD%9C%E6%88%90)と同じようにpdns.conf、recursor.confを作ります。

## docker-composeの作成
これも[前回](https://qiita.com/amax/items/286d8f246af65f2ca0b8#docker-compose%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)とほとんど同じ。
imageの部分が違うのでそこだけは前項で指定したイメージ名（``` pdns-recursor-master:raspi```、```pdns-auth-master:raspi```）に変えてあげる。

## ローカルDNS（systemd-resoloved）の機能停止
前回のやり方だと、どうもうまくいかなかったので調べ直したところ、[local DNS stub listenerを停止](https://qiita.com/shora_kujira16/items/31d09b373809a5a44ae5)すれば良いことがわかったので、先人の知識をお借りして停止する。
詳細については前の人の記事を参照

```/etc/systemd/resolved.conf``` を編集して

``` /etc/systemd/resolved.conf
[Resolve]
DNSStubListener=no

```

次のコマンドを実行して止める。

```
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
```

これにより、53番ポートが使えるようになる。

## 起動以降
ここからは一緒で、```docker-compose up -d ```で起動して、名前解決のテストをやってあげれば問題なし
Webadminのやり方も一緒。

##参考
[Ubuntu 18.04 の systemd-resolved で local DNS stub listener の利用をやめる](https://qiita.com/shora_kujira16/items/31d09b373809a5a44ae5)
[PowerDNS(GitHub)のDocker-README.md](https://github.com/PowerDNS/pdns/blob/master/Docker-README.md)
EEFC(Exchange Enforced Frequency Capping)とは何か

## 概要

PrivacySandboxに含まれる、広告のフリークエンシー(FQ)制御に関するCookie廃止への代替手段。

DSP側でFQの設定をBidResponseに記述し、実際のFQ制御をSSP側で行うことでDSP側にCookieを渡さなくてもFQ制御できるようになるよという理屈。

**実験段階なので仕様が変わる可能性がかなりある**

資料

- [ads-privacy/experiments/frequency-capping at master · google/ads-privacy (github.com)](https://github.com/google/ads-privacy/tree/master/experiments/frequency-capping)

## 詳細
### 背景

Chromeの3rd party Cookie廃止でDSPがCookieを取得できなくなるため、FQ制御が行えなくなる。FQ制御が行えない(=同じ広告を何回も見させられる)事は広告主側の広告効果、ユーザー側の体験双方に悪影響を与える。

元来DSP側で行っていたFQ制御をSSP側で制御することによって、プライバシーを守りながら広告効果及びユーザー体験を保つ仕組みを用意する。

### 目的

- 扱うこと
    - ユーザー識別子を用いないFQ制御
- 扱わないこと
    - クロスデバイスのFQ制御
    - SSP間のFQ制御

### 他技術との関連性

- 3rd party Cookie, idfa/anid: 既存のFQ制御ロジック
- TURTLEDOVE: デバイス側オークションでFQについて考慮する
- PETREL: デバイス側の広告除外グループにてFQについて考慮する

### 仕組み

流れ

1. 予めDSP管理画面でFQの設定を行う
2. ユーザーがメディアを開く
3. メディアがSSPに広告をリクエストする
4. SSPがDSPにBidRequestを送る
5. BidResponseに**FQの設定を記述**して返す
6. SSP側で対象のユーザーが、DSPから返却された fcap_id に過去何度接触したかを捜索し、BidResponseのFQの記載に従って落札可否を判定する
    - DSPが指定したFQ設定を超過していた場合、オークションプライスが一番高かったとしても落札されず、次点のクリエイティブが表示される
7. 落札されたクリエイティブがメディアに返される
8. クリエイティブをユーザーが見る

BidResponse内のFQの設定

```json
”fcap”: [
  {
    "fcap_id": "kuruma-1",
    "time_unit": "WEEK",
    "time_range": 1,
    "max_imp": 10
  }
]
```

- `fcap_id`
    - FQ制御単位のID
    - 必須
    - オーダー単位でも、スケジュール単位でも、クリエイティブ単位でも、とにかくこのID単位でFQが制御される
        - IDをスケジュールやクリエイティブと定めないことで仕様の柔軟性を保っているらしい
- `time_unit`
    - FQ制御が解除される時間の単位
    - 必須
    - `["MINUTE", "DAY", "WEEK", "MONTH", "INDEFINITE"]`
- `time_range`
    - FQ制御が解除される時間の数字
    - 任意
    - time_unit: WEEK, time_range: 1 なら1週間後に制限が解除される
- `max_imp`
    - IMPが許可される最大数
    - 必須

## 個人的な感想
### 入札時にFQ超過していた場合、DSP側としては無駄撃ちになる

DSP側でFQ制御をする場合は別案件を入札することができるが、それができない。
一応FQ超過で却下された場合のために、他の入札情報をBidRequsetに含めることができるらしい

> Frequency caps are applied to each bid independently. This means that a bidder may include additional bids in their bid response if they would like an alternate bid to be considered should their frequency-capped bid be rejected.

### SSP側で落札拒否された場合の理由がDSP側に検知できないので、FQが超過しているユーザーに延々と無駄撃ちになる広告をレスポンスする

一応落札拒否理由はlurlとかで送れるが、キャップ期限とかfcap_idとかも送らないと不十分だと思う。
扱ってる案件規模にもよるがDSP/SSP双方の負荷コストが高そう。

### SSP側での制御なので、同一ユーザーであってもSSPが違うとFQが別々に制御される

例えば、SSP A経由の面とSSP B経由の面を同一ユーザーが閲覧した場合、FQ制御はSSP別々に実施されるため、10回というFQ制御をDSP側でかけた場合、合計20回表示される

ドキュメントにはDSP側でSSPごとに「7回 / 3回」と分けて設定してよと書いてある~~(わりと無茶苦茶)~~
cufftComplexに画素値を代入してみる

##概要
* OpenCV C++ で読み込んだ画像を、cufftで使用するcufftComplexに渡す

##関連記事

>* [cuFFTパラメータ編](ttps://qiita.com/toroi-ex/items/de7df7be239fc97d83b2)

##コード

```.cpp
#include <opencv2/core/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv/cv.hpp>
#include <iostream>

#include "cufft.h"

#include <string>
#include <vector>
#include <sys/stat.h>
#include "helper_cuda.h" //省いてもok　省く場合はcheckCudaErrorsは使えない

using namespace std;

int main()
{
    //画像をグレースケールで読み込み（作業がしやすいので）
 	cv::Mat img = cv::imread("test.jpg", 0);

    if(img.empty()==true){
      cout << "Error opening file" << "("<< __LINE__<< ")" << endl;
      return 0;
    }
 
    int width, height;

    width  = img.cols;	// 横幅を取得
	height = img.rows;	// 縦幅を取得
    
    // 入力
    cufftComplex *d_i_img;
    checkCudaErrors(cudaMalloc((void **)&d_i_img, sizeof(cufftComplex)*width*height));

    // 画素値代入用変数
    cufftComplex *h_complex;
    checkCudaErrors(cudaMallocHost((void **)&h_complex, sizeof(cufftComplex)*height*width));
 
    cout << "width : "<< width << " height : " << height << endl;
 
    // 画素値代入
    for( int icnt =0;  icnt<height*width; ++icnt){ 
        h_complex[icnt].x = (float)img.data[icnt];
        h_complex[icnt].y = 0.0f;
    }
    
    checkCudaErrors(cudaMemcpy(d_i_img, h_complex, sizeof(cufftComplex)*width*height, cudaMemcpyHostToDevice));
    
    // すべてのcopyが終わるまで待機
    if (cudaDeviceSynchronize() != cudaSuccess){
      cout << "Cuda error: Failed to synchronize\n";
      return 0;
    }

    cudaFree(d_i_img);
    cudaFree(h_complex);
    cout << "ok";

    return 0;
}
```

### 解説
コードの方を少し解説しますと

GPUに入力する画像の大きさ分のメモリを`cudaMalloc`で確保

```.cpp
// 入力
cufftComplex *d_i_img;
checkCudaErrors(cudaMalloc((void **)&d_i_img, sizeof(cufftComplex)*width*height));
```

画素値を代入する用のcufftComplexを`cudaMallocHost`でホスト側にメモリを確保
> * [cudaMallocHostについて](https://fareastprogramming.hatenadiary.jp/entry/2016/10/14/233842)

```.cpp
// 画素値代入用変数
cufftComplex *h_complex;
cudaMallocHost((void **)&h_complex, height * width * sizeof(cufftComplex));
```

その後画素値を代入

```.cpp
// 画素値代入
for( int icnt =0;  icnt<height*width; ++icnt){ 
    h_complex[icnt].x = (float)img.data[icnt];
    h_complex[icnt].y = 0.0f;
}
```

これで画像を使ってcufftの入力に加えたりすることができます。
SPSS Modeler ノードリファレンス 2-6 レコード追加

# 2-6 レコード追加ノード[レコード設定タブ]
<img width="200" alt="107_89レコード追加.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/c6ee9636-f533-2a15-03b5-0d11da02262c.png">

## 1.ノードの目的
複数のテーブルを縦方向に追加（アペンド）します。

## 2.解説動画（60秒）

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


## 3.クイックスタート
視聴者が閲覧したWebログデータの①上期6ヶ月分のデータに②下期6ヶ月分のデータを追加します＊データは[5.参考情報]からダウンロードできます。

①Webログ上期
![スクリーンショット 2022-02-21 14.23.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/349acae8-f67a-7f3f-ef56-21394bdd6165.png)


②Webログ下期
![スクリーンショット 2022-02-21 14.54.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/f914ea24-5aca-7d29-f2d3-f1f01d5149f4.png)

ふたつの[可変長ファイル]ノードに[レコード追加]、[ソート]。[テーブル]ノードを順番に接続します。このとき[レコード追加]には①上半期(1H）、②下半期(2H）の順で接続します。
![スクリーンショット 2022-02-21 15.02.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/ba915855-1ec6-032a-7b8f-a63e64cc0e69.png)

[レコード追加]ノードを編集し赤枠をチェックします。
![スクリーンショット 2022-02-21 15.07.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/b5db62f1-e491-592a-264d-4296b6694f60.png)

結果を見やすくするために[ソート]ノードで以下のように設定します。
![スクリーンショット 2022-02-21 15.07.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/390ddbeb-427d-48f9-4f44-97f0dec88260.png)

[テーブル]ノードを実行します。黒文字レコードの①に赤文字レコード②が追加され、[入力]フィールドにオリジナルのテーブル番号が記されています。
![スクリーンショット 2022-02-21 15.06.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/b925e7f6-d7bd-0f88-21a2-bff44dd64b28.png)
テーブルの文字色を赤くハイライトする方法はノードリファレンス6-1Tips参照

### レコード追加の注意点
#### フィールド名と順序
たとえばC1からC10といった便宜的な項目名で順序が正確に把握できていればフィールドの位置による一致も可能ですが、一般的にはフィールド名で一致させます。フィールド名後方に空白文字が含まれる等で不一致がおきますので注意します。

![スクリーンショット 2022-02-21 15.30.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/aa0af442-7f0c-33fa-069f-6d398fb8cf30.png)

#### データ型の一致
例えば①の[顧客番号]は整数で②の[顧客番号]は文字列で識別されていると以下の警告がます。あらかじめ[置換]ノードでデータ型を一致させます。
![スクリーンショット 2022-02-21 15.35.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/1648c313-b741-8fa9-3ce3-03b3ff2c0436.png)



## 4.Tips

#### フィールド比較のためのレコード追加

https://community.ibm.com/community/user/blogs/yoichiro-nishimaki/2025/01/14/modelertips16

## 5.参考情報

#### 利用データ

右クリックでリンク先を保存してください。

①Webログ上期

https://raw.githubusercontent.com/yoichiro0903n/blue/main/WebLog2021_1H_mac.csv

②Webログ下期

https://raw.githubusercontent.com/yoichiro0903n/blue/main/WebLog2021_2H_mac.csv

#### ノードのヘルプ

https://www.ibm.com/docs/ja/spss-modeler/18.3.0?topic=nodes-append-node

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/items/4bf813e6cba84b2a1008

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee
Jetpack ComposeのRowで画面の幅に合わせてUIを折り返す（FlowRow）

# はじめに
Jetpack Composeで開発をしている際に下のようなレイアウトを再現したいと思いました。
![iamge](https://i.stack.imgur.com/172AX.png)
このUIでは、Rowを使用しているのですが、画面の幅がいっぱいになったら折り返して次の行からまたレイアウトしていくという、CSSで言うと``flex-box?``みたいなのを再現しようとした時に、あんまり記事がなかったので今回書いてみます。

# やり方
デフォルトでJetpack ComposeのRowでは、画面の幅以上に要素を並べようとした場合にこのようなUIになります。
![スクリーンショット 2022-02-21 17.58.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2397901/2d5a2fe3-9862-3a37-6a00-bad1802b661a.png)


コードはこんな感じです

```kotlin
@Composable
fun OnBoardScreen() {
    Surface(modifier = Modifier.fillMaxSize()) {
        Text(text = "Demo", style = MaterialTheme.typography.h1)
        Row(
            modifier = Modifier.fillMaxSize(),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Card(
                modifier = Modifier.size(150.dp),
                backgroundColor = MaterialTheme.colors.primary
            ) {
                Text(text = "Hello")
            }
            Card(
                modifier = Modifier.size(150.dp),
                backgroundColor = MaterialTheme.colors.secondary
            ) {
                Text(text = "Hello")
            }
            Card(
                modifier = Modifier.size(150.dp),
                backgroundColor = MaterialTheme.colors.error
            ) {
                Text(text = "Hello")
            }
            Card(
                modifier = Modifier.size(150.dp),
                backgroundColor = MaterialTheme.colors.secondaryVariant
            ) {
                Text(text = "Hello")
            }
            Card(
                modifier = Modifier.size(150.dp),
                backgroundColor = MaterialTheme.colors.onSurface
            ) {
                Text(text = "Hello")
            }
        }
    }
}
```

ごく簡単なコードで、サイズが``150.dp``の``Card``を5つ表示しています。
しかし、``Row``では、3つ目の半分までしか表示されません。
画面の幅を超えてしまったからですね。

これを画面の幅を超えてしまった場合に折り返すようにするためには、*``FlowRow``*を使います。

使い方はシンプルです。
まずは、``build.gradle``に下記を追加します。

```kotlin:build.gradle
dependencies {
    .....
    implementation "com.google.accompanist:accompanist-flowlayout:0.24.2-alpha"
}
```

これで、*``FlowRow``*が使えるようになりました。
早速使ってみましょう！

```kotlin:MainActivity.kt
// さっきのコードのRowの部分をFlowRowに変える
FlowRow(
            modifier = Modifier.fillMaxSize(),
            mainAxisSpacing = 12.dp,
            mainAxisAlignment = MainAxisAlignment.Center,
            crossAxisSpacing = 5.dp,
        ) {
........
}
```
- ``mainAxisSpacing``で、要素間の横のスペースを定義しています。
- ``mainAxisAlignment``で、要素を真ん中に寄せています。
- ``crossAxisSpacing``で、要素間の縦のスペースを定義しています。


UIはこうなります。
![スクリーンショット 2022-02-21 18.11.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2397901/4cdfd228-0a67-a686-dfd2-7b6dbb04e66e.png)

さっきみたいに画面の外にはみ出さずに、折り返して全ての要素が表示されていますね。
これで、完成です！

# 最後に
Rowを使って、多くの要素を横並びに表示したいときに、役に立つのでぜひ使ってみてください！

### 参考
https://stackoverflow.com/questions/61022452/any-ideas-on-the-list-with-fit-wrap-width-on-item-in-jetpack-compose
Azure Bastionファイル転送機能をProxy環境下で試してみた

Azure Bastionではこれまでテキストのコピペはできたものの、ファイル転送はサポートされていませんでした。
個人的にはセキュリティの観点から、この仕様はよいなと思っていたのですが、最近ファイル転送機能がプレビューリリースされたので、Proxy環境下でも利用できるのか試してみました。
[Azure Bastion now supports file transfer via the native client
](https://azure.microsoft.com/en-us/updates/azure-bastion-file-transfer-native-client/)

# 結論
* Proxy環境下でも利用可能でした。ネイティブクライアントはmstscやTeratermを使うことになりますが、内部的には443でアクセスし3389をトンネリングするような動きになっているようです。

# Azure Bastion作成
* [公式ページ](https://docs.microsoft.com/ja-jp/azure/bastion/tutorial-create-host-portal#createhost) を参考にAzure Bastionを作成します。

* 作成されたら[構成]を選択し、[ネイティブクライアントサポート]にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/feb6163a-79a1-0e87-4dc5-ad4eb2eb470e.png)

* Basic SKUだと使えないため、Standard SKUに変更します。
    * StandardからBasicへのダウングレードはできないため注意

# Azure CLIバージョンアップ
* 最新のAzure CLIが必要なため、ローカルの[Azure CLIをバージョンアップ](https://docs.microsoft.com/ja-jp/cli/azure/update-azure-cli)します。

# RDP接続
* ローカルPCでAzureアカウントにログインします。

```
$ az login
$ az account list -o table
$ az account set --subscription [SubscriptionId]
```

* mstscを使ったRDP接続をします。[VMResourceId]は、Azure Portalから接続したいVMの[プロパティ]→[リソースID]の順にコピーしてもってきます。

```
$ az network bastion rdp --name [BastionName] --resource-group  [ResourceGroupName] --target-resource-id [VMResourceId]
```

* コマンド実行すると見慣れたmstscが起動します。いつものRDP接続なので、ファイルコピペでダウンロード/アップロード可能です。

# SSH接続
* ローカルPCでAzureアカウントにログインします。

```
$ az login
$ az account list -o table
$ az account set --subscription [SubscriptionId]
```

* ローカルPCからターゲットVMにトンネルをはります。ローカルのポートは使っていない任意のポートを指定します。例えば55555とか。

```
$ az network bastion tunnel --name [BastionName] --resource-group [ResourceGroupName] --target-resource-id [VMResourceId] --resource-port "22" --port [任意のローカルポート]
```

* コマンドプロンプトが返ってこずにトンネルがはられた状態になります。この状態でTeratermなどのSSHクライアントを立ち上げて接続します。接続先はlocalhost、ポートは事前に指定した任意のポート番号を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/46e3f99e-bbfe-d455-d7df-a1129f2c4b12.png)

* あとはTeratermの機能でファイルのダウンロード/アップロード可能です。

以上
javaScript96_DOM操作

UdemyでJavaScriptをとことんやってみよう【超初心者から脱初心者へレベルアップ】の講座を勉強しているため、メモをこちらに残します。

DOMを操作するには、よくある流れは以下になります。
1. 変化させたい要素を指定する。
2. イベントを指定する。
3. 処理を書く。

イベントを指定する方法は下記３つあります。
**イベントハンドラー（関数）** (イベントは一つのみ指定できる）
1. <button onclick="function()">
2. document.getElementById().onclick = function();

**イベントリスナー**(イベントは追加、複数登録できる）
3. el.addEventListener('click',callback);

1つ目の例：(HTMLタグの中で設定する）
```ruby
<body>
   <div id="test"></div>
   <button id="target" onclick="change_color()">押してね</button>
   
<script>
    function change_color(){
        const test = document.getElementById("test");
        test.textContent = "テスト";
    }
</script>
</body>
```

２つ目の例：（JSのelement値の中で設定）
```ruby
<body>
   <div id="test"></div>
   <button id="target">押してね</button>
   
<script>
    // JSの中で設定する　element.onclick
    document.getElementById("target").onclick = function(){
        const test = document.getElementById("test");
        test.textContent = "テスト";

    }

</script>
</body>
```

3つ目の例：　イベントリスナー
```ruby
<body>
   <div id="test"></div>
   <button id="target">押してね</button>
   
<script>

    // イベントリスナー
    const target = document.getElementById("target");
    target.addEventListener('click',()=>{
        const test = document.getElementById("test");
        test.textContent = "テスト";
    });

</script>
</body>
```

引数にeを入れると、eventオブジェクトを取得することができる
```ruby
  target.addEventListener('click',(e) => {
        console.log(e);
```

**ハブリングを止める(イベントの連鎖を止める）には**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/6ccdadf0-232e-c997-e35b-f1dad4a396f8.png)

e.stopPropagation();
```ruby
<body>
  <div id="div1">div1
    <div id="div2">div2
        <div id="div3">div3</div>
    </div>
  </div>
<script>
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
const div3 = document.getElementById("div3");

div1.addEventListener('click',()=>alert("div1です"));
div2.addEventListener('click',()=>alert("div2です"));
div3.addEventListener('click',(e)=>
// e.stopPropagation(); イベントの伝播を止める
{   e.stopPropagation();
    alert("div3です")
});
</script>
</body>
```

(aタグ）リンクが実行されるのを止める
e.prevendDefault();

DOMの中に要素の子ノードを取得するには、
下記４つの方法があります。
1. getElementsByTagName()
   -メソッド：　指定ノードの子ノードを返す
2. childNodes
   -属性：　現在ノードの全ての子ノード
3. firstChild
   -属性：　現在ノードの初めての子ノード
4. lastChild
   -属性：　現在ノードの最後の子ノード

1. getElementsByTagName()
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/e8432bc0-37dd-dac8-42bd-7d321e641b34.png)
2. childNodes
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/3c1adbc8-28fb-bfee-f768-4e954edf1764.png)
3. firstChild,lastChild属性
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/f003a8d9-8f2b-0efd-d451-c0c601d81951.png)
hmtlファイル：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/73aeb0b0-c01d-c952-4e6d-f0a1148f91e0.png)
Pythonコーティングテクニックメモ

他のプログラミング言語の経験者がちょっとだけPythonの知識を学んで、コーティングを始めると漏れやすい便利なテクニックをちょちょいまとめました。

```memo.py
#変数の値交換
x,y = 1,2
y,x = x,y
print(x,y) #2 1

#リスト内の全項目と比較
z = 0
print(z in [x,y]) #False
print(z not in [x,y]) #True

#for文やif文でリストを定義する
list = [i for i in range(3,10) if i%2 == 0]
print(list) #[4, 6, 8]

#リストの項目数と同じ数の変数にまとめて値を代入する
str_list = ["a","b","c"]
a,b,c = str_list
print(a,b,c) #a b c

#リスト合併
str_list = [*str_list,*["d","e"]]
print(str_list) #['a', 'b', 'c', 'd', 'e']

#リストの項目の値を変数と可変長引数に代入
s,*l = str_list 
print(l) #['b', 'c', 'd', 'e']

#リスト→文字列
str = "".join(str_list)
print(str) #abcde

#for文で辞書を定義する
dict = {i:i.upper() for i in str_list}
print(dict) #{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E'}

#辞書合併
dict = {**dict, **{"f":"F","g":"G"}}
print(dict) #{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G'}

#リスト項目增殖
flg_list = [False]*5
print(flg_list) #[False, False, False, False, False]

test = [-25,3,11,11]
#リストの中の絶対値が一番小さい項目を特定
print(min(set(test), key = abs)) #3
#リストの中の数一番多い項目を特定
print(max(set(test), key = test.count)) #11
#リストの全項目と比較し、いずれの結果がTrueの場合、Trueとする
print(any(i == 11 for i in test)) #True
#リストの全項目と比較し、いずれの結果がFalseの場合、Falseとする
print(all(i == 11 for i in test)) #False

#複数のfor文をまとめて処理する
from itertools import product
tuple_i = (5,6)
tuple_j = (3,4)
tuple_k = (1,2)
print([i*j+k for i,j,k in product(tuple_i,tuple_j,tuple_k)]) #[16, 17, 21, 22, 19, 20, 25, 26]

#CounterモジュールでCounter用辞書を生成する
from collections import Counter
item_list = ["回復薬","回復薬","ハチミツ","ハチミツ","ハチミツ","回復薬グレード"]
item_counter = Counter(item_list)
print(item_counter) #Counter({'ハチミツ': 3, '回復薬': 2, '回復薬グレード': 1})
print(item_counter["ハチミツ"]) #3
print(Counter("abbcccdddd")) #Counter({'d': 4, 'c': 3, 'b': 2, 'a': 1})

#jsonファイル作成
import json
pokemon_data = {"Name":"Garchomp","No":"445","Type1":"Dragon","Type2":"Ground",
"Stats":{"HP":108,"Atk":130,"Def":95,"Sp.Atk":80,"Sp.Def":85,"Speed":102}}
print(json.dumps(pokemon_data, indent=2)) #インデント設置
#実行結果↓
'''
{
  "Name": "Garchomp",
  "No": "445",
  "Type1": "Dragon",
  "Type2": "Ground",
  "Stats": {
    "HP": 108,
    "Atk": 130,
    "Def": 95,
    "Sp.Atk": 80,
    "Sp.Def": 85,
    "Speed": 102
  }
}
'''
#再帰の書き方（階乗）
def factorial(n):
    return 1 if n == 1 else n*factorial(n-1)
    
print(factorial(4)) #24
```
A Philosophy of Software Design読書メモ Chapter 1., 2.

"A Philosophy of Software Design 2nd Edition" の輪読会に参加した。その際の読書メモを記録として残す。

本は以下。
https://www.amazon.co.jp/dp/B09B8LFKQL/

注意: Kindleで買うと自動的に2nd Editionになるが、ペーパーバックを選ぶと版が指定できないように思うので、紙で読みたい人は注意が必要。

# 序文

> The most fundamental problem in computer science is problem decomposition: how to take a complex problem and divide it up into pieces that can be solved independently.

Computer Scienceでの一番基礎の問題は、問題の分割。

ソフトウェアデザインはそもそも教えられるのか疑問に思ってた。そこで実際にStanford大のCS190で教えてみた。
自分の経験からくるものを教えてる。すべての原則がわかってるわけではない。

# 1. Introduction

複雑性にどう対処するか。

大きく２つ方法がある。
1. コードを簡潔にする
2. カプセル化する - モジュラーデザインと呼ばれる

ソフトウェアは変更しやすいので、建物とか船のような物理的なものとは違うんだけど、それらを作る手法が取り入れられてきた。
ウォーターフォールとか。ソフトウェアだとウォーターフォールはめったにうまく行かない。
その理由：
　・ソフトウェアは物理より複雑で完全に設計できないので、初期設計から設計に変更がない手法はと難しい

だからインクリメンタルな手法が使われてる。アジャイル開発とか。
インクリメンタル開発が意味するのは、ソフトウェアデザインには完了がないということ。

この本は２つのゴールを目指す。
1. ソフトウェアの複雑性の性質を明らかにする
2. 複雑性を小さくする開発手法を伝える



# 2. The Nature of Complexity

## 2.1. Complexity defined

> Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.

複雑性は、開発者の行動と関係してる。
たとえば、めっちゃ複雑な部分があるけど、そこを開発者が修正することがめったにないなら、システム全体として複雑性は低い。

つまり、システム全体の複雑性Cは

C = SUM(Cp * Tp)

Cp = 複雑なコードの箇所の量
Tp = 開発者がその箇所の修正にかける時間

の総和になる。

書き手より読み手のほうが複雑性を感じやすいかも。もし読み手が複雑だなぁと思ったら、それは複雑である。


## 2.2 Symptoms of complexity

複雑性は３つの兆候がある

1. Change amplification(変更量の増加)
ある機能を変更するときに、色んな所を変更する必要がある

2. Cognitive load(認知負荷)
変更するために理解する箇所が多い。
ときどき、コード量が多いほうが、シンプルな場合がある。これは認知負荷を下げるため、シンプルに感じられる。

3. Unknown unknowns:(知らないことを知っていない)
どこを変更すればタスクが終わるかがわからない
webサイトの背景色を変更するときに、１つのcssをいじったら完了とおもいきや、特定ページで個別のcssを当ててたので、開発者はそれを探しにいかないといけない

3つの兆候のうち、unknown...が一番悪い。

知るべきことがあるんだけど、それを知る方法がないから。バグってみないとわからない。

良いデザインのゴールの一つは、"明確に(obvious)” すること。


## 2.3. Causes of complexity

2つのことが原因で複雑になる。

1. dependencies = 依存
2. obscurity = 不明瞭さ？


依存はソフトウェアの基本なので、そもそも全部なくすことはできない。
だけど、数を減らし、シンプルに、明瞭にすることはできる。


不明瞭さは、重要な情報が明確にされていないときに起こる。
だいたい不適切なドキュメントによって起こる。これはChapter 13で取り上げる。

だけどデザインの問題でもある。
もし設計がちゃんとして明確であれば、ドキュメントは少なくて済む。
Rで比率の差の検定

クロス集計したデータで、比率の差の検定をする。

参考にしたのはこちらのページ↓

https://www.pu-hiroshima.ac.jp/p/ttetsuji/R/[87]fisher-multcomp.html


データはこんな感じ。
各グループにおいて yes/no の比率に差があるか？＝グループ４は yes が多いと言えるか？を検定する。

|group|yes|no|
|:---|----|----:|
|1|0|1|
|2|2|10|
|3|1|10|
|4|4|9|


ライブラリ読み込み

```R
library(RVAideMemoire)
```

今回はデータを手入力。
Rは、シリアルにデータを入れて、マトリクスに区切るスタイル。

```R
data <- matrix( c(0,1,2,10,1,10,4,9), ncol=2,dimnames=list(c("1","2","3","4"),c("yes","no")),byrow=TRUE)
```

フィッシャーの多重比較

```R
fisher.multcomp(data)
```

 p値がマトリクス形式で出力される。
 p＜0.05になるペアが有意差ありと判定する。