OmniAICreator/Qiita-1.07M huggingface dataset:
初心者におすすめ！トップ9の思考マップソフトウェア

思维导图软件は、思考の整理やプロジェクトの計画、学習やクリエイティブな作業に非常に適しています。ここでは、中国とアメリカ市場で人気のある思维导图ソフトウェアをいくつか紹介します：

XMind（中国）：XMindは、個人やチームに適した豊富な構造とテンプレートを提供する人気の高い思维导图ツールです。クロスプラットフォームに対応しており、直感的なユーザーインターフェースと強力な機能を備えています。
Flowlist.jp（日本）：大纲ノートと思维导图ツールを組み合わせたもので、モバイル端末での使用に特に適しています。大纲の編集や思维导图の共有が可能で、豊富な思维导图テンプレートを提供しています。
MindNode（アメリカ）：MindNodeは、Appleエコシステム向けの思维导图ソフトウェアです。シンプルなインターフェースと使いやすさで知られており、初心者や迅速な構想が必要なユーザーに最適です。
MindManager（アメリカ）：MindManagerは、詳細なプロジェクト計画と管理機能を提供する包括的なプロフェッショナルな思维导图ソフトウェアです。企業ユーザーや複雑なデータ整理が必要な専門家に適しています。
Coggle（アメリカ）：Coggleは、協力機能と共有の容易さで評価されているシンプルなオンライン思维导图ツールです。個人使用やチーム協力に適しています。
MindMeister（アメリカ）：MindMeisterは、リアルタイム協力機能を提供するオンライン思维导图ツールで、チームプロジェクトやブレインストーミングに適しています。直感的なインターフェースで、使いやすいです。
幕布（中国）：幕布は、大纲ノートと思维导图を組み合わせたツールで、プロジェクト計画や会議記録に特に適しています。中国語に対応しており、中国市場に非常に適しています。
これらのソフトウェアはそれぞれ特徴がありますので、個人のニーズや好みに合わせて最適なソフトウェアを選ぶことができます。
Route53にほどよい設定のDMARCレコードを登録をする


1.以下を CloudShell に貼り付ける

```
cat <<'EOL' > set_dmarc.sh
#!/bin/bash

# 引数の確認
if [ "$#" -ne 2 ]; then
    echo "使用法: $0 [ドメイン名] [メールアドレス]"
    exit 1
fi

DOMAIN=$1
EMAIL=$2

# ホストゾーンIDの取得
HOSTED_ZONE_ID=$(aws route53 list-hosted-zones | jq -r --arg domain "$DOMAIN." '.HostedZones[] | select(.Name==$domain) | .Id')

# ホストゾーンIDが見つからない場合のエラー処理
if [ -z "$HOSTED_ZONE_ID" ]; then
    echo "ホストゾーンIDが見つかりません: $DOMAIN"
    exit 1
fi

# DMARCレコードの設定
DMARC_RECORD='"\"v=DMARC1; p=quarantine;sp=quarantine;pct=100;adkim=r; rua=mailto:'$EMAIL';\""'

aws route53 change-resource-record-sets --hosted-zone-id "$HOSTED_ZONE_ID" --change-batch "{
  \"Changes\": [{
    \"Action\": \"UPSERT\",
    \"ResourceRecordSet\": {
      \"Name\": \"_dmarc.$DOMAIN\",
      \"Type\": \"TXT\",
      \"TTL\": 300,
      \"ResourceRecords\": [{ \"Value\": $DMARC_RECORD }]
    }
  }]
}"
EOL

chmod +x set_dmarc.sh

```

2.`./set_dmarc.sh <ドメイン名> <emailアドレス>` を実行

## mailtoのドメインとホストゾーンのドメインが異なる場合

