【最新決定版】タイタニックデータで学ぶ　Pandasによるデータ加工からグラフ化まで

この記事は[Nuco Advent Calendar 2023](https://qiita.com/advent-calendar/2023/nuco)の21日目の記事です。
# 第１章：はじめに
### 本記事の目的
さてさて、この記事を読んだらいったい何が出来るようになるのよ！っていうのが、気になるところではあると思います。簡単に対象レベルを述べるのであれば、以下のような人が挙がってくるのではないでしょうか？

**＜対象レベル＞**
    ・何となくPythonを勉強し始めてみたけど、何やってみようかな？と考え中の方
    ・データ分析をこれからやってみたいなって思っている方
    ・就活もしくは転職でデータサイエンティスト、データアナリストを目指している方
    ・普段SQLは使っているけど、Pandasだとどうやるんだ？となっている方

などなど、様々な境遇の皆さんに刺さる記事になってくれれば。。！


**＜出来るようになること＞**
・グラフ出力に必要なデータの加工
・よく使う一般的なグラフの出力
・各グラフの持つ特徴の理解

最後の『特徴』って何だよって感じですが、実務では出力をしたグラフの体感半分くらいの行先は他者の目に止まる資料などです。彼らが**例えあなたの出力したグラフの背景知識に乏しかった**としても、「あぁなるほどね！理解理解！」と言わせなければなりません。であれば、どのグラフを使用するか決定するのに『グラフの持つ特性』を理解することは必要不可欠です！

ではでは、最後までお付き合いよろしくお願いしますm(__)m

::: note
弊社Nucoでは、他にも様々なお役立ち記事を公開しています。よかったら、Organizationのページも覗いてみてください。
また、Nucoでは一緒に働く仲間も募集しています！興味をお持ちいただける方は、[こちら](https://www.recruit.nuco.co.jp/?qiita_item_id=10819d3dc10f4c0807f2)まで。
:::
https://www.recruit.nuco.co.jp/?qiita_item_id=10819d3dc10f4c0807f2

### タイタニックデータについて
#### 1\.タイタニック号沈没事件 
それでは早速ですが皆様、こちらの船はご存知でしょうか？

**ドン！！**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/df9e6518-3763-10cd-9759-c4124647fd43.png)
>https://voi.id/ja/memory/273057 『1912年4月15日:大西洋に沈没したタイタニック号』

はい、かの有名なタイタニック号ですね！

まあ歴史事実としても有名ですし、映画化もされていますよね。

いや観たことないよ！って人でも主題歌『My Heart Will Go On』を聴けば、
あぁこれタイニックなんだ！！ってなるでしょう。

多くの犠牲者を出してしまったタイタニック号事件ですが、Kaggleという機械学習のコンペサイトには、**乗客情報とその乗客の生死が記録されているデータ**があります。
本記事では、そのデータを用いて解説をしていこうと思います。

#### 2\.タイタニックデータをダウンロードしよう
Step 1： [Kaggle](https://www.kaggle.com/)にアクセス

Step 2: 右上のRegiterをクリック

Step 3: Googleアカウント、もしくはメールアドレスで登録

Step 4: トップページ検索欄から『Titanic - Machine Learning from Disaster』と検索

Step 5: 検索結果から『Titanic - Machine Learning from Disaster』を選択

Step 6：『Data』を選択

Step 7: 下にスクロールして、右下の『Summary』からダウンロード

#### 3\.タイタニックデータを見てみよう
では、早速タイタニックデータをダウンロードして、中身を見てみましょう。
データの取り込みと出力は以下のように実行します。
```python
import pandas as pd

# CSVファイルの読み込み
df = pd.read_csv('train.csv')

# trainデータを出力
df
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/e1be7a79-9f08-a66c-d94f-2d9ec2014502.png)
| カラム名 | 説明 |
| :-----: | :---: |
|PassengerId|乗客の一意の識別番号|
|Survived|乗客が生存したかどうか（1: 生存, 0: 死亡）|
|Pclass|乗客の社会経済的地位を示すクラス（1st, 2nd, 3rd）|
|Name|乗客の名前|
|Sex|乗客の性別|
|Age|乗客の年齢|
|SibSp|タイタニック号に同乗している兄弟または配偶者の数|
|Parch|タイタニック号に同乗している親または子供の数|
|Ticket|チケット番号|
|Fare|乗船料金|
|Cabin|部屋番号|
|Embarked|乗船した港（C: Cherbourg, Q: Queenstown, S: Southampton）|

いかがでしょうか？
このような生々しいデータに触れるなんて凄い時代ですよね😭

それでは、次の章からこのデータをつかってPandasの基本操作を確認していきます！！
# 第２章：データ操作ためのPandas基礎知識

### そもそもPandasって何？
PandasはPythonでモデリングする際のいわゆる前処理段階で力を発揮するライブラリーです。様々なデータ加工処理をスムーズに行うことができ、表計算やデータの抽出・検索などを行うことができます。例えば、読み込んだデータからある条件を満たすindex(行)を抽出したり、あるカラム（列）の平均値などの統計量を算出、さらにはデータ同士の結合などの操作を行うことができます。データベースのSQLや、より一般的なものだとExcelとほぼ同じような役割を果たしてくれます。


### Pandasの入力形式・出力形式
CSVが一般的ですが、他の形式も多く対応していますので以下の表にまとめておきます。

| データ形式 | 入力メソッド | 出力メソッド |
| :-----: | :---: | :---: |
| CSV | `read_csv` | `to_csv` |
|Excel|`read_excel`|`to_excel`|
|JSON|`read_json`|`to_json`|
|HTML|`read_html`|`to_html`|
|SQL|`read_sql`|`to_sql`|
|Parquet|`read_parquet`|-|
|Feather|`read_feather`|-|
|Clipboard|`read_clipboard`|`to_clipboard`|
|HDF5|`read_hdf`|`to_hdf`|
|Msgpack|`read_msgpack`|`to_msgpack`|
|Stata|`read_stata`|`to_stata`|
|SAS|`read_sas`|-|
|Pickle|`read_pickle`|`to_pickle`|

### データの基本情報を調べる
#### 1\.先頭・末尾の５行を抽出
まずは簡単にデータの中身を見たい時に役立つメソッドのご紹介から、、、

:::note info
**先頭５行の表示**
`df.head()`

**先頭N行の表示**
`df.head(N)`

**末尾５行の表示**
`df.tail()`
:::

::: note warn
`()`内に何も入れなければ、デフォルトの５行が表示されます。
:::
頭のhead,尻尾のtailなので覚えやすいですよね☺️
では、試しに実行して出力結果を見てみましょう。

```python
#先頭５行を出力
df.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/50c4a81f-7c0c-9852-220b-eb6071758a63.png)

```python
#末尾５行を出力
df.tail()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/0ac35b23-2d60-c2be-6c12-90c1ac6ae582.png)
想像通りの結果ですね。
ちなみにindex（行番号）は０始まりなので、一緒に覚えておきましょう！

#### 2\.データフレームの大きさ
『データフレームの大きさ』と言われても、
まずデータフレームってなんだよ！！って最初はなりますよね。

今回で言えば、読み込んでいま`df`に入っている”表”のことですから、
つまりは**何行何列の表**かを知りたいわけです。

そんな時は以下のように書けばOKです！

:::note info
#データフレームの大きさの確認
**`df.shape`**
:::
```python
#データフレームの大きさを確認
df.shape
```
そうすると出力は、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/e439c51f-559c-cc77-c642-97cd3cc1eba6.png)
となります。
この出力結果は行・列の順番に記載されています。

覚え方は『行列』でもいいですし、（x,y）と数学で記載するように
（x方向、y方向）つまり（行数、列数）でも良いんじゃないでしょうか？
#### 3\.基本統計量の表示
次に、読み込んだデータの統計量を簡単に確認する方法を紹介します。
ちなみに統計量とは、平均や最大値などのデータから計算できる値のことを言います。

:::note info
**基本統計量の表示**
**`df.describe()`**
:::
英語でdescribeは『〜を説明する。〜を描写する。』という意味ですので、覚えやすいですね！

では出力させてみましょうかね。
```python
#基本統計量の表示
df.describe()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/0d9bc7f6-3ea9-6988-5e86-621f0eb9279a.png)

