機械学習を意識した0から始めるデータ取得方法

# 目次
1. [はじめに](#はじめに)
2. [実験計画法](#実験計画法)
    1. [直行配列表](#直行配列表)
    2. [ラテンハイパーキューブサンプリング](#ラテンハイパーキューブサンプリング)
    3. [D最適計画](#d最適計画)
3. [まとめ](#まとめ)

## 1. はじめに
※この記事では、構造化データを想定して記載しております。

機械学習では、通常、大規模なデータを使用して学習することが推奨されます。質の良いデータが大量にあれば、どのようなアルゴリズムを使用しても、モデルの精度やロバスト性能が向上する傾向があります。つまり、機械学習においては、質の良いデータが大規模に存在することで、より効果的なアプローチとなります。

一方で、少量のデータしかない場合には、機械学習の適用には細心の注意が必要です。例えば、学習時の評価をLeave-One-Out検定にする、ランダムシードを複数回変更して評価するなどの方法が考えられます。この記事では、データ取得のアプローチから機械学習の精度改善に繋げていく事をまとめます。
少量のデータを集める際に、データサイエンスの知見から、効率よくデータを取得して、最終的に機械学習を適用する事を考えていきます。

この記事では、新たにデータを取得する以下のようなケースを想定しています：

- MI（Materials Informatics）の研究分野でデータが全くないケース
- 製造機器などで機器のパラメータ変更がほとんどないケース
- 新たな施策などで過去のデータが全くないケース
- その他、新規でデータを取得するケース

## 2. 実験計画法

実験計画法（Design of Experiments, DOE）は、効率的かつ効果的に実験を行い、少ない試行回数で最大限の情報を得るための統計的手法です。実験計画法の主な目的は、対象となるシステムやプロセスにおいて因果関係や最適な条件を解明することです。この手法は、製造業や科学研究、エンジニアリングなどの分野で広く使用されており、データに基づいた意思決定を行うための強力なツールとなっています。

### 実験計画法の基本的な概念

実験計画法では、対象とするプロセスやシステムに影響を与える **因子**（独立変数）と、それらがどのように結果に影響を与えるかを調べます。具体的には、以下の概念が重要です：

- **因子（Factor）**：実験の条件や変数を指し、システムに影響を与える要素です。例えば、製造プロセスにおける温度、圧力、時間などが因子となります。

- **水準（Level）**：各因子が取りうる具体的な値や状態を指します。例えば、温度因子が「300°C」「400°C」「500°C」と異なる水準を持つことがあります。

- **応答（Response）**：実験の結果として得られるデータや観測値で、因子の変化によって影響を受けるものです。例えば、製品の強度や耐久性などが応答となります。

- **交互作用（Interaction）**：複数の因子が同時に結果に与える影響を示すものです。因子が単独で結果に影響を与えるだけでなく、他の因子と組み合わさることで異なる影響を与える場合があります。

### 実験計画法の目的

実験計画法の主な目的は、次の3つです

1. **因果関係の解明**：どの因子が結果にどのように影響を与えるかを明らかにすること。

2. **最適条件の特定**：因子の組み合わせや設定を調整することで、システムやプロセスの最適な条件を見つけること。

3. **効率的なデータ収集**：試行回数を減らしつつ、統計的に有効な情報を収集すること。

今回の記事は、効率的なデータ収集の側面を重視した内容としています。
ここから実験計画法の中で3つの手法について記載していきます。



## 2.1 直行配列表 (Orthogonal Array Design,タグチメソッド)
直行配列表（Orthogonal Array Design）は、少ない実験回数で多くの因子の影響を評価できる実験計画法です。この手法は、製造業で広く採用され、特にタグチメソッドとして知られています。各因子の水準を均等に組み合わせることで、効率的に因子の主効果や交互作用を評価することができ、信頼性の高いデータ収集が可能です。

### 直行配列表のメリット・デメリット

| **メリット** | **デメリット** |
| --- | --- |
| 少ない実験回数で多くの情報を得ることができる | 因子や水準が多くなると、必要な実験回数が増え、計画が複雑になる |
| 因子間の交互作用を評価できる | 因子数が多い場合、適切な直行配列表を見つけるのが難しくなることがある |
| 実験計画がシンプルで実行しやすい | 数値情報が多い水準では、実験回数が増加する可能性がある |

### 直行配列表の特徴
直行配列表は、因子間の交互作用を評価する上で非常に有効です。また、少ない実験回数で多くの因子を同時に検討できるため、コストや時間の削減が可能です。さらに、直交性により、各因子の組み合わせが均等に分布し、バイアスの少ないデータを得ることができます。

### 因子の決定方法
因子を決定する際は、次のステップが重要です。

1. **目的の明確化**：実験の目的を明確に設定します（例：製品の品質向上やプロセスの最適化）。
2. **因子のリストアップ**：実験に影響を与える可能性のある因子をすべてリストアップします。過去のデータや専門家の意見を参考にします。
3. **因子の選定**：リストアップした因子の中から、重要なものを選定します。影響の大きさや現実性を考慮します。
4. **水準の決定**：各因子における異なる条件（レベルまたは水準）を決定します。一般的には2〜3水準が使われます。

### 具体例：製品の品質向上のための因子決定
製品の強度と耐久性を最適化するために、新しい金属部品の製造プロセスを直行配列表で最適化する例を考えます。

1. **目的の明確化**：
   - 新しい金属部品の強度と耐久性を最適化し、製品の品質を向上させる。

2. **因子のリストアップ**：
   - A因子：熱処理温度（低、中、高）
   - B因子：冷却速度（遅い、普通、速い）
   - C因子：合金成分（成分1、成分2、成分3）

3. **因子の選定**：
   - 上記の因子は全て重要であり、実験の目的に直接関係していますので、全て選定します。ここで、必要とする因子は選定して、必要としない因子は含めません。

4. **水準の決定**：
各因子について、具体的な水準を決定します。例えば、温度や速度の異なる条件を設定します。

  - **A因子**: 熱処理温度（500°C～700°Cの範囲を低、中、高で表現）
  - **B因子**: 冷却速度（1°C/s～5°C/sの範囲を遅い、普通、速いで表現）
  - **C因子**: 合金成分（成分1、成分2、成分3）

上記の条件の場合、以下のような実験条件が得られます。(一部のみ表示)

| 実験番号 | 熱処理温度 | 冷却速度 | 合金成分 |
| --- | --- | --- | --- |
| 1 | 低 | 遅い | 成分1 |
| 2 | 低 | 普通 | 成分2 |
| 3 | 低 | 速い | 成分3 |
| 4 | 中 | 遅い | 成分2 |
| 5 | 中 | 普通 | 成分3 |
| 6 | 中 | 速い | 成分1 |
| 7 | 高 | 遅い | 成分3 |
| 8 | 高 | 普通 | 成分1 |
| 9 | 高 | 速い | 成分2 |

### デメリットの理解
数値データを多く含む水準を使用する場合、水準のユニークな値が増えすぎると、全ての実験を行うことが難しくなることがあります。したがって、直交配列表では因子をカテゴリカルな情報として扱うことが一般的です。

例えば、3因子3水準の直行配列表でも、3 × 3 × 3 = 27通りの組み合わせが存在します。このため、多くの実験を行える場合に限り適用するのが適切です。

### 直行配列表のまとめ
少ない実験回数で多くの情報を取得でき、因子間の交互作用を評価できる点が特徴です。
実験計画もシンプルで実行しやすいですが、課題として、因子や水準が多くなると、適切な直交配列表の選定が難しくなります。また、複雑な交互作用が多い場合、解析が困難になることがあります。


## 2.2 ラテンハイパーキューブサンプリング (Latin Hypercube Sampling, LHS)


ラテンハイパーキューブサンプリング (Latin Hypercube Sampling, LHS) は、数値実験において効率的にサンプリングを行う手法です。多次元空間を均等にサンプリングすることで、少ないサンプル数でも統計的に有意な結果を得ることができ、シミュレーションやモデリングにおいて非常に有用です。

LHSは、直行配列表と異なり、数値型データも扱いやすいという特徴があります。

### LHSのメリット・デメリット

| メリット |  |
| --- | --- |
| **効率的なサンプリング** | 少ないサンプル数で広範なデータ分布をカバーできるため、実験やシミュレーションのコストと時間を削減できます。 |
| **多様なデータセット** | 各因子の範囲を均等にサンプリングすることで、偏りのない多様なデータセットを取得できます。 |
| **高精度な結果** | 均等なサンプリングにより、少ないサンプル数でも統計的に有意な結果を得ることができます。 |

---

| デメリット |  |
| --- | --- |
| **因子の範囲設定** | サンプリングポイントの選定にランダム性があるため、同じ設定でも結果が異なる場合があります。 |
| **複雑な因子の処理** | 非線形な因子や複雑な相互作用がある場合、適切なサンプリングが難しいことがあります。 |

### LHSの基本原理
LHSは次の手順で実行されます：

1. **因子の範囲設定**：各因子の取りうる範囲を決定し、その範囲を均等に分割します。
2. **サンプリングポイントの選定**：各分割区間から1つのサンプルポイントをランダムに選びます。
3. **組み合わせの決定**：選ばれたサンプルポイントを組み合わせて、多次元空間におけるサンプリング点を決定します。

#### LHSのイメージ図
|       | **X1**  | **X2**  | **X3**  |
|-------|---------|---------|---------|
| **Y1** |         |    ●    |         |
| **Y2** |    ●    |         |         |
| **Y3** |         |         |    ●    |

見にくい図で申し訳ございませんが(筆者がマークダウンを使いこなしていない)、2次元のイメージ図となります。この図で伝えたいことは、因子の範囲設定は非常に重要という事です。因子の数が増えることで、分割数が増加し、サンプリング点の選定に影響を与えるためです。
LHSのデメリットを先に纏めましたが主にデメリットな方向で影響していきます。これについて後述でも記載します。

### 具体例：製品の品質向上のためのLHSの適用

次に、具体例を交えてLHSの適用ケースを見ていきます。新しい合金材料の強度と耐久性を最適化するためにLHSを適用する場合を考えてみましょう。

#### ステップ1: 因子の範囲設定

- **A因子**：熱処理温度（500°C ～ 700°C）
- **B因子**：冷却速度（1°C/s ～ 5°C/s）
- **C因子**：合金成分比率（0.1 ～ 0.3）

#### ステップ2: サンプリングポイントの選定

各因子範囲を均等に10分割し、各分割区間から1つのサンプルポイントをランダムに選定します。

#### ステップ3: 組み合わせの決定

選ばれたサンプルポイントを組み合わせて、多次元空間のサンプリング点を決定します。

ここまでのステップはコード上ですぐに結果を得られます。

### LHSにおけるPythonコードの一例
```python
# !pip install pyDOE2 #installしてなければインストールしてください
import numpy as np
import pandas as pd
from pyDOE2 import lhs

# ステップ1: 因子の範囲設定
# 各因子の範囲を設定（A: 熱処理温度, B: 冷却速度, C: 合金成分比率）
lower_bounds = np.array([500, 1, 0.1])  # 各因子の下限
upper_bounds = np.array([700, 5, 0.3])  # 各因子の上限

# ステップ2: サンプリングポイントの選定
# LHSによって各因子範囲を均等に10分割しサンプリング
n_samples = 10  # サンプル数
lhs_samples = lhs(3, samples=n_samples)  # 3因子のLHSサンプルを生成

# 各因子の範囲に基づいてサンプリングポイントを計算
lhs_scaled = lower_bounds + (upper_bounds - lower_bounds) * lhs_samples

# サンプリング結果をデータフレームに変換
df_lhs = pd.DataFrame(lhs_scaled, columns=['熱処理温度 (°C)', '冷却速度 (°C/s)', '合金成分比率 (%)'])

# 結果を表示
print("LHSによるサンプリング結果:")
print(df_lhs)

# 結果をCSVに保存
df_lhs.to_csv('lhs_sampling_results.csv', index=False)

	熱処理温度 (°C)	冷却速度 (°C/s)	合金成分比率 (%)
0	565.938832	4.613612	0.135821
1	529.931888	3.097852	0.200190
2	584.080180	4.501643	0.220779
3	670.864203	2.199318	0.286583
4	615.108424	3.557389	0.105907

```
### LHSにおけるPythonコードの一例(カテゴリーを含む場合の例)
```python
import numpy as np
import pandas as pd
from pyDOE2 import lhs

# ステップ1: 因子の範囲設定
# 数値型変数の範囲（A: 熱処理温度, B: 冷却速度, C: 合金成分比率）
lower_bounds = np.array([500, 1, 0.1])  # 各因子の下限
upper_bounds = np.array([700, 5, 0.3])  # 各因子の上限

# カテゴリカル変数（合金のタイプ）の設定
alloy_types = ['タイプA', 'タイプB', 'タイプC']

# ステップ2: サンプリングポイントの選定
# 数値型変数に対してLHSを適用
n_samples = 10  # サンプル数
lhs_samples = lhs(3, samples=n_samples)  # 3因子のLHSサンプルを生成

# 数値型変数をスケーリング
lhs_scaled = lower_bounds + (upper_bounds - lower_bounds) * lhs_samples

# カテゴリカル変数をランダムに選択
random_alloy_types = np.random.choice(alloy_types, size=n_samples)

# ステップ3: サンプリング結果を統合
# 数値型変数の結果とカテゴリカル変数をDataFrameにまとめる
df_lhs_with_category = pd.DataFrame(lhs_scaled, columns=['熱処理温度 (°C)', '冷却速度 (°C/s)', '合金成分比率 (%)'])
df_lhs_with_category['合金のタイプ'] = random_alloy_types

# 結果を表示
print("LHSによるサンプリング結果（数値型 + カテゴリ型）:")
print(df_lhs_with_category)

# 結果をCSVに保存
df_lhs_with_category.to_csv('lhs_sampling_results_with_category.csv', index=False)

   熱処理温度 (°C)  冷却速度 (°C/s)  合金成分比率 (%) 合金のタイプ
0  543.113767     1.411546    0.139669   タイプC
1  626.290163     2.994343    0.101674   タイプA
2  618.316923     2.579844    0.268506   タイプC
3  503.078449     1.394405    0.256459   タイプA
4  650.561295     3.187404    0.224899   タイプA
5  681.184548     4.591895    0.173911   タイプA
6  572.313657     3.655971    0.194153   タイプC
7  532.579682     4.086322    0.215924   タイプA
8  584.911708     4.728253    0.280397   タイプC
9  673.330351     1.964577    0.152000   タイプA
```

### LHSの実用性

出力されたサンプリング結果に基づいて実際の実験を行うことで、少ないデータでも機械学習にとって有意義な教師ラベルを手に入れることが可能です。これが直行配列表とは異なり、数値型データを簡単に取り扱える点で優れています。

ただし、デメリットにも記載した通り、因子の数が増えた場合や、非常に大きな空間でサンプリングする際には注意が必要です。

### ※注意点
LHSでは各因子の範囲を均等に分割するため、非常に広大な空間で有限回のサンプリングを行うと、空間の中心付近にサンプルが少なくなりがちです。この「スカスカ」状態は、有益なサンプリング結果を得られず、機械学習を使用する時に、精度が低下する可能性もあります。

### LHSのまとめ

ラテンハイパーキューブサンプリング(LHS)は、効率的かつ高精度なデータ収集を可能にする手法であり、特に数値実験やシミュレーションにおいて有用です。多様なデータセットを取得するための強力なツールとして、機械学習やモデリングにおいて広く利用されています。

ただし、因子範囲の設定やランダム性の影響、広大な空間でのサンプリングの問題を考慮する必要があり、適切に実施するためには慎重な計画が求められます。

## 2.3 D最適計画

D最適計画 (D-Optimal Design) は、実験計画法の一つで、限られたリソースで最も効率的なデータを取得することを目的とした手法です。この手法では、実験データの「情報量」を最大化するように、実験の設計が行われます。特に、線形回帰モデルのパラメータ推定において、その推定精度を高めるために使用されます。

「D最適」という名称は、設計行列の行列式（デターミナント、D）を最大化することに由来します。これにより、推定されるパラメータの共分散行列の体積を最小化し、推定精度を向上させることができます。

## 設計行列とモデル

まず、線形回帰モデルを考えます。モデルは次のように表されます。

$$
y = X\beta + \epsilon
$$

ここで、それぞれの値の意味は以下の通りです。

- $y$ は観測値ベクトル
- $X$ は設計行列
- $\beta\$ は推定すべきパラメータのベクトル
- $\epsilon\$ は誤差項

設計行列 $X$ は、実験で選択された因子の組み合わせを示しています。この行列が持つ情報量が多いほど、より精度の高いパラメータ推定が可能になります。具体的には、この設計行列を用いて推定されるパラメータ $\beta\$ の精度が鍵になってきます。パラメータ$\beta\$  がどのように設計行列 $X$ の構造に依存するかを理解するためには、パラメータの推定誤差を定量化する共分散行列について考える必要があります。

## 共分散行列と推定精度

パラメータ $\beta\$ の最小二乗法による推定量 $\hat{\beta}\$ の共分散行列は次のように表されます。

$$
\text{Cov}(\hat{\beta}) = \sigma^2 (X^TX)^{-1}
$$

ここで、$\sigma^2\$ は誤差項 $\epsilon\$ の分散です。この式からわかるように、設計行列 $X$ の情報量は $(X^TX)^{-1}$ に依存しています。行列の逆行列が小さければ小さいほど、つまり $\(X^TX)\$ が大きければ大きいほど、推定精度が高くなります。

## D最適計画の特徴

- **情報量の最大化**：D最適計画は、限られた数の実験で得られるデータから、最大の情報を引き出すように設計されます。これにより、効率的な実験が可能となります。
- **適応性**：D最適計画は、既存のデータや制約条件に基づいて柔軟に設計を行うことができるため、実際の実験環境に適した計画を立てやすいです。
- **高い推定精度**：D最適計画は、推定するパラメータの共分散行列を最小化するため、モデルのパラメータ推定精度が向上します。

## D最適計画の基本原理

D最適計画は以下の手順で実行されます：

1. **候補点の選定**：実験候補点（実験の設定）を定義し、実行可能な全ての候補点の集合を作成します。
2. **行列式の計算**：候補点の中から、設計行列の行列式を最大化する組み合わせを選定します。この行列式を最大化することで、情報量を最大化します。
3. **実験の実施**：選定された候補点に基づいて実験を行い、データを収集します。

## D最適計画のメリット・デメリット

| メリット |  |
| --- | --- |
| **効率的な実験計画** | 最小限の実験回数で、最大限の情報を得ることができるため、リソースの効率的な利用が可能です。 |
| **高精度なパラメータ推定** | モデルのパラメータ推定精度が向上し、より信頼性の高い結果が得られます。 |
| **柔軟な設計** | 既存のデータや制約条件に基づいて実験計画を最適化できるため、現実的な環境に適した計画が立てやすいです。 |



| デメリット |  |
| --- | --- |
| **計算の複雑さ** | D最適計画は、行列式を最大化するための計算が複雑であり、大規模な問題に対しては計算負荷が高くなることがあります。 |
| **ドメイン性が求められる** | 専門的な知識がないと適用が難しい場合があります。 |
| **全ての因子の網羅は難しい** | D最適計画は、限られた実験回数で最大限の情報を引き出す手法です。そのため、全ての因子やその組み合わせを完全に網羅することは難しく、特定の条件下で最適な解を得ることに重きを置いています。 |

## D最適計画の実施例

新しい合金材料の硬度を予測するためのD最適計画を考えます。

- **温度 (°C)**：500°C ～ 700°C
- **圧力 (MPa)**：100 MPa ～ 200 MPa
- **合金成分 (％)**：成分A（10％ ～ 30％）、成分B（5％ ～ 15％）

これらの因子の範囲には、現実の実験での制約があり、例えば温度や圧力の設定には機器の性能や安全基準に従う必要があります。そこで、実験計画ではこれらの制約を考慮しつつ、限られた実験回数で効率的に情報を引き出すためにD最適計画を採用します。

### ステップ1: 候補点の選定

実験の設定として、温度、圧力、合金成分の候補点を定義します。一例として以下のような組み合わせが考えられますが、Pythonコードで回す際にはここは上限下限の値からランダムで候補点を探ります。

- **温度**：500°C, 550°C, 600°C, 650°C, 700°C
- **圧力**：100 MPa, 125 MPa, 150 MPa, 175 MPa, 200 MPa
- **合金成分A**：10％, 15％, 20％, 25％, 30％
- **合金成分B**：5％, 10％, 15％

### ステップ2: 行列式の演算

次に、候補点の中から設計行列の行列式 \(\det(X^TX)\) を最大化する組み合わせを選定します。この行列式の最大化により、パラメータ推定の精度を高めることができます。

例えば、D最適計画により以下の実験設定が選ばれる可能性があります：

- **温度**：500°C, 600°C, 700°C  
- **圧力**：125 MPa, 175 MPa, 200 MPa  
- **合金成分A**：10％, 20％, 30％  
- **合金成分B**：5％, 10％, 15％  

これらの選定された実験点は、設計行列の行列式が最大になるように最適化されており、効率的にデータを収集することができます。

### ステップ3: 実験の実施

選定された実験条件に基づいて、実際に実験を行いデータを収集します。これにより、最小限の実験回数で最大限の情報が得られ、パラメータ推定の精度を向上させることが可能になります。


### D最適計画の簡易コード例1(数値型変数のみの場合)
```python
import numpy as np
import pandas as pd
from scipy.optimize import minimize

#変数がすべてnumericの場合
# ステップ1: 候補点の選定（ランダムにサンプリング）
# 温度、圧力、合金成分A, Bの範囲
lower_bounds = np.array([500, 100, 10, 5])  # 下限
upper_bounds = np.array([700, 200, 30, 15]) # 上限

# n_samplesは候補点の数
n_samples = 20
np.random.seed(0)  # 再現性のため
candidates = np.random.uniform(lower_bounds, upper_bounds, size=(n_samples, len(lower_bounds)))

# ステップ2: D最適計画の実行
# 行列式を最大化する関数を定義
def objective(indices):
    selected_points = candidates[indices.astype(int)]
    X = np.c_[np.ones(selected_points.shape[0]), selected_points]  # 定数項を加える
    return -np.linalg.det(X.T @ X)  # 行列式の負の値を返す（最小化問題として解くため）

# n_experimentsは選ばれる実験点の数
n_experiments = 10

# 最適化の初期値を設定（ランダムに選んだ候補点のインデックス）
initial_indices = np.random.choice(range(n_samples), n_experiments, replace=False)

# インデックスを離散値に制約して最適化
result = minimize(objective, initial_indices, method='Nelder-Mead', options={'maxiter': 1000})
design_indices = np.round(result.x).astype(int)

# 選定されたD最適実験条件
d_optimal_design = candidates[design_indices]

# 結果をCSVに出力
df = pd.DataFrame(d_optimal_design, columns=['温度 (°C)', '圧力 (MPa)', '合金成分A (%)', '合金成分B (%)'])
df.to_csv('d_optimal_design.csv', index=False)

#5行分の結果出力
df.head()

	温度 (°C)	圧力 (MPa)	合金成分A (%)	合金成分B (%)
0	591.230066	156.843395	10.375796	11.176355
1	571.901580	143.703195	23.952624	5.602255
2	604.369664	141.466194	15.291112	12.742337
3	695.723668	179.915856	19.229587	12.805292
4	609.762701	171.518937	22.055268	10.448832

```

### D最適計画の簡易コード例2(数値型変数+カテゴリーの場合)
```python
import numpy as np
import pandas as pd
from scipy.optimize import minimize

# ステップ1: 候補点の選定（ランダムにサンプリング）
# 温度、圧力、合金成分A, Bの範囲
lower_bounds = np.array([500, 100, 10, 5])  # 下限
upper_bounds = np.array([700, 200, 30, 15]) # 上限

# n_samplesは候補点の数
n_samples = 20
np.random.seed(0)  # 再現性のため
candidates_numeric = np.random.uniform(lower_bounds, upper_bounds, size=(n_samples, len(lower_bounds)))

# カテゴリ変数の設定（合金のタイプ：タイプA, タイプB, タイプC）
alloy_types = np.random.choice(['タイプA', 'タイプB', 'タイプC'], size=n_samples)

# カテゴリ変数をダミー変数に変換
alloy_dummies = pd.get_dummies(alloy_types)

# 候補点にカテゴリ変数を追加
candidates = np.hstack([candidates_numeric, alloy_dummies.values])

# ステップ2: D最適計画の実行
# 行列式を最大化する関数を定義
def objective(indices):
    selected_points = candidates[indices.astype(int)]
    X = np.c_[np.ones(selected_points.shape[0]), selected_points]  # 定数項を加える
    return -np.linalg.det(X.T @ X)  # 行列式の負の値を返す（最小化問題として解くため）

# n_experimentsは選ばれる実験点の数
n_experiments = 10

# 最適化の初期値を設定（ランダムに選んだ候補点のインデックス）
initial_indices = np.random.choice(range(n_samples), n_experiments, replace=False)

# インデックスを離散値に制約して最適化
result = minimize(objective, initial_indices, method='Nelder-Mead', options={'maxiter': 1000})
design_indices = np.round(result.x).astype(int)

# 選定されたD最適実験条件
d_optimal_design = candidates_numeric[design_indices]
d_optimal_alloy = alloy_types[design_indices]

# 結果をCSVに出力
df = pd.DataFrame(d_optimal_design, columns=['温度 (°C)', '圧力 (MPa)', '合金成分A (%)', '合金成分B (%)'])
df['合金のタイプ'] = d_optimal_alloy
df.to_csv('d_optimal_design_with_category.csv', index=False)

df.head() 


温度 (°C)	圧力 (MPa)	合金成分A (%)	合金成分B (%)	合金のタイプ
0	584.730960	164.589411	18.751744	13.917730	タイプC
1	633.353343	167.063787	14.207651	6.289263	タイプB
2	604.369664	141.466194	15.291112	12.742337	タイプC
3	504.043679	183.261985	25.563135	13.700121	タイプC
4	695.352218	160.484552	24.785272	5.391878	タイプA

```

カテゴリー型の変数を含む場合でも、D最適計画を適用することは可能であることを示すため、例2のコードを記載しました。このコードを使用する際には、必要に応じて修正を加える必要がありますが、基本的な流れとしては上記の通りです。

注意点として、このコードでは数値を整数で出力していない点や、候補点や実験点の数を比較的少なく設定している点があります（LHSの例でも同様です）。実際に使用する際には、より大きな数値を使用し、実際の状況に適した候補点から実験を設計してください。また、数値を整数で出力する、あるいは変数ごとに整数型と浮動小数点型を使い分けて出力したい場合は、追加のコード修正が必要になります。ご要望があればお気軽にお知らせください。

### D最適計画のまとめ
D最適計画（D-Optimal Design）は、限られた実験回数で最大限の情報を得ることを目的とした実験計画法です。行列式（デターミナント）を最大化することで、推定されるパラメータの精度を高める手法です。この手法により、効率的にデータを収集し、最適な条件を見つけることが可能です。ただし、計算が複雑であり、すべての因子を網羅することは難しいため、問題の規模や条件に応じた適用が必要です。


## 3.まとめ

ここまで、直行配列表 (タグチメソッド)、ラテンハイパーキューブサンプリング (LHS)、そしてD最適計画を紹介してきました。データ収集時にはそれぞれの手法の特徴を理解し、目的に沿って手法を選ぶことが重要です。