[External Destination Verification](https://dmarcian.com/what-is-external-destination-verification/?_gl=1*5vruv3*_gcl_au*MTY1ODM3MjY5NC4xNzA1MDU0Nzc4&_ga=2.57405672.393730873.1705054778-121921402.1704975869) を設定すること。

レコード名：`<上記のホストゾーンのドメイン>._report._dmarc.<mailtoのドメイン>`
値： `"v=DMARC1"`
【MySQL】複数テーブルをJOINしてUPDATEする

## はじめに

複数テーブルをJOINしてUPDATEする機会がたまにあるのですが、毎回ググってるので簡単にまとめておきます。

## 構文

```sql
UPDATE tableA AS t1
JOIN tableB AS t2 ON (t1.columnA = t2.columnB)
SET 
    columnC = value,
    columnD = value,
WHERE t1.columnA = value;
```

:::note info
JOINの後にSETを書きます。
:::

## 具体例

社員（employees）テーブルと部署（departments）テーブルがあるとします。

### ER図

```mermaid
erDiagram
    departments ||--o{ employees : "1つの部署には0人以上の社員がいる"
    departments {
        int id PK
        varchar(3) code
        varchar(100) name
    }
    employees {
        int id PK
        varchar(3) department_code FK
        varchar(100) name
        int salary
    }
```

<details><summary>Mermaid記法</summary>

```mermaid.md
erDiagram
    departments ||--o{ employees : "1つの部署には0人以上の社員がいる"
    departments {
        int id PK
        varchar(3) code
        varchar(100) name
    }
    employees {
        int id PK
        varchar(3) department_code FK
        varchar(100) name
        int salary
    }
```
</details>

<details><summary>CREATE文</summary>

```sql:create.sql
CREATE TABLE departments
(	id integer PRIMARY KEY,
	code varchar(3) NOT NULL,
	name varchar(100) NOT NULL
);

CREATE TABLE employees
(
	id integer PRIMARY KEY, 
	department_code varchar(3),
	name varchar(100) NOT NULL,
	salary integer,
	FOREIGN KEY(department_code)
	REFERENCES departments(code)
);
```
</details>

<details><summary>INSERT文</summary>

```sql:insert.sql
INSERT INTO departments
(id, code, name)
VALUES
(1, "001", "人事部"),
(2, "002", "開発部");

INSERT INTO employees
(id, department_code, name, salary) 
VALUES
(1, "001", "鈴木太郎", 300000),
(2, "001", "田中次郎", 250000),
(3, "002", "渡辺花子", 400000),
(4, "002", "佐藤洋子", 150000);
```
</details>

### UPDATE文

部署名が"開発部"の人だけ給料を20%増にします。

```sql:update.sql
UPDATE employees AS t1
JOIN departments AS t2 ON (t1.department_code = t2.code)
SET t1.salary = t1.salary * 1.2
WHERE t2.name = "開発部"
;
```

WHERE句は使用せずにON句で条件を指定することも可能です。

```sql:update.sql
UPDATE employees AS t1
JOIN departments AS t2 ON (t1.department_code = t2.code AND t2.name = "開発部")
SET t1.salary = t1.salary * 1.2
;
```

## おわりに

はじめてMermaid記法を使いましたが、簡単にER図を書けて便利でした。

## 参考

### Mermaid記法について

https://qiita.com/Qiita/items/c07f3262d8f3b25f06c9

https://zenn.dev/seiya0/articles/tech-mermaid-erd
Git で今いるブランチだけを表示／コピペする。

# 絶対もっとよい方法があるはず！

ローカルで作業をしていて今いるブランチを確認したいときや、プルリクエストをするたびにローカルブランチ名を入力するとき面倒くさい！絶対もっとよい方法があるはずと思って調べました。

# 今いるブランチだけを表示する方法

今までは自分が今いるブランチを知りたいとき、よくあるこのようなコマンドをターミナルに打っていました。

```
git branch
```
確かに今いるブランチだけ色が違うので分かりはするのですが、ローカルブランチの数が多くなるととても面倒です。

調べてみるとこれで行けることが分かりました。
```
git branch --show-current
```
これでターミナル上に今いるブランチのみが表示されます。最高です。

# 今いるブランチ名をコピペする方法

プルリクエストのためローカルブランチと同じ名前でリモートレポジトリにプッシュしたいとき今まではタイプしたりコピペしていました。時間がかかるし、タイプミスしていて最悪でした。

でもこれで行けます。

``` 
git branch --show-current | clip
git push origin (ここでペーストすると今いるブランチ名が貼りつく）
```

タイプミスからも解放されました。

参考にさせていただいた記事はこちらです。感謝しかありません。ありがとうございました！

https://qiita.com/XeO3/items/7d96b5371beda51c4f31

https://stackoverflow.com/questions/6245570/how-do-i-get-the-current-branch-name-in-git
paizaラーニング解答: ハッシュメニュー[Ruby]

# ハッシュメニュー
https://paiza.jp/works/mondai/hash_problems/problem_index?language_uid=ruby

## STEP: 1 ハッシュ関数とは
```ruby
n, mod = gets.split.map(&:to_i)
x = n.times.map { gets.to_i }
hash = x.to_h { |i| [i, i % mod] }
puts hash.values
```
xの要素に対して順番に% modするだけでも解答できますが、それをハッシュテーブルに保存してみました。

## STEP: 2 やや複雑なハッシュ
```ruby
n, a, b, mod = gets.split.map(&:to_i)
x = n.times.map { gets.to_i }
hash = x.to_h { |i| [i, (a * i + b) % mod] }
puts hash.values
```

## STEP: 3 文字列のハッシュ
```ruby
n = gets.to_i
x = n.times.map { gets.chomp }
hash = x.to_h { |i| [i, i.count('paiz')] }
puts hash.values
```

## FINAL問題 ハッシュ関数を作ってみよう
```ruby
n = gets.to_i
x = n.times.map { gets.chomp }
x.each do |s|
    hash = 0
    s.each_char.with_index do |char, i|
        hash += (i + 1) * (char.ord - 'a'.ord + 1)
    end
    puts hash % 100
end
```

## STEP: 1 ハッシュテーブル(オープンアドレス法)
```ruby
n = gets.to_i
x_array = n.times.map { gets.to_i }
table = [-1] * 10
x_array.each do |x|
    hash = x % 10
    if table[hash] == -1
        table[hash] = x
    else
        while table[hash] != -1
            hash = (hash + 1) % 10 
        end
        table[hash] = x
    end
end
puts table
```

## STEP: 2 ハッシュテーブル(チェイン法)
```ruby
n = gets.to_i
x_array = n.times.map { gets.to_i }
table = Array.new(10) { [] }
x_array.each do |x|
    hash = x % 10
    table[hash] << x
end
table.each do |row|
    puts row.join(' ')
end
```

## FINAL問題 ハッシュテーブルを使おう
```ruby
a, b = gets.split.map(&:to_i)
q = gets.to_i
table = Array.new(100) { [] }
q.times do
    k, x = gets.split.map(&:to_i)
    hash = (a * x + b) % 100

    if k == 1
        table[hash] << x
    elsif k == 2
        puts table[hash].include?(x) ? "Yes" : "No"
    end
end

table.each do |row|
    puts row.join(' ')
end
```

追記
@scivola 様よりコードの改善点をご教示いただき、コードの一部を編集いたしました。
この度はご指摘いただきありがとうございました！
誤ったブランチからのブランチ作成とその修正方法

## 困ったこと

プロジェクトにおいて、新しい機能の開発やバグ修正を行う際、通常は特定のブランチ（例えば**`feature`**ブランチや**`bugfix`**ブランチ）から新しいブランチを作成します。しかし、間違って**`develop`**ブランチから新しいブランチを作成してしまうことがあります。このような状況は、特に大きなプロジェクトや多くの開発者が関わる環境でよく発生します。

以下は、そのような状況の一例です：

```
e078d2b27c3b (HEAD -> feature/new-feature, origin/feature/new-feature) [api] fix - ...
1f217f6f5829 [api] fix - ...
c1048fc72e55 [api] fix - ...
1f7164ecb346 [api] move and add definitions to protocols belonging to area
78f3f8ec288c [api] add area code-based ranking protocol
```

## 解決方法

この問題を解決するためには、以下の手順を実行します

1. 新しいブランチの作成
正しいベースブランチ（この場合は**`feature/special-event`**）から新しいブランチを作成します。
    
    ```bash
    git checkout feature/special-event
    git pull
    git checkout -b correct-branch
    ```
    
2. 必要なコミットの適用
誤ったブランチから正しいブランチへ必要なコミットを移動します。これは**`cherry-pick`**コマンドを使用して行います。
    
    ```bash
    git cherry-pick e078d2b27c3b
    git cherry-pick 1f217f6f5829
    git cherry-pick c1048fc72e55
    # 必要なすべてのコミットに対して繰り返します
    ```
    
3. 新しいブランチをリモートにプッシュ
修正したブランチをリモートリポジトリにプッシュします。
    
    ```bash
    git push origin correct-branch
    ```
    
4. 新しいプルリクエストの作成
GitHub上で、新しいブランチから目的のベースブランチ（**`feature/special-event`**）に向けてプルリクエストを作成します。

## 図解

1. **誤ったブランチの状況**:
    
    ```
    develop
    |
    |--- commit A (78f3f8ec)
    |--- commit B (1f7164ec)
    |
    |--- feature/new-feature (誤ったブランチ)
          |
          |--- commit C (c1048fc7)
          |--- commit D (1f217f6f)
          |--- commit E (e078d2b2)
    ```
    
2. `cherry-pick`のプロセス
    
    ```
    feature/special-event (正しいベースブランチ)
    |
    |--- correct-branch (新しいブランチ)
          |
          |--- cherry-pick commit C (c1048fc7)
          |--- cherry-pick commit D (1f217f6f)
          |--- cherry-pick commit E (e078d2b2)
    ```
    
3. 最終的なブランチの状態
    
    ```
    feature/special-event
    |
    |--- correct-branch
          |
          |--- commit C (c1048fc7)
          |--- commit D (1f217f6f)
          |--- commit E (e078d2b2)
    ```
    

## **まとめ**

誤ったブランチからブランチを作成してしまった場合、**`git cherry-pick`**を使用して必要なコミットを新しい、正しいブランチに移動することで問題を解決できます。この方法は、既にいくつかの作業が行われている場合に特に有効です。Gitは非常に強力なツールであり、このような一般的な問題を解決するための複数の方法を提供しています。
Looker Studio にてページに名称を設定する方法

## 概要

Looker Studio にてページに名称を設定する方法を共有します。本手順を実施することで表示したときに、左側のウィンドウにてページ名を表示することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/23597084-d64e-b94b-5e8f-6801452baa35.png)

本手順を実施するために、２ページ以上がレポートに含まれていることが前提となるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/07d3dd59-1978-2d2d-17c8-fb5fcef9a153.png)

１ページのレポートでは、以下のようにページを選択するウィンドウは表示されないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/888cbac6-fff6-2412-482b-1145a9089523.png)

## 手順

### 1. Looker Studio にて `ページ`タブ -> `ページの管理`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/874d79bb-d133-e1f2-4ce7-8593b118b9a6.png)

### 2. `レポートのページ`ウィンドウにて名称を変更したいページを右クリック -> `名前を変更`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/94262040-010c-5fe1-a7ff-13b86e9b7cb1.png)

### 3. ページの名称を変更（例：`「作業前」のコピー`から`作業後`へ変更）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/fbca8223-58f6-0cd7-b475-54f0356c9b0b.png)

### 4. ページの名称が変更されたことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/466e8aea-32be-a6e7-8990-d648bea5c2d9.png)
Looker Studio にてページを複製する方法

## 概要

Looker Studio にてページを複製する方法を共有します。タブから複製する方法と`ページの管理`ウィンドウにて複製する方法があるようです。`ページの管理`ウィンドウは、２ページ以上ないと選択できないことに注意が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/cb0e6f64-6df1-9060-86b6-70e2f88cde22.png)

１ページしか存在しないレポートでは、`ページの管理`は非アクティブとなっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7b8da431-e93b-56a7-7d61-2bddce118cb7.png)

ページを追加することで、`ページの管理`はアクティブとなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/c8f5b5c0-04fb-e3ae-b0a5-061ec0f205df.png)


## 手順

### 1. タブから複製する手順

#### 1-1. Looker Studio にて `ページ`タブ -> `ページの複製`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/cb0e6f64-6df1-9060-86b6-70e2f88cde22.png)

### 1-2. ページが複製されたことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7789afa7-a5d3-edb1-d831-4285c8be92bc.png)

### 2. `ページの管理`にて複製する手順

#### 2-1. Looker Studio にて `ページ`タブ -> `ページの複製`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a8fd14e2-34c6-5010-25ee-9ab0455c59ca.png)

### 2-2. 右側に表示される`レポートのページ`ウィンドウにて複製対象のページを右クリック -> `複製`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/56f960f2-8f15-9537-3c7e-57d1cffe85d1.png)

### 2-3. ページが複製されたことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/c74f3f79-d9d0-247f-6711-f2a6212e5cbc.png)
Looker Studio のグラフにて罫線を非表示にする方法

## 概要

Looker Studio のグラフにて罫線を非表示にする方法を共有します。罫線自体を非表示にすることはできないようですが、罫線を透明に設定することで設定できるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/528c6699-1989-29d9-0f0c-9aa76684bac0.png)

## 設定手順

### 1. グラフを選択後、`スタイル`にある`グリッド線の色`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f0828be0-6511-b237-0f11-87030fdfb8a8.png)

### 2. `transparent`を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/67cbf63a-b158-1759-b878-0e6658c7d09a.png)

### 3. 罫線が非表示になったことを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/e806b84c-5487-e172-b38b-716d69855de1.png)
ChatGPTはどこまで会話の文脈を覚えているか

# 会話の文脈をおぼえている？
![DALL·E 2024-01-11 09.35.31 - A cartoon illustration of a human and a robot (representing ChatGPT) engaged in a long conversation. The human is animatedly talking, with many speech.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/65f4b1fc-88b8-fcb0-1814-4525437ddff6.png)

ChatGPTと会話のやりとりをしていると、疑問が湧いてくる時がある。