簡単に表示された統計量についてまとめておきます。
| カラム名 | 説明 |
| :-----: | :---: |
|count|件数|
|mean|平均（算術平均）|
|std|標準偏差|
|min|最小値|
|25%|第１四分位数|
|50%|中央値（第２四分位数）|
|75%|第３四分位数|
|max|最大値|

数Ⅰまでに習ったものばかりですね。（今は中３で中央値をやってるらしい。。）

中央値は後程述べますので、第１四分位数・第３四分位数に関して少しだけ復習を、、、

第１四分位数：最小値と中央値（第２四分位数）の”中央値”
第３四分位数：中央値（第２四分位数）と最大値の”中央値”
でしたね？

こんな感じで、`describe()`を使うことで、
数行眺めただけでは得られない情報が一瞬で確認できるので、是非とも覚えておきましょう🥸
### データの選択とフィルタリング
#### 1\.列や行の選択
カラムを選択したい時は、

:::note info
**`df[['欲しいカラム名1', '欲しいカラム名2',...]]`**
:::

とするだけです！
このように、**DataFrame 型のオブジェクトに対して、`[]` でインデックスを指定することで対象カラムのデータのみ抽出可能**です。このとき、インデックスを対象カラム名のリスト `["カラム名1", "カラム名2", ...]` で与えなければならないことに注意しましょう。