**「ChatGPTって会話の文脈をどこまで覚えて回答しているんだろう？」**

たとえば、

>質問(ユーザー)
↓
回答（ChatGPT）
↓
回答を踏まえた質問(ユーザー)
↓
回答を踏まえた質問に対する回答（ChatGPT）
↓
回答を踏まえた質問に対する回答を踏まえた質問(ユーザー)
↓
回答を踏まえた質問に対する回答を踏まえた質問に対する回答（ChatGPT）

と会話が長くなったとき、
最初の質問からの文脈をどこまで理解しているのか、
直前の質問に回答しているだけなのか、
覚えているとすればどこまで覚えているのか、
ということです。

最初の方は会話の流れを踏まえて回答してくれている。
しかし、途中から回答がいまひとつしっくりこなくなることもあるかもしれない。
果たして、全体の文脈を理解しているのか？

# ChatGPTが文脈を覚えられる限界量がある
**結論からいうと、ChatGPTは会話の文脈を覚えている。**
**しかし覚えられる長さには限界がある。**

どこまで覚えられるかは明確に決まっている。

|model|トークン数|日本語換算単語数|
|---|---|---|
|ChatGPT3.5（無料）|4096|約1000~4000|
|ChatGPT4（$20/month）|8000|約2000~8000単語|
|ChatTeam（$25/month）|32000|約10000~30000単語|
|ChatGPTEnterprise（有料）|128000|約40000~120000単語|

トークン数は「コンテキスト・ウィンドウ・サイズ」と呼ばれていて、
この上限を超える会話、文章は、最初の方から忘れていく。
コンピュータのメモリのようなものである。

なので、ビジネスプランの全体の設計から詳細まで扱いたい場合や、
大規模なシステムのコードの整合性をとりたい場合は、
コンテキストウィンドウサイズの大きい有料プランを検討した方が良いかもしれない。

****
ちなみに、新しいチャットスペースを開くとChatGPTは全て忘れている。
各トークルームで毎回新人が出てくるようなものである。
というのは、会話によってChatGPTモデルが学習しているわけではないからである。
あくまで、その会話ルームの中のメモリのみで文脈を追っている。






🐣
****
フリーランスエンジニアです。
AIについて色々記事を書いていますのでよかったらプロフィールを見てみてください。

もし以下のようなご要望をお持ちでしたらお気軽にご相談ください。
AIサービスを開発したい、ビジネスにAIを組み込んで効率化したい、AIを使ったスマホアプリを開発したい、
ARを使ったアプリケーションを作りたい、スマホアプリを作りたいけどどこに相談したらいいかわからない…

いずれも中間コストを省いたリーズナブルな価格でお請けできます。

お仕事のご相談はこちらまで
rockyshikoku@gmail.com

機械学習やAR技術を使ったアプリケーションを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
PyTorch / Python / CUDAのバージョンの上げ方

すぐ忘れてしまうのでメモ。

# 前提（使っているもの）

- Debian
- pyenv
- Poetry

# 1. コードの書き換え

上げたいバージョンに書き換える。
確認したいファイルは

- pyproject.toml
- .circleci.yml
- Dockerfile
- requirement.txt

あたり。

PyTorchとPythonのバージョン対応は公式サイトを確認する。

https://pytorch.org/

以下のコマンド内のバージョンは適宜変更すること。

# 2. Pythonのアップデート

pyenv-update プラグインを使うと楽ちん。

https://github.com/pyenv/pyenv-update

（brewで入れている場合はそちらから行う。）

```bash
pyenv update
```

```bash
pyenv install 3.11.4
```

```bash
pyenv local 3.11.4
```

# 3. Poetryで使う環境を変更

```bash
poetry env use 3.11
```

もし `python not found` と言われたら以下の記事の通り修正する。

https://zenn.dev/ymd/articles/68ed58e45ea275

```bash
poetry lock —no-update
```

```bash
poetry install --sync
```

依存性など問題が出たら適宜修正。

# 4. CUDA / nvidia-driver のアップデート

概ね以下の記事の通りでOKですが、うまくいかない手順があるので必要なところだけ補足。

https://e-penguiner.com/install-upgrade-specific-cuda/

CUDAの過去バージョン一覧はこちら。

https://developer.nvidia.com/cuda-toolkit-archive

## 今入っている　CUDAバージョンの確認

```bash
nvcc -V
```

## 古いCUDAの削除

`sudo apt purge cuda*` で `Unable to locate package` と出るときはコマンドの引数を

```bash
sudo apt purge 'cuda*'
```

のように `''` で囲む。
参考↓

https://askubuntu.com/questions/1026230/unable-to-locate-package-error-during-apt-get-uninstall

## バージョンは必ず指定する

元記事にも書かれていますが、 `sudo apt install` 時の **パッケージのバージョンは必ず指定しましょう** 。
間違って最新版を入れてしまったら `sudo apt purge` からやり直し。

---

ちなみにGCEでCUDA関連アンインストールした状態でログインすると、「ドライバーが入ってないのでこのスクリプト使ってインストールしてね」的なメッセージが表示される。
インストールバージョンがどうなるのかわからないので、私はいつも自分で指定して入れている。

# 5. 動作確認

プロダクトによって見る所は様々だと思いますが、私の場合は主に以下の通り。

 - 前処理が動くか
     - できるだけ事前にテストを書いておくと楽ちん
 - 学習が動くか
     - CPU / GPU 両環境で
     - できたら学習結果も良さそうか確認する
 - リリース用のコンテナに出来るか
 - リリース用のコンテナで正しく推論できるか
     - アップデート前のコンテナの結果と比較する
【Next.js】404エラーの際に外部サイトの404ページを使いたい！（App Router）

# はじめに
存在しないページにアクセスしたときに、自作の404ページではなく、外部サイトの404ページを使いたい！って方向けの記事です。

（この記事の通りに作りたい人ってどれくらいいるんですかね？）

# 実装
まず、App Routerでは存在しないURLへのアクセス時に呼び出される`not-found.tsx`というファイルがあります。
[File Conventions: not-found.js](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)

これを、appフォルダ直下（パスが「/」のpage.tsxが存在する階層）に設置します。
例）
```
.
└── app
    ├── page.tsx
    ├── layout.tsx
    └── not-found.tsx # ココ！
```

そして、`not-found.tsx`内に404ページを作っていくのですが、外部サイトのページがいい！って頑固者のために`redirect`という機能を使います。
[Functions: redirect](https://nextjs.org/docs/app/api-reference/functions/redirect)

詳しいことは公式リファレンスを見れば一発なので、実装だけ載せておきます。
``` not-found.tsx
import { redirect } from 'next/navigation';

export default function NotFound() {
  redirect(遷移したい外部サイトのURL)
}
```

これだけです。記述が少なくてシンプルですね！
[PackageManager]パッケージマネージャとは

### 環境
- Windows 10 Enterprise
***
### まとめ
- パッケージマネージャ:プログラムを便利に管理するツールであり大きく2つに分けられる。
    1. OSレベルの機能やソフトウェアパッケージを管理するもの（Ex.Linuxのyumやapt等）
    1. ソフトウェア開発時に使用する機能部品や実行部品を管理するもの（Ex.Node.jsのnpmやyarn、JavaのMaven等）
※本ページでは、後者のソフトウェア開発時に使用するパッケージマネージャについて記載する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/b107ffdf-348f-3460-2ce1-ad1f77a9df95.png)

***
### 機能
1. **パッケージ配布サーバーの公開**
    多くのOSS開発言語／フレームワークではインターネット上にリポジトリ(Webサイト)と呼ばれる配布サーバーが公開され、各種の機能を実現するOSSがパッケージとして配布されている。パッケージマネージャはネットワークを通じて必要なパッケージを取得することができる。
1. **インストール管理**
    パッケージのインストールやアンインストール、情報管理、バージョンアップ手段の提供などを行う。
    （パッケージのインストールやアンインストール時は、共通のコマンドで実施可能）
1. **依存関係の解決**
    一般的にあるパッケージを動作させるための前提条件として別のパッケージが必要となる場合が多く、これを依存関係（パッケージAの中にパッケージB,C、モジュールD,Eが存在するイメージ）と呼ぶ。パッケージマネージャはこの依存関係を管理し、必要に応じてインストールやバージョンアップなどを行う。
    パッケージマネージャーに依るが、脆弱性情報やEOL（更新期限切れ）情報の提供など、色々な機能が提供されている場合もある。

 ***
### ライブラリ/パッケージ/モジュールのイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/62c4ff1a-454c-0ffe-bf6e-5403f5129b70.png)

***
### 参考リンク
- [パッケージマネージャーとは？わかりやすい仕組みの解説～直接使っていないOSSとは～](https://www.hitachi-solutions.co.jp/sbom/sp/blog/2023033101/)
- [Pythonのモジュール、パッケージ、ライブラリ徹底解説！](https://ai-inter1.com/python-module_package_library/)
Kintoneで親と子アプリ間でフィールドの値を引き継いでレコード作成するJavaScript

# 概要

kintoneにて、ある顧客に関連する詳細データを別アプリで作りたいとき、
詳細データ用アプリで親アプリでの顧客IDや電話番号手入力での手間があるので、
ボタン1発でデータを引き継いでレコード作成できるJavaScriptを作りました。

▼イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211946/5edddc07-7e32-daf5-97d9-60a4f7112d71.png)


# 導入方法

親と子、それぞれのアプリごとに下記を導入してください。

## 親アプリには下記JavaScriptを入れます。

```javascript:parent.js
(function() {
  'use strict';

  var RELATED_APP_ID = 'your_child_app_id'; // 子アプリのIDを設定

  // レコード詳細画面が表示されたときのイベント処理
  kintone.events.on('app.record.detail.show', function(event) {
    // ボタンを検索して、既に存在する場合は何もしない
    var buttonId = 'create_related_record_button';
    if (document.getElementById(buttonId) !== null) {
      return;
    }

    // 新規関連レコード作成ボタンを作成
    var createNewRecordButton = function(text, spaceElementId, fieldCode) {
      var button = document.createElement('button');
      button.id = buttonId;
      button.innerText = text;
      button.style.marginTop = '20px';

      button.addEventListener('click', function() {
        var record = event.record;
        var fieldValue = record[fieldCode].value;
        var queryString = '?' + fieldCode + '=' + encodeURIComponent(fieldValue);
        var createRecordUrl = '/k/' + RELATED_APP_ID + '/edit' + queryString;
        window.open(createRecordUrl, '_blank');
      });

      var spaceElement = kintone.app.record.getSpaceElement(spaceElementId);
      if (spaceElement) {
        spaceElement.appendChild(button);
      }
    };

    // 新規関連レコード作成ボタン追加
    // 'your_space_element_id', 'your_field_id'は実環境にあわせて書き換えてください。
    createNewRecordButton('関連レコードの新規作成', 'your_space_element_id', 'your_field_id');
  });

})();
```

※以下は自身の環境に合う適切な値に置き換えてください。
`your_child_app_id`→子アプリのID（"30"など）
`your_space_element_id`→ボタンを設置する要素（スペース要素）のID（"space_element"など）
`your_field_id`→keyとして子アプリに連動する要素のID（'telNumber'など）


## 子アプリには下記JavaScriptを入れます。

```javascript:child.js
(function() {
  'use strict';

  // URL クエリパラメータからパラメータの値を取得する関数
  function getQueryParameter(paramName) {
    var query = window.location.search.substring(1);
    var params = query.split('&');

    for (var i = 0; i < params.length; i++) {
      var pair = params[i].split('=');
      if (decodeURIComponent(pair[0]) === paramName) {
        return decodeURIComponent(pair[1].replace(/\+/g, ' '));
      }
    }
    return null; // パラメータが存在しない場合はnullを返す
  }

  // 子アプリの新規レコード追加画面表示時にURLのクエリパラメータからフィールド値を設定
  kintone.events.on('app.record.create.show', function(event) {
    // 取得フィールド名は設定や環境変数から取得できるようにする
    var fieldName = 'your_field_name'; // 実際に設定するフィールド名に変更
    var fieldValue = getQueryParameter(fieldName);

    if (fieldValue) {
      // 取得した値をレコードの対応するフィールドにセット
      event.record[fieldName]['value'] = fieldValue;
    }

    // 変更をkintoneに反映
    return event;
  });

})();
```
※以下は自身の環境に合う適切な値に置き換えてください。
`your_field_name`→親アプリから受け取った値を入れる要素のID（"telNumber"など）

# 終わりに

じっくりとPCに向き合えるならこのような機能は不要かもしれませんが、分秒を争うコールセンターではこのような仕組みがあると情報入力に集中できるので作成してみました。
デフォルト機能のルックアップでも同じように情報を取得してフィールドを埋められるかもしれませんが、そもそもルックアップするためのkeyが最初から入っていてほしい場合などに有効かと思います。

さらに堅牢性を高めるために、子アプリのレコード画面で自動入力されたフォームを利用者が編集できないようにすると、ミスがより減るかも知れません。
Azure Static Web AppsにNextjsをAzure DevOps Pipelinesからデプロイ

# Azure Static Web Apps(SWA)とは

Freeプランでも十分使え、NextjsではプレビューステータスですがSSR,SSG,ISRも動作するというかなりいい感じなサービスです

https://azure.microsoft.com/ja-jp/pricing/details/app-service/static/#pricing

https://learn.microsoft.com/ja-jp/azure/static-web-apps/deploy-nextjs-hybrid

https://github.com/Azure/static-web-apps/discussions/1066

# SWAリソースの作成
デプロイの詳細はその他で作っておきます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476893/0f01cb6a-5128-1679-4de7-3c54ed5be70d.png)


# デプロイ

Reposで作成したブランチを任意のタイミングで手動でデプロイすることを想定しています



```pipelines.yml
trigger: none

pool:
  vmImage: ubuntu-latest

steps:
- checkout: self
  submodules: true
- task: AzureStaticWebApp@0
  inputs:
    app_location: "."
    azure_static_web_apps_api_token: $(DEPLOYMENT_TOKEN)

```

トークンは「デプロイトークンの管理」から取得し、Pipelinesの変数に設定しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/476893/d76ac551-b819-eca5-3d96-ef65ed5faad7.png)

注意が必要なのは2024/01現在SWAはNodejsの18系までしか対応していないため、package.jsonのenginesで明示的にバージョンを指定する必要があります


```json:package.json
{
  "engines": {
    "node": "18.x"
  }
}
```

デプロイにはサイズ制限があるためstandaloneモードにすることを忘れずに
```js:next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone'
}
```
[Git Hub]


# GitとGitHubの違い
## Git:
- **定義:** Gitは分散型のバージョン管理システムで、プログラムのソースコードやファイルの変更履歴を管理するためのツールです。
- **機能:** ローカルリポジトリを作成し、コミット、ブランチ、マージ、リモートリポジトリとの同期などの基本的なバージョン管理機能を提供します。
- **使いどころ:** プロジェクトの変更履歴を管理し、複数の開発者が協力して作業する際に利用されます

## **GitHub:**

- **定義:** GitHubは、Gitリポジトリをホスティングし、ソースコード管理やプロジェクトの協力的な開発を支援するウェブベースのプラットフォームです。
- **機能:** Gitリポジトリのホスティング、プルリクエスト、イシュートラッキング、コードレビュー、ウィキ、アクション（自動ビルドやテストなど）など、多様な協力ツールや機能を提供します。
- **使いどころ:** プロジェクトを管理し、複数の開発者が協力して作業するためのプラットフォームとして利用されます。オープンソースプロジェクトや企業のプライベートリポジトリをホスティングするために広く利用されています。
リモートデスクトップが繋がらなくて困った話

## 環境
* Windows 10 Pro

## 内容
VM環境でポリシーを色々弄っていたら、そのPCへリモートデスクトップ接続ができなくなってしまった。
ポリシーを弄ったことが原因とは思っていたが、一個一個試していたわけでは無いので、どれだーー？！となった。

ちなみにリモートデスクトップを接続しようとすると、
「使用できるログオンの種類(ネットワークが対話型)がシステム管理者により制限されています。サポートが必要な場合は、システム管理者かテクニカルサポートに問い合わせてください。」
だって。

ググったりして目ぼしい所を探して、原因が分かったので、メモ

* ロケーションはここ。
コンピューターの構成\Windows 設定\セキュリティ 設定\ローカルポリシー\ユーザー権利の割り当て

* ポリシーはこれ。
ネットワーク経由のアクセス拒否

これに対象ユーザーが入っていたからだった。やれやれ。
classList(add.remove)の使い方

## classList(add.remove)って何？
特定のhtml要素にcssで記述したクラスを追加、削除するJavaScriptのメソッドです！

#### 例えば下の要素に新しいクラスを付与したい時
```html:html
<div id="demo">ピーマン</div>
```
```javascript:js
<script>
const element = document.getElementById("id");
element.classList.add("green pepper");
<script>
```
```html:classList.addした後
<div id="demo" class="green pepper">ピーマン</div>
```
ロジックとして
1. div id="demo"をhtmlから取得
2. 取得したdiv idにclassList.addでgreen pepperを付与
という流れになります

#### クラスを削除するとき

```html:classList.removeする前
<div id="demo" class="greenpepper">ピーマン</div>
```
```html:js
<script>
const element = document.getElementById("demo");
element.classList.remove("greenpepper");
</script>
```
```html:class.List.removeした後
<div id="demo" class>ピーマン</div>
```
こちらのロジックは
1. div id="demo"をhtmlから取得
2. 取得したdiv idにclassList.removeでgreen pepperを削除
という流れになります

**用途例**
cssとの組み合わせでクリックした時に色を変えたり、要素を動かしたり様々な動きをつける事が可能になります！

### 最後に
簡単に要素の追加と削除が出来る便利なメソッドです！
さらにaddとremoveの動作を合わせたもっと便利なメソッドもあるのでどんどん使ってマスター出来るように一緒に頑張りましょう！