それでは、早速乗客の名前と生存情報を抽出してみましょう！
```python
#NameとSurvivedのみ抽出
df[['Name', 'Survived']]
```
そうするとこんな感じ、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/139b07cf-9c00-aa79-ff56-dca1f03ebb7c.png)

簡単ですよね！！
`df[['Name', 'Survived']]`からNameとSurvivedのみしか出力されていませんね。

ちなみに３つ以上のカラムを出力したいときは、以下のようにリスト内に加えていけばOKです！
```python
#Name,Sex,Survivedのみ抽出
df[['Name', 'Sex', 'Survived']]
```
#### 2\. 条件に基づくフィルタリング
条件を課したい時は、以下のように記述します。

:::note info
**`df [df['条件を課すカラム名'] == num]`**　（等号成立）

**`df[df['条件を課すカラム名'] >= num]`** （以上）

 **`df[df['条件を課すカラム名'] < num]`**　（未満）
:::
Pythonを少し触ったことがある人なら分かるかもしれませんが、
**条件式**なのでif文と同様、＝を２つ付けるんですね〜

では、生存した乗客のデータのみピックアップしてみましょう。
言い換えれば、Survivedが1となる列のみ出力すればいいわけです！
```python
# Survivedが1の行だけを抽出
df[df['Survived'] == 1]
```

結果はこんな感じ、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/a7843727-4fcb-111d-8afb-2d4bc99cb926.png)

ご覧のとおり、Survivedの列は1しか無くなってしまいましたね👍

少し補足します。
`df['条件を課すカラム名'] == num`の部分だけを実行したらどうなるんでしょうか？
```python
df['Survived'] == 1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/721d33e7-097c-17bb-f9b4-0b48e929e0dd.png)

はい。条件式が成立したインデックス(行番号)がTrue, 不成立がFalseで表記されていますね！
つまり、前項と同様に`[]`でTrueとなるインデックスを指定しているわけです。

#### 3\.複数条件の組み合わせ
次は複数の条件を課してみましょう。
条件式を複数使用する場合は、以下のように論理演算子を用います‼️

:::note info
`df[(条件式１) 論理演算子 (条件式２)]`
:::

|　論理演算子　|　意味　|
|:-------:|:---:|
| & | 積集合（かつ条件）|
| ｜ | 和集合（または条件）|
| ^ | 排他的論理和（一方が真、他方が偽のもの）|

３つ目を除けば、「あ！これね！」って感じですよね！主に上２つがメインです。
自分の今やりたいことは、どの演算子でいけるかな？と考えてみて下さいね(^-^)

では例えば、
『Queenstownから乗船した女性』という条件は『EmbarkedがQ,かつSexがfemale』と言い換えできますね！

なので、
```python
# 生存した女性のデータを抽出
df[(df['Embarked'] == 'Q') & (df['Sex'] == 'female')]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/27934ce3-1b3b-0188-2850-0058a7235c03.png)

というわけですね！

今回はかつ条件（積集合）なので&でしたが、
または条件（和集合）であれば | に置き換えればオッケーです⭕️

どうですか？少しずつ幹らしきモノが見えてきましたか？

**これまでと同様にどうやってインデックスを指定するかですよね！**

#### 4\.行・列の削除
さて、次は行・列の削除について扱います。
上でやったように、カラムの要素に条件を与えることで『削除』と同義になりますが、

今回は`drop`メソッドを使ってやってみましょう☝️
使い方は以下の通り！

:::note info
**＜行の削除＞**
`df.drop(["削除したい行のindex番号１", "削除したい行のindex番号2",・・・], axis=0) `
⇨index指定の場合は`リスト型[]`に！

**＜列の削除＞**
①消したいカラム（列）が１つの場合
`df.drop("削除したい列のカラム名１", axis=1) `

②消したいカラム（列）が複数の場合
`df.drop(["削除したい列のカラム名１", "削除したい列のカラム名2",・・・], axis=1) `
:::
:::note warn
drop**メソッド**ですので、`drop[]`としないように注意⚠️
:::
:::note warn
列の削除の場合でも、複数カラムの場合は`リスト型[]`で指定しないといけないので注意⚠
:::
上記のことに注意して、まずは**先頭の５行**を消してみましょう。