1/13 追記です。
いただいたコメントで知ったのですが使用したブラウザによってremove後の動作が異なるようです。知識不足でしたのでとても参考になりました。
Railsのparamsの基本とログでの確認方法

### Railsのparamsとは？
Ruby on Railsでの開発において、paramsは非常に重要な概念です。これは、ユーザーから送信されたデータ（フォームの入力、URLのクエリパラメータなど）をコントローラー内で取得するために使用されるメソッドです。

### paramsの基本
paramsは、ユーザーからのリクエストに含まれるパラメータを含むハッシュのようなオブジェクトです。例えば、フォームから送信されたデータやURLのクエリパラメータを取得する際に使用します。

### パラメータのログでの確認
Railsアプリケーションでは、開発中に送信されたパラメータをログで確認することができます。これはデバッグに非常に役立ちます。

1\. サーバーログの確認:
    Railsサーバーを起動し（rails s）、ユーザーからのリクエストを行います。
    ターミナルに表示されるサーバーログを確認します。

2\. パラメータの検索:
    ログ内でParameters:という部分を探します。これに続く情報が、リクエストに含まれるパラメータです。

3\. 例:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623005/acc4abd4-d1d5-7816-fc25-7df3f4e69516.png)
このログ出力では、ユーザー名とメールアドレスがパラメータとして送信されていることがわかります。
反映されないCSSについて

CSSがなかなか反映されず、htmlのスクールの課題提出に時間を要しました。
CSSが反映されない要因を共有します。


・文字がところどころ全角になっている
かな表記した後に、英数字表記に戻すのを失念していて反映されない。
大雑把な性格が出ました…


・セミコロン（；）が抜けている
よくよく見ると、抜けている場面がありました。




・{}が多い、少ない
``` CSS
.sample{
  margin : 100px;
  width : 350px;
 }}  /* ←カッコが多い */ 
 ```


・CSSの指定が被っている


・知らず知らずのうちに同じclass名にしている
CSSの指定が被ることにもなり、大変なことになりました…
特にflex-boxは使用することが多いので、知らずにclass名を同じにしていました。


上記がCSSが反映されない大きな要因でした。
一つ一つ丁寧に見ていくことって大事ですよね。
【pug】インクルードとmixin


# はじめに
pugのインクルードとmixinの使い方について

# 構造

インクルードするファイルはcomponentsディレクトリに置いてあると仮定。
今回はindex.pugから_head.pugを読み込んで使用する。
```
index.pug
components
└_head.pug
```

# インクルード方法
index.pugで_head.pugをインクルードするにはincludeを使って下記のように記載。

```
<main>
    ~
    include components/_head
    ~
</main>
```

# mixinで引数を渡す
pugではmixinを使えるので引数にテキストやJSONを渡すことができる。

受け側のpug(_head.pug)にはmixinを定義する。
```
mixin head(title, text)
    h1= title
    p= text
```
読み込み側のpugは「+」で記述する。
```
<main>
    ~
    +head('タイトル', '説明文')
    ~
</main>
```
これでheadに定義したmixinに引数が渡って指定したテキストたちが表示されるようになる！

引数にはJSONを指定することも可能!
Javascriptの基本について

Javascriptを勉強している中で、下記の点を中心に復習をしたので、自分なりにわかりやすい形で残しておきます。
・一度読んだだけでは分かりづらかった点
・考え方として他の言語に通じる（であろうと思われる）点

## Javascriptの型

・プリミティブ型…"1""2"などの数字、"Hello"などの文字列。
・オブジェクト型…プリミティブ型以外は全てオブジェクト型。
違いは、プリミティブ型は単一データで、オブジェクトは要素（プロパティ）が持てる。
また、オブジェクトに定義されている値をどうするかという手続きをメソッドという。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190651/03873235-88fb-ea77-18fe-5c04d9fa1fb6.png)
## 配列
[]で囲んだ中に保持したい値をカンマで区切ることで複数のデータを保持できる
配列には配列やオブジェクトを持つこともできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190651/b1575ac9-7df5-4d1c-f757-38af853fb4b9.png)

## オブジェクト
単一のデータではなく、１つのオブジェクトに対して複数のプロパティ（値）を持つことができる。
それぞれの要素に名前をつけて管理することができる。
「オブジェクト.プロパティ」という構造でできている。
## コンストラクタ
同じプロパティで値の違うオブジェクトを生成するときに使う関数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190651/62071035-12a9-db7e-ef4f-33bc8783075d.png)

## 関数
・引数は仮引数で定義してあとから渡しても良い
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190651/d47e423a-b787-6032-30f0-49f6fb711f2c.png)

・戻り地とは、関数の中ので処理された結果の値
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190651/407e018c-403d-a7c1-13fc-8903603b5f77.png)

・関数の中のthisとは、関数が呼び出された時にその関数が所属していたオブジェクト
Python音声処理&可視化チートシート

# 概要
音声処理関連のチートシートです。書き途中。今後随時更新予定。

# 音声の読み込み

```python
_, x = scipy.io.wavfile.read("read_audio.wav")
```

# 音声の書き出し

```python
scipy.io.wavfile.write(
    filename="write_audio.wav",
    rate=16000,
    data=x,
)
```

# 音声波形の可視化

## 1つ
```python
fig, ax = plt.subplots(figsize=(8,2))
librosa.display.waveshow(audio00, sr=16000)

ax.set_title("audio00")
ax.set_ylabel("Amplitude")

plt.tight_layout()
```
## 複数を縦に並べる
```python
row = 2
fig, ax = plt.subplots(row, 1, figsize=(8,2*row))
librosa.display.waveshow(audio00, sr=16000, ax=ax[0])
librosa.display.waveshow(audio01, sr=16000, ax=ax[1])

ax[0].set_title("audio00")
ax[1].set_title("audio01")

for a in ax:
    a.set_ylabel("Amplitude")
plt.tight_layout()
```

# 音声の埋め込み

```python
print("audio00")
IPython.display.display(IPython.display.Audio(audio00, rate=16000))

print("audio01")
IPython.display.display(IPython.display.Audio(audio01, rate=16000))
```

# 音声の対数振幅スペクトログラムとGriffin-Limアルゴリズム

## シンプルに使いたい場合

## クラスとして扱いたい場合
```python
class Converter:
    def __init__(self, ref=1.0):
        self.ref = ref

    def logamplitudespectrum(self, audio):
        frame_shift = int(16000 * 0.005)
        n_fft = 2048

        X = librosa.stft(
            audio,
            n_fft=n_fft,
            win_length=n_fft,
            hop_length=frame_shift,
            window="hann",
            center=False,
        )
        spec = np.abs(X)
        logspec = librosa.amplitude_to_db(spec, ref=self.ref)
        return logspec

    def griffinlim_and_pad(self, logspec, audio_size):
        frame_shift = int(16000 * 0.005)

        spec = librosa.db_to_amplitude(logspec, ref=self.ref)
        audio = librosa.griffinlim(spec, hop_length=frame_shift, n_iter=200)

        if audio.shape[0] < audio_size:
            num_pad = audio_size - audio.shape[0]
            num_pad_top = num_pad // 2
            num_pad_bottom = num_pad - num_pad_top
            audio = np.concatenate(
                [
                    np.zeros(num_pad_top, dtype=np.float32),
                    audio,
                    np.zeros(num_pad_bottom, dtype=np.float32),
                ]
            )
        else:
            audio = audio[:audio_size]

        return audio

converter = Converter()
```
Griffin-Limのアルゴリズムに関して、そのままだと左詰めで復元されるため適切なパディングが必要

## 対数振幅スペクトログラム

```python
logspec = converter.logamplitudespectrum(potential_adv)
fig, ax = plt.subplots(1, 1, figsize=(8, 4), sharex=True)
img = librosa.display.specshow(
    logspec,
    hop_length=int(16000 * 0.005),
    sr=16000,
    x_axis="time",
    y_axis="hz",
    ax=ax,
)
fig.colorbar(img, ax=ax, format="%+2.f dB")
ax.set_xlabel("Time [sec]")
ax.set_ylabel("Frequency [Hz]")
fig.savefig("log_amplitude_spectrogram.png")
```

## Griffin-Limのアルゴリズム
```python
audio = converter.griffinlim_and_pad(logspec, audio.shape[0])
```

# 音声の周波数変換
ボタンを押した時だけheadタグにscriptタグを追加する

```html

<button type="submit" name="sending" id="addCustomCodeBtn">
    headタグに追加する
</button>

<script>
    document.getElementById('addCustomCodeBtn').addEventListener('click', function () {
        // 新しいスクリプト要素を作成
        var newScript = document.createElement('script');

        // スクリプトの内容を設定
        newScript.textContent = ` 入れたい内容(scriptタグはいらない) `;

        // <head>内に新しいスクリプト要素を挿入
        document.head.appendChild(newScript);
    });
</script>


```
Java パフォーマンスチューニング