```python
#リストは０番スタートなので先頭５個は０〜４
df.drop([0, 1, 2, 3, 4], axis=0)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/82b1745a-23fe-1224-8825-4f718e828d32.png)
０〜４のindexが削除されていますよね！


次に列（カラム）を複数削除してみましょうか。
まあ何でもいいんですが、
個人情報になり得そうな名前、性別、年齢、チケット番号を消しましょう。
（本当に何でもいいですよ）

```python
#Name,Sex,Age,Ticketを削除
df.drop(["Name", "Sex", "Age", "Ticket"], axis=1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/96c86d3a-b844-5010-b46d-47bf159d1c30.png)
消したかった４つのカラムが消えていますよね！

メソッドを使うとより直感的に理解できるのではないでしょうか？

### 欠損値の取扱い
#### 1\.欠損値の検出
先から出力されたデータフレームを見ていると、NaNが目に入ってきません？
これが欠損値と呼ばれものです。まあ、情報がないってことですよ！
（発生原因は、入力忘れや無回答、システム上の問題など様々）


まずは、各カラムに欠損値がどれだけあるかを確認してみましょう！
```python
#欠損値の検出
missing_values_per_column = df_data.isnull().sum()

#欠損値のあるカラムを出力
missing_values_per_column
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/fa8e5565-4e16-ef80-bd2b-55a8b2f6da14.png)

言うまでもありませんが、一応まとめときましょうか。。

年齢（Age）不明が１７７人
部屋番号（Cabin)不明が６８７人
乗船した港（Embarked）不明が２人

こんな感じです。


#### 2\.欠損値の補完と削除
欠損値を把握したら、今度は補完（妥当と思われる値を入れること）してみましょう！
今回は年齢（Age）を平均値で補完してみようと思います！
```python
# 年齢の平均値を計算
average_age = df_data['Age'].mean()

# 年齢の欠損値を平均値で補完
df_data['Age'] = df_data['Age'].fillna(average_age)

#欠損値の検出
missing_values_per_column = df_data.isnull().sum()

#欠損値のあるカラムを出力
missing_values_per_column
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/032fb5a9-ee58-503b-ae05-3dbc3e96a20e.png)

先程まで１７７個も欠損値があったAgeが０個になっていますね！

コードの説明ですが、
`.mean()`で平均値の計算　　
`.fillna()`で欠損値の補完をしているわけです！

#### 3\.欠損値の統計情報
少々ここで脱線します。（一回休憩☕️）

機械学習を学び始めたばかりだと私を含め、
『欠損値はない方がいい！！埋めなければ！！！！』なんて思ったりもします😅

しかし必ずしもそうではありません！
例えば、
・ユーザーがある意図を持ってアンケートで空欄にした
・駐車場の有無を1,0で表していたが、nullはデータの入手不可を意味していた

つまり、
**欠損値であることが有効な情報になることもあるのです！**

だからこそいきなり欠損値補完するのではなく、**一回思考を踏む必要がある**わけですね。
無視するのがいいか、除外するのがいい、それらしい値を入れるのか、これが重要です。
取る方法によっては、大きなバイアスのある結果を与え、誤った意思決定に繋がってしまい、大きな損失を被る可能性があります。

ですから、より慎重に。。。

って、私も尊敬する先輩から教わりました笑笑


### データの集約
さてさて、ここではよく使う集約メソッド４つ紹介します。
| カラム名 | 説明 |
| :-----: | :---: |
|`count()`|件数|
|`sum()`|合計|
|`mean()`|平均（算術平均）|
|`median()`|中央値（第２四分位数）|
|`max()`|最大値|
|`min()`|最小値|
|`corr()`|相関係数|

では実際に使っていきましょう！！
#### 1\.件数(count)
countメソッドを使って、生存者者数・非生存者数を出してみましょう。
```python
# 生存者と非生存者の人数を計算
survived_count = df_data[df_data['Survived'] == 1]['PassengerId'].count()
not_survived_count = df_data[df_data['Survived'] == 0]['PassengerId'].count()

# 結果を表示
print("生存者の人数:", survived_count)
print("非生存者の人数:", not_survived_count)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/1145a1d0-b1ec-1582-7797-b39fcfced538.png)

#### 2\.合計(sum)
『欠損値の補完（欠損値の検出）』のとこでしれっと登場していましたが、
もう一度確認しときます！
今回はFare（料金）の合計値でも出してみます。
```python
# Fare列の値の合計を計算
total_fare = df['Fare'].sum()

print(f'Total Fare: {total_fare}')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/f309df08-24b3-102f-e42d-102001bafc02.png)
最近の為替で円に直せば大体400万程度といったところでしょうか。

やはり、物価は当時に比べたら遥かに上がっていますね〜