## 概要
JavaのパフォーマンスチューニングポイントのNoteです。

## チューニングポイント
**(1)** 可能な場合は、オブジェクト型ではなくプリミティブ型を使用して、メモリ使用量を削減します。
   - プリミティブ型は、ヒープではなくスタックに直接格納されるため、オブジェクト型よりもメモリ使用量が少なくなります。
       - スタック
          - スタックという文字の通り、実行される場合はこの領域に、格納され、終わるとここから取り除かれる。１スレッドで１つの領域が使われる。
          - 値型はここに格納される。
          - 特定のスコープのみに対して使用されるため、メモリ管理がしやすい反面、大きいデータの格納には向いていない。
          - オブジェクトのデータはポインタがここに格納され、実データは後述するヒープ領域で管理される。
      - ヒープ
          - 開始時に一定領域確保され、必要の都度アプリケーションに割り当てられる。
          - 大きいデータ、スコープがはっきりしないもの、サイズが動的なデータなどの格納に向いている。
          - 領域が動的な代わりに少し遅い。
          - クラスの実態はこの領域に実データが格納される。

<img width="400" alt="a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288854/90b0a62e-5647-ede2-a74f-9e48408fc239.png">


**(2)** String 連結の代わりに StringBuilder または StringBuffer を使用して、新しい String オブジェクトが作成されないようにします。
   - 文字列連結は連結ごとに新しい String オブジェクトを作成しますが、StringBuilder と StringBuffer は同じオブジェクトをその場で変更するため、不要なオブジェクトの作成が回避されます。

   
**(3)** 可能な場合はオブジェクトを再利用して、不要なオブジェクトの作成を回避します。
- オブジェクトの作成には、メモリの割り当てとオブジェクトの初期化が含まれるため、コストが高くなる可能性があります。オブジェクトを再利用すると、メモリ使用量が削減され、パフォーマンスが向上します。新しいオブジェクトを作成する代わりに、オブジェクトのプールを使用します。


**(4)** ジョブに適切なデータ構造を使用して、メモリ使用量を最小限に抑えます。
- データ構造が異なれば、メモリ使用量の特性も異なります。適切なデータ構造を使用すると、メモリ使用量が削減され、パフォーマンスが向上します。

**(5)** 遅延初期化を使用して、実際に必要になるまでオブジェクトを作成しないようにします。
- オブジェクトを不必要に作成すると、メモリが浪費され、パフォーマンスが低下する可能性があります。遅延初期化により、オブジェクトが実際に必要なときにのみ作成されます。ユーザーから要求された場合にのみオブジェクトを初期化する

**(6)** try-with-resources を使用して、ストリームやその他のリソースが不要になったときに適切に閉じられるようにします。これにより、メモリリークが防止されます。
- ファイル入力ストリームなどのリソースは、メモリリークを回避するために適切に閉じる必要があります。try-with-resources は、例外がスローされた場合でもリソースが閉じられるようにします。


**(7)** 変数のスコープを最小化して、メモリ使用量を削減します。たとえば、変数を可能な限り最小のスコープで宣言して、必要な期間だけメモリに保持されるようにします。


**(8)** 不必要なメモリ使用を防ぐために、静的変数の使用は避けてください。


**(9)** メモリ使用量を減らすために、スレッドが多すぎないようにします。
- スレッドの作成には、メモリ割り当てとコンテキストの切り替えが含まれるため、コストが高くなる可能性があります。スレッド プールを使用すると、メモリ使用量が削減され、パフォーマンスが向上します。


**(10)** キャッシュを使用して、メモリ使用量を減らし、パフォーマンスを向上させます。- キャッシュにより、操作の実行回数が減り、メモリ使用量が減り、パフォーマンスが向上します。


**(11)** メモリ プロファイラーを使用して、メモリ リークやその他のメモリ関連の問題を特定します。サンプルツール、VisualVM、JProfilerなど

## Note
新しいJavaバージョンでの変更は、これらのポイントが最新のメモリ改善とどの程度一致するかに影響を与える可能性がありますが、理解して使用することは重要です。
ローカル環境のMySQLに接続（Sequel Pro）


# はじめに
ローカル環境（docker）にインストールしたMySQLへ接続するのにdockerコマンドとSQLコマンドを使っていたけど、Sequel Proだと一瞬だったので備忘録。

# Sequel Proとは
Mac用のMySQLのデータをGUI操作で管理するためのフリーソフトウェアのアプリケーション。

# MySQLへ接続してみる
Sequel Proを立ち上げて接続情報を入力。
![スクリーンショット 2024-01-12 17.05.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587166/f6333c91-98f0-5751-0a67-b123d51f6a52.png)

docker環境の場合、ユーザー名やパスワードはdocker-compose.ymlに記載があるはず。

ホストはローカルの場合は「0.0.0.0」。

ポート番号はportsの「:」の左側を使用する（=外部ポート）
この場合は13306。
```
ports: - '13306:3306'
```

これだけでMySQLへ接続できた！
Sequel Proはローカル環境でMySQLへ接続したい時に便利！
本番環境は大体PHPMyAdminを使うことが多いらしい。
【PHP】MySQLに接続する方法(mysqli)

この記事では、MySQLにPHPから接続する方法、そしてSQL文を実行する方法を紹介します。

# 注意

この記事には、コード内には**おかしいコメントが**、コード外には**おかしい文章が**書いてあります。
笑わないように**十分に警戒してください**。

そして、この記事ではPHPのモジュールでmysqliが有効化(コメントアウトされていない状態)されていることを前提としています。

# やっていこう

テレレッテッテ、テレレッテッテ、テレレッテッテッテッテ！
PHPクッキングのお時間です。
今回は、「PHP」に、「MySQL」というご馳走を食べさせるために、「MySQL」を作っていこうと思います。

まずは、PHPソースを1つと、プログラムエディタを1つ用意します。
おすすめのプログラムエディタは、VSCodeです！

まず、お好みのPHPソースを用意して、以下のソースをトッピングします！

```php:接続.php

# 製品情報(接続情報)を入力

$mysqli = new mysqli("発売元(正式には接続先)", "生産者(正式にはユーザー名)", "MySQLの合言葉(正式にはパスワード)", "生地の合言葉(正式にはデーターベース名)"); 

# ソースをこぼしたらお知らせします。

if ($mysqli->connect_error) {
    die("Error: " . $mysqli->connect_error); 
}

# ここにお好みのSQLソースをかけてください(ここに実行したいSQL文を入力)

$stmt = $mysqli->prepare("SELECT isOwner FROM user WHERE username = ?");

# SQLソースの物理現象をしっかり捉えます。(プレースホルダに値をバインドしています。)

$stmt->bind_param("s", $hogehoge);

# SQLソースをかけます。

$stmt->execute();

# 完成したMySQLはこちらの冷蔵庫に入っております！(結果はこちらに入っています。)

$stmt->bind_result($intOwner);
$stmt->fetch();

# SQLソースを爆破させます。(SQL文をクローズします)

$stmt->close();

# 製品情報を隠蔽します。(接続をクローズします)

$mysqli->close();

```

完成しました！

# なぜ書こうと思ったか

個人で自作している「tCloud Next」というPHPのプログラムで、PHPからMySQLに接続する必要があったので、その方法を解説しようと思いました。

# 終わりに

PHPでMySQLを使えるようになってよかったです。
皆さんもやってみてください。

また、このプログラムはtCloud Nextというプログラムで使っています。こちらもみてみてください。

https://github.com/DiamondGotCat/tcloud

それでは、また他の記事で！
【Rails】generateコマンドでRSpecファイルを自動生成！

## はじめに

`rails generate`コマンドのデフォルトだと`minitest`のファイルが生成されますよね？
初めのうちは`RSpec`を使用するためにテストファイルの生成をスキップするのみでした。
どうにか時短できないかと探していたら、しっかり設定がありました！

:::note warn
ご覧いただきありがとうございます!
この記事の投稿者は主にRuby on Railsを学習している初学者です。
理解不足な点や内容に誤りがございましたらご指摘いただけますと幸いです。
:::

## 問題
`rails generate`コマンド実行時に不要なファイルが生成されてしまう。
個人開発中の`git add .`コマンド等で不要ファイルもGitHubにアップロードされることは避けたい。

## 解決方法
config/application.rbへの設定で`rails generate`コマンドで指定されたファイルが生成されなくなる、もしくは必要なファイルを生成するようになる。
下記はヘルパーファイルとルーティングの自動生成を停止、テストフレームワークにRSpecを使用しているため、生成されるテストファイルを変更しています。

```#app/config/application.rb

module App
  class Application < Rails::Application



    config.generators do |g|
      g.skip_routes true       # ルーティングをスキップする
      g.helper false           # ヘルパーの生成をスキップする
      g.test_framework :rspec  # RSpecのテストファイルを自動生成
    end
  end
end

```

## 終わりに
`RSpec`の雛形が自動で用意されていることでテストの記述に着手しやすくなりました！
これからも思いついた時に調べる習慣を継続していきます。

## 参考
こちらの記事を参考にさせていただきました。
ちなみに`slim`のファイルも自動生成できるようです。
https://himakuro.com/rails-generator-config-guide
dynamics 365のsales Copilotを有効化手順

##### dynamics 365のsales Copilotを有効化　[参考URL](https://learn.microsoft.com/en-us/dynamics365/sales/enable-setup-copilot)

- 先ずDynamics365テスト環境作成、そしてsales hub appに入る、
左下App Settling画面に入る、「Copilot」設定画面を開き、
「Try our newest preview features before they're rolled out to everyone」チェックを入れ、目標APPにCopilot有効化、「publish」をクリック 
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/92198d19-a13f-5dcd-0ac9-f2362597e429.png)

- Copilotの使用により、データが他regionへの遷移が発生する場合がある、sales APPが所属する環境（https://admin.powerplatform.microsoft.com/ ）に入り、「Generative AI features」の項目をONになるを確認する　[参考URL](https://learn.microsoft.com/en-us/power-platform/admin/geographical-availability-copilot#enable-data-movement-across-regions)
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/fc806dad-2662-91ff-baec-f08d9bace0f5.png)

- 有効かした後、「App Setting」画面には「Copilot」タブが表示される、leadとopportunity tableがCopilot処理対象になる
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/b44acaac-a542-e11b-43ae-c108bbcd1071.png)

- leadとopportunity table其々最小4個、最大10個のfiledを選択出来る。
Coplilotはこれらのfieldを分析し、 [要約（summarize）](https://learn.microsoft.com/en-us/dynamics365/sales/copilot-get-information#summarize-a-lead-or-opportunity) と [最近の変更（recent changes）](https://learn.microsoft.com/en-us/dynamics365/sales/copilot-ask-questions#get-recent-changes-to-a-lead-or-opportunity)を作成
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/c649b5f2-9e1a-4624-4dc1-cf87d9b10f76.png)