#### 3\.平均(mean)
はい！！！
一個前の節では年齢の平均を扱いましたので、今回はチケット代を平均してみましょう。
```python
# Fareの平均を計算
average_fare = df_data['Fare'].mean()
# Fareの平均を小数点第一位まで丸める
average_fare = round(average_fare,1)

# 結果を表示
print("Fareの平均:", average_fare)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/23ed4914-bb13-dc8b-5842-3290b7092765.png)

ちょっと知識の補充がてらroundメソッドを使用してみました😎
使い方は小数第N位に丸めたいとするなら、`round(丸めたい数字,N)`とすればいいです！

#### 4\.中央値(median)
平均値の次は中央値です。
何となく似たような値かな？なんて高校生の頃は思ったりもしましたが、実はちがう。。。
```python
# Fareの中央値を計算
median_fare = df_data['Fare'].median()
# Fareの中央値を小数点第一位まで丸める
median_fare = round(median_fare,1)

# 結果を表示
print("Fareの中央値:", median_fare)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/37f184a5-04f4-8980-73bb-f58658b67c18.png)

平均値と中央値でどちらを選ぶべきかという話を少しだけしましょう。
結論、**外れ値の影響を受けやすいかどうか**です！

「5人が物理のテストを受けた結果、
　4人が10点以下を取ったが1人だけ100点だった。」

これで平均値が機能しないのは何となく想像できますよね？
え！？じゃあ少し実験してみましょう。

**『A：100点　B：5点　Cさん：2点　Dさん:8点　Eさん：10点』**
だったとします。この状況で平均値と中央値それぞれ求めると、

**平均値：25点 中央値：8点**

さあ、普通の人がこのテスト受けた時に取りそうな点数はどちらの値ですか？
中央値の方ですよね！

このように上のような事象では中央値の方が、私たちの持つ『平均』のイメージとなるのです。
以下のように覚えておいて下さいね！

:::note info
中央値は平均値に比べ、外れ値の影響を受けいにくい
:::

#### 5\.最大値・最小値(max・min)
では次に乗客の最年長、最年少を求めてみましょう。

```python
# Ageの最大値と最小値を計算
max_age = df_data['Age'].max()
min_age = df_data['Age'].min()

# 結果を表示
print("Ageの最大値:", max_age)
print("Ageの最小値:", min_age)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/0b343083-3a9d-7730-2f9d-3bde92040f46.png)

なんか不思議な値が出てきましたね😅
おそらく、生後５ヶ月という意味なのでしょう。

#### 6\.相関係数(corr)
では集約メソッドの締めくくりに年齢（Age）と料金（Fare）の相関係数を算出しましょう。
相関係数は-1から1の値を取り、1に近いほど**正の相関**,-1に近いほど**負の相関**が強いと言えます。
よく使う指標ですので、思い出しておくと良いでしょう。

```python
# Age列とFare列の相関係数を計算
correlation = df[['Age', 'Fare']].corr()

print(correlation)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/6bcf46d4-738f-33b0-1f9b-f3d71b6d4a7a.png)
表の見方ですが、行名と列名が異なる値がその二つの相関係数です。

今回はAgeとFareの相関係数が0.096067ですので一応正の相関ですね？

私は年配の乗船客の方が高めの運賃を払っているかと予想していましたが、
あながちそうとも言い切れない微妙な結果でした・・・😭


### その他の実用的なメソッド
Pandasの文法紹介の締めくくりは、これまでで紹介できなかったデータ操作について扱います。この章もあと少しです。頑張りましょう！
#### 1\.並びかえ（ソート）
「ソート」とは、データをある基準に基づいて順番に並べ替えることを指します。これにより、データが整理され、特定のパターンや傾向を見つけやすくなります。

:::note info
①要素を並べたいカラムが１つ
`df.sort_values(by='要素を並べ替えたいカラム', ascending=True)`

②要素を並べ替えたいカラムが複数
`df.sort_values(by=['要素を並べ替えたいカラム1', '要素を並べ替えたいカラム2',・・・], ascending=True)`
:::

:::note info
・昇順（1, 10, 100, 1000）の場合
`ascending=True`

・降順（1000, 100, 10, 1）の場合
`ascending=False`
:::
:::note warn
複数のカラムについて並べ替える際、リスト内に記述したカラム順が優先順位となる。
:::
`drop`メソッドのときと同様に、列の場合も複数選択の場合はlistで入れることを忘れないでください！

では、今回は年齢と料金の昇順ソートを行ってみましょう。
```python
#Age列とFare列で昇順ソート（Age優先）
df.sort_values(by=['Age', 'Fare'], ascending=True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/9bad6532-46b5-5ab1-d279-c4ab29226565.png)

indexが644,831のところを見てみましょう。
ここでは、Ageの昇順が優先されているので、Fareの大小が逆転していますよね？

このように複数カラムでソートする場合は、リスト内に記載する順番にも気を付けなければなりません。

#### 2\.グループ化
Pandasではグループ化を`groupby`メソッドを用いることで行うことが出来ます。

そもそもグループ化は何ぞやという人のために説明すると、
例えば『性別ごとの年齢の平均』を出したいとします。

この時の『〜ごとの、〜ごとに』をコード上で表現するために使われるのが、
`groupby`メソッドです。

:::note info
①単独のカラムでグループ化したい場合
`df.groupby('グループ化したいカラム')`

②複数のカラムでグループ化したい場合
`df.groupby(['グループ化したいカラム1', 'グループ化したいカラム2',・・・])`

:::
はい、毎度恒例の複数のカラム選択の場合はリスト化ってやつですね！

今回は、乗客の**性別ごと**の平均年齢を出してみたいと思います。
```python
#性別ごとの平均年齢の算出
df.groupby('Sex')['Age'].mean()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/f6de0c2c-a555-a26a-f651-388346016936.png)
コードが少々見づらい、理解しづらいと思う方は`df.groupby('Sex')`で一つの塊の`df`と考えれば、納得できるのではないでしょうか？

#### 3\.カラム名の変更
元データのカラム名を変更して、より直感的にわかりやすいカラム名に変更したいことは、
しばしばあることです。そんな時は`rename`メソッドを使用します。

:::note info
`df.rename(columns={'変更前のカラム名': '変更後のカラム名'}, inplace=True)`
:::

:::note warn
`inplace=True`:元の`df`が変更され、新しい`df`を作成せずに変更が反映される。
`inplace=False`:新しい`df`が返されるが、元の`df`は変更されない。
:::
では、PclassをPassengerClassにSexをGenderに変更してみましょう。
```python
#PclassとSexのカラム名を変更
df.rename(columns={'Pclass': 'PassengerClass', 'Sex': 'Gender'}, inplace=True)

#新しいdfの出力
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/16e113fd-3928-3a5d-f9f1-22f835d8d750.png)
カラム名が変更されていることが確認できますね！
`inplace`の`True`と`False`はしっかりと区別しましょう。


#### 4\.locとilocを使った行・列指定
`loc`と`iloc`を使用すれば、より詳細に行・列の指定がしやすくなります。

**`loc`メソッド:**
ラベルベースのインデックスを使用してデータを選択します。

:::note info
2~5までの行とカラム１とカラム２の列を選択
`df.loc[2:5, ['カラム1', 'カラム2']]`
:::

**`iloc`メソッド:**
整数ベースのインデックスを使用してデータを選択します。

:::note info
2~5までの行とカラム１(１列目)とカラム２（２列目）の列を選択
`df.loc[2:6, [0, 1]]`
:::
それでは、両方のメソッドを使用して「2~5行目までのPclass,Name,Ageの列」を抽出しましょう。
```python
# locメソッドを使用して特定の行と列を選択
df.loc[2:5, ['Pclass', 'Name', 'Age']]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/2cab34a9-2d68-67ad-22f8-07f4d764d9bc.png)

```python
#ilocメソッドを使用して特定の行と列を選択
df.iloc[2:6, [2, 3, 5]]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/ccaa2de6-7663-b931-ea9b-343edc42b7fa.png)

#### 5\.queryメソッドによる抽出
この章の最後に`query`メソッドを扱います。
『条件に基づくフィルタリング』で扱った内容とゴールは同じですが、書き方が少々異なります。
SQLに慣れている人は、このメソッドの方が使いやすいのではないでしょうか？

:::note info
`df.query("条件式")`
:::
それでは、『条件に基づくフィルタリング』の時と同様に「生存した女性」をQueryメソッドを使って出してみましょう。
```python
#queryメソッドを使った抽出(フィルタリング)
df.query("Sex == 'female' and Survived == 1")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/7d261427-aaae-4a9b-6e2e-800f7732218d.png)


ちゃんと狙い通りのフィルタリングがされていますね！
ちなみに和集合(または条件)の時は`or`とします。これも復習ですね？

#### 6\.文字列の数値化
この章の締めくくりに文字列の数値化を行いたいと思います。
具体的にはSexのカラムがmale→１,female→0とするわけです。これは機械学習よりの話になっていしまいますが、機械学習をさせるには読み込む特徴量は**文字列ではなく数字**でなければなりません。

このように文字列で表現されたカテゴリを、
値(数字)に変化することを**カテゴリ変数化**と言います。