- 設置完了から環境に反映まで時間が掛かる場合もある（およそ十分）。それが終なら、画面右上Copilotのアイコンが表示される。  [参考URL](https://learn.microsoft.com/en-us/dynamics365/sales/use-sales-copilot)
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/e650d79a-b739-e9de-0138-7a93790b0cf4.png)
  - もしcopilotのアイコンが出ない場合、 [Power Platform admin center](https://admin.powerplatform.microsoft.com/)でユーザーのsecurity ruleを確認、「sales copilot administrator」或いは「sales copilot user」roleを追加
javascriptでfizzbuzzプログラム

https://playcode.io/javascript

```fizzbuzz.js

for (let i = 1; i <= 100; i++) {
    let output = '';
    if (i % 3 === 0) output += 'Fizz';
    if (i % 5 === 0) output += 'Buzz';
    console.log(output || i);
}


```
Amazon Pollyとは？の備忘録

テキストから音声への変換サービスです。Amazon Pollyを使用することで、テキストデータを自然な音声に変換することが可能です。

## できること

#### テキストから音声変換
Amazon Pollyは、与えられたテキストを多様な声質や言語で自然な音声に変換します。これにより、アプリケーションやデバイスに音声を組み込むことができます。

#### 多言語対応
Amazon Pollyは複数の言語に対応しており、異なる声やアクセントを提供しています。サポートされている言語には英語、スペイン語、フランス語、ドイツ語、日本語、中国語などが含まれます。
全て記載↓
アラビア語
アラビア語 (湾岸)
カタロニア語
中国語 (広東語)
標準中国語
デンマーク語
オランダ語 (ベルギー)
オランダ語
英語 (オーストラリア)
英語 (英国)
英語 (インド)
英語 (ニュージーランド)
英語 (南アフリカ)
英語 (米国)
英語 (ウェールズ)
フィンランド語
フランス語
フランス語 (ベルギー)
フランス語 (カナダ)
ヒンディー語
ドイツ語
ドイツ語 (オーストリア)
アイスランド語
イタリア語
日本語
韓国語
ノルウェー語
ポーランド語
ポルトガル語 (ブラジル)
ポルトガル語 (欧州)
ルーマニア語
ロシア語
スペイン語 (欧州)
スペイン語 (メキシコ)
スペイン語 (米国)
スウェーデン語
トルコ語
ウェールズ語

#### 音声ファイルの生成
Amazon Pollyは、テキストから音声に変換された結果を音声ファイル（MP3やWAVなど）として出力できます。これにより、生成された音声を保存したり、再利用したりすることができます。
OpenCVで "The function is not implemented. Rebuild the library with Windows"てなって動かなくなった時の直し方

## 結論！
再インストールでなおる！

## はじめに
　`InsightFace`というライブラリを使おうとした際、`Onnxruntime`という前提ライブラリ的なのが必要らしかったのですが、それを入れたり消したり、関連ライブラリの設定をいじっていたらタイトルの通りになりました。

　その直し方の備忘録。

　まぁ[海外のサイト](https://stackoverflow.com/questions/50783177/opencv-the-function-is-not-implemented-rebuild-the-library-with-windows)にあったやつをそのままガバガバ翻訳して、適当に補足しているだけです。
## 起こったこと
　以下の通りの問題が起こりました。（読みにくいので適当に改行しています。）
```shell-session:problem
cv2.error: OpenCV(4.8.1.78) 
C:\projects\opencv-python\opencv\modules\highgui\src\window.cpp:636: 
error: (-2) The function is not implemented. Rebuild the library with Windows,
GTK+ 2.x or Carbon support. If you are on Ubuntu or Debian,
install libgtk2.0-dev and pkg-config,
then re-run cmake or configure script in function cvShowImage
```
　起こった際の状態を再現するサンプルコードは以下の通り。

```python:test.py
import cv2
import numpy as np

img=cv2.imread('image.jpg',1)
cv2.imshow('image', img)
cv2.waitkey()
cv2.destroyAllWwindows()
```


## 解決方法
　OpenCVの再インストール！！！！
　これだけです。

　以下のように、OpenCV関連のライブラリをすべてアンインストールしてインストールしなおしましょう。
```shell-session:example
$ pip uninstall opencv-python 
$ pip uninstall opencv-contrib-python 
$ pip uninstall opencv-python-headless  

$ pip install opencv-python 
$ pip install opencv-contrib-python 
$ pip install opencv-python-headless  
```
　たまに、バージョンが新しくなりすぎて動かなくなる時があります。
　`pip list`でバージョンを確認できるのでメモっておくと良いでしょう。
　参考までに自分の最小環境を記載しておきます。
```shell-session:note
$ pip list
Package       Version
------------- --------
numpy         1.26.1
opencv-python 4.8.1.78
pip           23.3.1

```
　ちなみに、インストールの際に以下の通りにすればバージョンを指定できます。

```shell-session:note2
$ pip install opencv-python==4.8.1.78
```

　すっごいちなみに、このOpenCV関連ライブラリたちをすべて同じバージョンにしとくと安定するそうな。
エクセルファイルへの書き込み(Ruby)

rubyXLクラスを使ってエクセルファイルへの書き込み処理を実装しました

```ruby 
require 'rubyXL'

array1 = ["白石麻衣","秋元真夏","遠藤さくら","山下美月","吉田綾乃クリスティー"]
m_array = []
m_array = array1.shuffle!
array2 = ["好きなコスメ","好きなお笑い芸人","好きな落語家","好きな駅","好きなファッションブランド"]
t_array = []
t_array = array2.shuffle!
out_file = "result1.xlsx"
# rubyXLクラスを読み込み、エクセルファイルへ出力するインスタンスを呼び出す
xlsx = RubyXL::Workbook.new
# ワークシートの定義
worksheet = xlsx[0]
n=array1.length
srow = 2
erow = srow + n
nowrow = srow


# 配列の要素を取り出し、エクセルファイルへ書き込む
for i in 0...n do
    name = m_array[i]
    title = t_array[i]
    worksheet.add_cell(nowrow,1,name)
    worksheet.add_cell(nowrow,2,title)
    nowrow += 1
end

# エクセルファイルを保存する
begin
  xlsx.write(out_file)
rescue
  puts "ファイルの保存に失敗しました"
end
```
JavaScript について説明できる

# 1. JavaScript とは
JavaScript は、Webページを動的に制御し、対話的な要素を追加するためのスクリプト言語です。JavaScriptは主にブラウザ上で動作し、ユーザーとの対話やWebページの挙動を制御する役割を果たします。

# 2. ECMAScript とは
ECMAScriptは、JavaScriptの標準規格のことを指します。JavaScriptは、Webページ上で動的な挙動や対話性を実現するためのスクリプト言語であり、その基本的な仕様や機能がECMAScriptによって定義されています。
<br>
プログラム言語には標準規格が存在し、それに基づいて開発されたプログラムが異なる実行環境で一貫性を保つためには、標準化が不可欠です。ECMAScriptはJavaScriptの標準規格であり、各ブラウザやJavaScriptエンジン（Node.jsなど）はこの標準に基づいてJavaScriptを実装しています。
Swiftで二つの文字列を比較し、片方の文字列の構成要素だけでもう一方の文字列が構成可能かをチェックするアルゴリズム


# 概要
[383. Ransom Note](https://leetcode.com/problems/ransom-note/description/)を解いているときに書いた処理をもしかしたらどこかで使うかも？と思ったので記事として残しておきます。


# 詳細

以下のようなケースで使用できます。

>ransomNoteとmagazineの2つの文字列が与えられたとき、ransomNoteがmagazineの文字を使って構成できる場合はtrueを、そうでない場合はfalseを返す。
magazineの各文字はransomNoteで一度しか使用できない。

今回書いた方法は`Dictionary`を用いた処理、そして`reduce`関数を用いた処理です。

まずはDictionaryを用いた例です。
こちらは単純な考え方で、Dictionary内に`magazine`の内容を詰め込んだ後に`ransomNote`内との照会を行う、という内容になります。
基本的なデータ構造を用いている分、記述がしやすい反面、個人的にはちょっと記述量が多く、やや冗長に感じます。


```swift:Dictionaryを用いた例
class Solution {
    // ransomNoteの文字列がmagazine文字列のみで構成されているかを確認する関数
    func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
        var counter = [Character: Int]()
        // Dictionaryのcounter内に`magazine`の内容を詰める処理
        for m in magazine{
            if let count = counter[m]{
                counter[m] = count+1
            }else{
                counter[m] = 1
            }
        }

        // ransomNote内とDictionaryのcounter内の照合を行い、存在していて、かつカウントが0よりも大きい場合にはカウントの減算を行い、処理を継続する
        for r in ransomNote{
            if let count = counter[r],count > 0{
                counter[r] = count-1
            }else{
                return false
            }
        }
        return true
    }
}
```

次にreduce関数を用いた例です。
こちらも基本的な考え方は先述の内容と同じですが、`reduce`関数を用いた処理を関数として外出ししており、メイン処理を行う関数がスッキリしています。
コードの量がスッキリし、読みやすくなった印象です。

```swift:reduce関数を用いた例
class Solution {

    // ransomNoteの文字列がmagazine文字列のみで構成されているかを確認する関数
    func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
       var magazineWordCount = countCharacters(magazine)
       for r in ransomNote {
           if let count = magazineWordCount[r],count > 0{
               magazineWordCount[r] = count - 1
           }else{
               return false
           }
       }
       return true
    }

    // reduce関数を用いて関数内に存在する文字とその文字が登場する回数をカウントする関数
    func countCharacters(_ str:String) -> [Character:Int]{
        return str.reduce(into: [:]) { counts,char in
            counts[char,default:0] += 1
        }
    }
}
```

ただ、このようなコードを書いておいてなんですが、私のSwiftの習熟度的には何も見ずにいきなり`reduce`関数を使った処理を書いてと言われると困ると思ったので、記事として残しておきます！
Eaton Park Gamuda Land

イートン パーク ガムダ ランドのイートン パーク プロジェクトは、ホーチミン市トゥ ティエムの中心部にある卓越性と利便性の象徴です。 3.76ヘクタールを超える規模のこのプロジェクトには、最大39階建てのタワーマンション6棟が含まれ、1,968戸のアパートメントと12戸のペントハウスが提供されます。 総投資額は最大11億ドルで、イートンパークは住居としてだけでなく、プール、ジム、ショッピングモールなどの多様な施設を備えた優れた都市コミュニティを約束します。 -上品で便利な居住空間。 Eaton Park Quận 2

イートンパークアパートメントプロジェクト

イートン パーク プロジェクトの立地は、ホーチミン市トゥ ティエムの行政の中心地に位置するという比類のない利点があります。 トゥドゥック市 2 区アンフー区、マイ チ トー アベニューの正面住所により、住民はラック チエック国立スポーツ コンプレックスに近く、トゥ ティエム トンネルに直結しています。 この戦略的な立地は最高の利便性をもたらすだけでなく、1 区の中心部や独特な道路への素早いアクセスを実現し、居住者にとって贅沢で快適な生活環境を作り出しています。

イートンパークアパートメントの場所 căn hộ Eaton Park

ガムダ ランドのイートン パーク プロジェクトは、その大規模さで際立っているだけでなく、豪華なアメニティとデザインでもユニークです。 6 ブロックの高層アパートメントを備えたイートン パークは、幼稚園、屋外バーベキュー エリア、ファミリーマートと韓国商業センター、ビンマート、ジム、スイミング プール、銀行、その他多くの高層ビルなどの豪華な施設を統合することで生活空間を管理しています。エンドスポット。 イートン パークは、1 ベッドルームから 3 ベッドルームまで柔軟にアパートメントを選択でき、多様なエリアと高級な都市デザインを備えており、居住者に快適で贅沢な生活をもたらすことをお約束します。 イートン パーク アパートメンツ

出典: https://9xland.com/eaton-park.html



最新の料金表を入手してツアーに申し込みましょう！

0909.022.466
Skype を Ubuntu にインストール

昔懐かし Skype を使うことになった。
特に問題なかったけれども、インストールできましたという記録。

# 環境

- Ubuntu 22.04
- Skype 8.110.0.218

# インストール

こちらから

https://www.skype.com/en/get-skype/download-skype-for-desktop/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/0e1e3ce1-66ec-0966-3e32-266418dbadad.png)

Linux の場合は snap に飛ぶようだ。
https://snapcraft.io/skype
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ddbda3de-5a75-552d-2f36-3448a307acf8.png)


Vies in Desktop store のボタンは snap のインストールかな? その下に表示されているコマンドでインストール。

```
$ sudo snap install skype
```

インストールできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3f15bbec-f504-da66-8c6f-da0208c45638.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4cdb845a-373f-6c40-7d93-4a3b282e85f4.png)
Mailman のメーリングリストメンバーを全部出力する

## 目的

Mailman で管理されているすべてのメーリングリストのすべてのメンバーをファイルに出力する。

## 手順

### 1. Mailman のディレクトリを特定する

Mailman Web サーバの本体スクリプトやユーティリティコマンドなどが入っているディレクトリがどこかにあるはず。  
気合いで探す。

筆者の環境では `/usr/lib/mailman` だった。  
以下はこのディレクトリの前提で進める。

### 2. ワンライナーをポンする

```bash
cd /usr/lib/mailman/bin
( echo 'list name,destination' && for list in $(sudo ./list_lists -b); do sudo ./list_members "$list" | sed "s/^/$list,/"; done ) | tee ~/lists-dump.csv
```

実行すると、以下の形式で CSV ファイルが得られる。

```csv:lists-dump.csv
list name,destination
list-a,user1@example.com
list-a,user2@example.com
list-b,user3@example.com
list-b,user4@example.com
```

おわり。
eli5とscikit-survivalの互換性

train -val- testに分けてRSFを行っていました。concordance indexまでは求めることができましたが、eli5とscikit-survivalの互換性の問題でfeature importanceを求めることができない。
コードを以下に示します。google colaboratoryを使用

生存関数が関与してくるとむずかしくなりますが、eli5とscikit-survivalの互換性の問題を皆さんどのように解決していますか。
feature importanceは論文を作成するうえで必ず必要となります。

!pip install scikit-survival

import pandas as pd
from sklearn.model_selection import train_test_split
from sksurv.ensemble import RandomSurvivalForest
from sklearn.model_selection import GridSearchCV
from sksurv.metrics import concordance_index_censored
import numpy as np

# Load your data
data = pd.read_excel('your_data_file.xlsx')

# Preparing the data
target = np.array([(e == 2, t) for e, t in zip(data['Event'], data['DFS'])], dtype=[('Event', '?'), ('DFS', '<f8')])
features = data.drop(columns=['Event', 'DFS'])

# Splitting the data
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=42)

# Hyperparameter tuning
param_grid = {
    'max_features': ['auto', 'sqrt', 'log2'],
    'max_depth': [None, 10, 20, 30],
    'min_samples_leaf': [1, 2, 4],
    'n_estimators': [100, 200, 300],
    'min_samples_split': [2, 5, 10]
}
rsf = RandomSurvivalForest(random_state=42)
grid_search = GridSearchCV(rsf, param_grid, cv=5, n_jobs=-1, scoring='roc_auc')
grid_search.fit(X_train, y_train)

# Best parameters and model
best_params = grid_search.best_params_
best_rsf = grid_search.best_estimator_

# Evaluation on test set
prediction = best_rsf.predict(X_test)
c_index = concordance_index_censored(y_test['Event'], y_test['DFS'], prediction)

# Output best parameters and concordance index
print("Best Parameters:", best_params)
print("Concordance Index on Test Set:", c_index[0])



!pip install eli5

sf = RandomSurvivalForest(random_state=42, **best_params)
rsf.fit(X_train, y_train) 

# Feature Importance
from eli5.sklearn import PermutationImportance
perm = PermutationImportance(rsf, n_iter=15, random_state=42)
perm.fit(X_test, y_test)
eli5.show_weights(perm, feature_names=X_test.columns.tolist())