```python
from sklearn.preprocessing import LabelEncoder

# Sexを数値化
le_sex = LabelEncoder()
df_data['Sex'] = le_sex.fit_transform(df['Sex'])

# 数値化後のデータを10個表示
df_data.head(10)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/a5c1a296-b715-5f9a-ff6e-ab5c5c3861fa.png)

性別のカラムが数字で表現されていますね！
こちらに関しては、データ分析というよりは機械学習で求められる操作ですね。

これから先のために覚えておいて下さい😎

# 第３章：データのグラフ化
### 下準備
この章から遂にデータの可視化に入っていきます。
以下のライブラリをインポートしておきましょう！
```python
import seaborn as sns
import matplotlib.pyplot as plt
```

:::note info
**matplotlib**:
Pythonのグラフの描画ためのライブラリです。Pandasだけでも簡単な描画はできますが、matplotlibを使うとより柔軟なグラフ描画が可能になります。

**seaborn**:
matplotlibをベースとした、データ可視化ライブラリです。matplotlib単体よりも、さらに豊富なパターンの可視化機能を有しており、かつ美しく鮮明な見た目も特徴です。
:::
これらのライブラリを組み合わせて使用することで、データの可視化や解析を効果的かつ美しく行うことができます🤩
### 棒グラフ
まず最初は皆さんお馴染みの棒グラフです。
カテゴリー毎の数量の比較や時系列データなど幅広く活躍してくるグラフです。

今回は乗客の社会経済的地位を示すクラス（Pclass）を横軸、人数を縦軸として出力してみます。
```python
# Pclassごとの乗客数を取得
pclass_counts = df_data['Pclass'].value_counts()

# 棒グラフの作成
plt.figure(figsize=(8, 6))
sns.barplot(x=pclass_counts.index, y=pclass_counts.values, palette="viridis")
plt.title('Passenger Count by Pclass')

#軸のタイトルを命名
plt.xlabel('Pclass')
plt.ylabel('Passenger Count')

#棒グラフ出力
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/b5460818-9aca-2232-d9b6-dde028eeacf3.png)

:::note info
長所
・視覚的な理解が容易　・異なるカテゴリー間の比較が容易　
・時系列データにも使える
:::
:::note warn
短所
・比率の情報が薄い　・連続データには向かない　・大量のデータには向かない
:::
使用例としては、『各地域の年間降水量の比較』や『異なるブランドの製品売上の比較』などの**カテゴリー分けされ且つ絶対量で見たいもの**に最適です。

### ヒストグラム
ヒストグラムは度数分布表の度数を縦軸、階級を横軸に持ってきたものです。

では横軸を年齢、縦軸を人数にとして出力してみましょう。
```python
# Ageのヒストグラムを作成
#　サイズを指定
plt.figure(figsize=(8, 6))
sns.histplot(df_data['Age'].dropna(), bins=30, kde=False, color='skyblue')
plt.title('Histogram of Age')
plt.xlabel('Age')
plt.ylabel('Frequency')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/0c0b1bc5-133d-de7f-db6e-82a59df3293a.png)

:::note info
長所
・分布の理解が容易　・中央傾向が把握しやすい　・異常値の発見が容易
:::
:::note warn
短所
・階級の選択の影響を受ける　・個々データの損失　・外れ値への敏感性
:::
使用例としては、『人口の年齢分布』や『製品の品質管理における寸法のばらつき』などの、**データに幅を持たせての"小さいグループ(階級)"ごとの比較**に便利です。
### 円グラフ
こちらもお馴染みのグラフですね。
直感的にわかりやすい一方で、項目（カテゴリー）が増えると非常に見辛くなりますから注意です。

では、男女の比率を円グラフで見てみましょう。
```python
# Sexごとのデータを集計
sex_counts = df_data['Sex'].value_counts()

# 円グラフの作成
plt.figure(figsize=(4, 4))
plt.pie(sex_counts, labels=sex_counts.index, autopct='%1.1f%%', startangle=90, colors=['skyblue', 'lightcoral'])
#タイトルの命名
plt.title('Passenger Distribution by Sex')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/7bafb1de-0b92-6f74-e42d-4e4483907bf1.png)

:::note info
長所
・直感的に理解できる　・比率が瞬時に読み取れる
:::
:::note warn
短所
・絶対量の情報が消える　・視覚的に項目数に限度がある　
・不連続データに向かない ・僅かな比率の差が捉えにくい 
:::
使用例としては、『企業の売上高における各製品ラインの割合』や『市場調査での消費者の好みの分布』などの**少数カテゴリー分類されるもの**に最適です。
### 箱ひげ図
なんか響きが懐かしいですよね箱ひげ図。。
私は高校の先生に、箱から髭が生えているから「箱ひげ図」なんだと教わりましたが、縦線が線が全て髭なんだみたいな教わり方もあるようです。

見方としては、縦線が重要です。縦線を左から見ていって、
1本目：最小値　2本目：第一四分位数　
3本目：中央値　4本目：第三四分位数　5本目：最大値

ヒストグラムなどを描かずとも、山がどの辺りで膨らんでいるのか把握できます。

それでは、箱ひげ図で年齢の分布を見ていきましょう。
```python
# Ageの箱ひげ図を作成
plt.figure(figsize=(8, 3))
sns.boxplot(x='Age', data=df, orient='h', color='skyblue')
#タイトルの命名
plt.title('Boxplot of Age')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/f980ee1c-a2b2-5f33-3c28-5209f3da878a.png)

:::note info
長所
・複数のデータセットの比較に最適　・分散, 外れ値, 中央値を示せる
:::
:::note warn
短所
・前提知識を必要とする　・平均値や最頻値は直接示せない
:::
出力結果に関してですが、最大値を示す右側にプロットされている⚪︎は外れ値を示しています。

使用例としては、『異なるクラスの試験成績の比較』や『複数地域の気温変動の比較』などの**異なるグループ間の比較**に最適です。
### 散布図
続いては散布図です。
集約メソッドの所で扱った相関係数と一緒に使われるグラフです。つまりこのグラフは二つの変数の相関関係を調べるときに役立ちます。

では、横軸を年齢, 縦軸を乗船料金として散布図を描いてみましょう。
```python
# AgeとFareの散布図を作成
plt.figure(figsize=(8, 6))
sns.scatterplot(x='Age', y='Fare', data=df, color='coral', alpha=0.5)
plt.title('Scatter plot of Age vs Fare')
plt.xlabel('Age')
plt.ylabel('Fare')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/ef00973d-9345-b9db-5f7f-206424d5bf9b.png)

:::note info
長所
・二つの変数の関係が視覚的に理解できる　
・データのパターンや傾向を把握しやすい
:::
:::note warn
短所
・三つ以上の変数を扱うのが難しい　・データのプロットが重なると読みづらい
:::

今回、「若い人ほど安いチケットを買って、年配の人ほど高いチケットが買える」という正の相関を予測して、カラム選択を行いましたが、ちょっと予測が外れてしまいましたね😭

集約メソッドの相関係数のところで紹介しましたが、正の相関が強い程データのプロットは傾きが１の直線上に並び、負の相関が強い程傾きが−１の直線上に並びます！

### 散布図行列
さあ、やってきました最後の学習項目です！ここまで一気に通した方はお疲れかと思いますが、本当に最後です！私もこれまで通り、気合を入れて書いておりますので、最後の最後までお付き合いよろしくお願いしますm(__)m

散布図の短所のところで、『三つ以上の変数を扱うのが難しい』と言いました。確かに、全ての変数に対して散布図を出力するのはあまりに面倒過ぎますよね？途中で「あれ？何やってっけ？」となるのが目に見えてますね？笑

そんな時に使える方法をご紹介したいと思います！以下のように入力してみましょう！
```python
# 散布図行列を作成
sns.pairplot(df)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624546/9e9cf054-4d4f-25b0-0963-652b2b5742a2.png)
なんか凄いですよね笑笑
見てわかる通り、**全てのカラムの組み合わせによる散布図**がこんなに短い手順で出力されてしまいました！！

これを使えば、今まで気付けなかった意外な相関関係にも気付けそうですよね👍


# 第４章：最後に
いかがだったでしょうか？
Pandasは結構直感的になんとなく理解しやすいものですので、もういけそうな感じがしませんか？
今回の記事でPandasの全てのメソッドを扱ったわけではありませんが、これから自学自習するフェーズにおいて、何も手が出せないということはなくなったはずです。あとは、皆さんが各々知識の補填をしていくことで、本当にPandasをものにしたと言えます。
次は実際にKaggleでタイタニック号の生存予測のモデルを組んでも良いですし、統計学をPythonを使いながら学んでいっても良いでしょう。

どの道にしろ、やっぱり手を動かすことが大事です！
自分の手で「この部分だけ実行したらどうなるんだ？」とか「ここを変えたらどうなる？」とある種の実験をしていきます。そうすると「あ！こういうことかも！」と、またある種の仮説が出来ます。その仮説が芯を食う場合もあれば、また他の混乱を呼ぶこともあります。しかし、この過程を踏むことが何だかんだ近道であると私は思います。ですから、暗記暗記と考えず「まぁそのうち覚えるでしょ」という楽な感覚で取り組んでみてください。

ここまでお付き合い頂き、ありがとうございました。

::: note
弊社Nucoでは、他にも様々なお役立ち記事を公開しています。よかったら、Organizationのページも覗いてみてください。
また、Nucoでは一緒に働く仲間も募集しています！興味をお持ちいただける方は、[こちら](https://www.recruit.nuco.co.jp/?qiita_item_id=10819d3dc10f4c0807f2)まで。
:::
https://www.recruit.nuco.co.jp/?qiita_item_id=10819d3dc10f4c0807f2