polarsを使ってspaceship titanicを提出する

# はじめに  
## polarsとは

[polars](https://www.pola.rs)はpythonで使える高速のデータフレームライブラリです。
データフレームライブラリといえばpandas🐼が有名ですが、それに対するpolars🐻‍❄️です。
今回はpolarsに慣れるため、kaggleのコンペ[Spaceship Titanic](https://www.kaggle.com/competitions/spaceship-titanic/overview)に、結果を提出してみます。

## やること

- テーブルデータの読み書き
- 平均、標準偏差などの基本統計量の確認
- ユニークな要素の確認
- 欠損値の処理

## やらないこと

- コンペのスコアは気にしません
- 高度な解析は行いません

## 事前準備 

実行環境にはgoogle colaboratoryを使用します。 
また、colab上でkaggle apiを叩いてデータのダウンロードや提出を行います。 
下記ページがわかりやすかった。
https://take-tech-engineer.com/kaggle-colab-api/

# やってみる

## csvファイルのリード　

```python 
import polars as pl
ldf = pl.scan_csv("train.csv")
```

## 先頭、末尾の行を見る

```python 
print('head', ldf.head(10).collect())
print('teil', ldf.tail(10).collect())

# result
'''
head shape: (5, 14)
┌─────────────┬────────────┬───────────┬───────┬───┬────────┬────────┬───────────────┬─────────────┐
│ PassengerId ┆ HomePlanet ┆ CryoSleep ┆ Cabin ┆ … ┆ Spa    ┆ VRDeck ┆ Name          ┆ Transported │
│ ---         ┆ ---        ┆ ---       ┆ ---   ┆   ┆ ---    ┆ ---    ┆ ---           ┆ ---         │
│ str         ┆ str        ┆ bool      ┆ str   ┆   ┆ f64    ┆ f64    ┆ str           ┆ bool        │
╞═════════════╪════════════╪═══════════╪═══════╪═══╪════════╪════════╪═══════════════╪═════════════╡
│ 0001_01     ┆ Europa     ┆ false     ┆ B/0/P ┆ … ┆ 0.0    ┆ 0.0    ┆ Maham         ┆ false       │
│             ┆            ┆           ┆       ┆   ┆        ┆        ┆ Ofracculy     ┆             │
│ 0002_01     ┆ Earth      ┆ false     ┆ F/0/S ┆ … ┆ 549.0  ┆ 44.0   ┆ Juanna Vines  ┆ true        │
│ 0003_01     ┆ Europa     ┆ false     ┆ A/0/S ┆ … ┆ 6715.0 ┆ 49.0   ┆ Altark Susent ┆ false       │
│ 0003_02     ┆ Europa     ┆ false     ┆ A/0/S ┆ … ┆ 3329.0 ┆ 193.0  ┆ Solam Susent  ┆ false       │
│ 0004_01     ┆ Earth      ┆ false     ┆ F/1/S ┆ … ┆ 565.0  ┆ 2.0    ┆ Willy         ┆ true        │
│             ┆            ┆           ┆       ┆   ┆        ┆        ┆ Santantines   ┆             │
└─────────────┴────────────┴───────────┴───────┴───┴────────┴────────┴───────────────┴─────────────┘
teil shape: (5, 14)
┌───────────┬────────────┬───────────┬──────────┬───┬────────┬────────┬──────────────┬─────────────┐
│ Passenger ┆ HomePlanet ┆ CryoSleep ┆ Cabin    ┆ … ┆ Spa    ┆ VRDeck ┆ Name         ┆ Transported │
│ Id        ┆ ---        ┆ ---       ┆ ---      ┆   ┆ ---    ┆ ---    ┆ ---          ┆ ---         │
│ ---       ┆ str        ┆ bool      ┆ str      ┆   ┆ f64    ┆ f64    ┆ str          ┆ bool        │
│ str       ┆            ┆           ┆          ┆   ┆        ┆        ┆              ┆             │
╞═══════════╪════════════╪═══════════╪══════════╪═══╪════════╪════════╪══════════════╪═════════════╡
│ 9276_01   ┆ Europa     ┆ false     ┆ A/98/P   ┆ … ┆ 1643.0 ┆ 74.0   ┆ Gravior      ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Noxnuther    ┆             │
│ 9278_01   ┆ Earth      ┆ true      ┆ G/1499/S ┆ … ┆ 0.0    ┆ 0.0    ┆ Kurta        ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Mondalley    ┆             │
│ 9279_01   ┆ Earth      ┆ false     ┆ G/1500/S ┆ … ┆ 1.0    ┆ 0.0    ┆ Fayey Connon ┆ true        │
│ 9280_01   ┆ Europa     ┆ false     ┆ E/608/S  ┆ … ┆ 353.0  ┆ 3235.0 ┆ Celeon       ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Hontichre    ┆             │
│ 9280_02   ┆ Europa     ┆ false     ┆ E/608/S  ┆ … ┆ 0.0    ┆ 12.0   ┆ Propsh       ┆ true        │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Hontichre    ┆             │
└───────────┴────────────┴───────────┴──────────┴───┴────────┴────────┴──────────────┴─────────────┘
'''
```

## 基本統計量を表示する

データの総数、nullデータの数、平均、標準偏差、最大、最小、中央値、25パーセンタイル、75パーセンタイルの値が確認できる。

```python
print(ldf.collect().describe())

# result
'''
shape: (9, 15)
┌───────────┬───────────┬──────────┬───────────┬───┬───────────┬───────────┬───────────┬───────────┐
│ describe  ┆ Passenger ┆ HomePlan ┆ CryoSleep ┆ … ┆ Spa       ┆ VRDeck    ┆ Name      ┆ Transport │
│ ---       ┆ Id        ┆ et       ┆ ---       ┆   ┆ ---       ┆ ---       ┆ ---       ┆ ed        │
│ str       ┆ ---       ┆ ---      ┆ f64       ┆   ┆ f64       ┆ f64       ┆ str       ┆ ---       │
│           ┆ str       ┆ str      ┆           ┆   ┆           ┆           ┆           ┆ f64       │
╞═══════════╪═══════════╪══════════╪═══════════╪═══╪═══════════╪═══════════╪═══════════╪═══════════╡
│ count     ┆ 8693      ┆ 8693     ┆ 8693.0    ┆ … ┆ 8693.0    ┆ 8693.0    ┆ 8693      ┆ 8693.0    │
│ null_coun ┆ 0         ┆ 201      ┆ 217.0     ┆ … ┆ 183.0     ┆ 188.0     ┆ 200       ┆ 0.0       │
│ t         ┆           ┆          ┆           ┆   ┆           ┆           ┆           ┆           │
│ mean      ┆ null      ┆ null     ┆ 0.358306  ┆ … ┆ 311.13877 ┆ 304.85479 ┆ null      ┆ 0.503624  │
│           ┆           ┆          ┆           ┆   ┆ 8         ┆ 1         ┆           ┆           │
│ std       ┆ null      ┆ null     ┆ 0.47953   ┆ … ┆ 1136.7055 ┆ 1145.7171 ┆ null      ┆ 0.500016  │
│           ┆           ┆          ┆           ┆   ┆ 35        ┆ 89        ┆           ┆           │
│ min       ┆ 0001_01   ┆ Earth    ┆ 0.0       ┆ … ┆ 0.0       ┆ 0.0       ┆ Aard      ┆ 0.0       │
│           ┆           ┆          ┆           ┆   ┆           ┆           ┆ Curle     ┆           │
│ max       ┆ 9280_02   ┆ Mars     ┆ 1.0       ┆ … ┆ 22408.0   ┆ 24133.0   ┆ Zubeneb   ┆ 1.0       │
│           ┆           ┆          ┆           ┆   ┆           ┆           ┆ Pasharne  ┆           │
│ median    ┆ null      ┆ null     ┆ 0.0       ┆ … ┆ 0.0       ┆ 0.0       ┆ null      ┆ 1.0       │
│ 25%       ┆ null      ┆ null     ┆ null      ┆ … ┆ 0.0       ┆ 0.0       ┆ null      ┆ null      │
│ 75%       ┆ null      ┆ null     ┆ null      ┆ … ┆ 59.0      ┆ 46.0      ┆ null      ┆ null      │
└───────────┴───────────┴──────────┴───────────┴───┴───────────┴───────────┴───────────┴───────────┘
'''
```

# nullが含まれない行を抽出する

```python 
print(ldf.drop_nulls().collect())

#result
'''
shape: (6_606, 14)
┌───────────┬────────────┬───────────┬──────────┬───┬────────┬────────┬──────────────┬─────────────┐
│ Passenger ┆ HomePlanet ┆ CryoSleep ┆ Cabin    ┆ … ┆ Spa    ┆ VRDeck ┆ Name         ┆ Transported │
│ Id        ┆ ---        ┆ ---       ┆ ---      ┆   ┆ ---    ┆ ---    ┆ ---          ┆ ---         │
│ ---       ┆ str        ┆ bool      ┆ str      ┆   ┆ f64    ┆ f64    ┆ str          ┆ bool        │
│ str       ┆            ┆           ┆          ┆   ┆        ┆        ┆              ┆             │
╞═══════════╪════════════╪═══════════╪══════════╪═══╪════════╪════════╪══════════════╪═════════════╡
│ 0001_01   ┆ Europa     ┆ false     ┆ B/0/P    ┆ … ┆ 0.0    ┆ 0.0    ┆ Maham        ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Ofracculy    ┆             │
│ 0002_01   ┆ Earth      ┆ false     ┆ F/0/S    ┆ … ┆ 549.0  ┆ 44.0   ┆ Juanna Vines ┆ true        │
│ 0003_01   ┆ Europa     ┆ false     ┆ A/0/S    ┆ … ┆ 6715.0 ┆ 49.0   ┆ Altark       ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Susent       ┆             │
│ 0003_02   ┆ Europa     ┆ false     ┆ A/0/S    ┆ … ┆ 3329.0 ┆ 193.0  ┆ Solam Susent ┆ false       │
│ …         ┆ …          ┆ …         ┆ …        ┆ … ┆ …      ┆ …      ┆ …            ┆ …           │
│ 9278_01   ┆ Earth      ┆ true      ┆ G/1499/S ┆ … ┆ 0.0    ┆ 0.0    ┆ Kurta        ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Mondalley    ┆             │
│ 9279_01   ┆ Earth      ┆ false     ┆ G/1500/S ┆ … ┆ 1.0    ┆ 0.0    ┆ Fayey Connon ┆ true        │
│ 9280_01   ┆ Europa     ┆ false     ┆ E/608/S  ┆ … ┆ 353.0  ┆ 3235.0 ┆ Celeon       ┆ false       │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Hontichre    ┆             │
│ 9280_02   ┆ Europa     ┆ false     ┆ E/608/S  ┆ … ┆ 0.0    ┆ 12.0   ┆ Propsh       ┆ true        │
│           ┆            ┆           ┆          ┆   ┆        ┆        ┆ Hontichre    ┆             │
└───────────┴────────────┴───────────┴──────────┴───┴────────┴────────┴──────────────┴─────────────┘
'''
```

## nullが含まれる行を抽出する

カラムごとにnullチェックを実施して表示。

```python
for c in ldf.columns:
  n_ldf = ldf.filter(pl.col(c).is_null())
  print(c, n_ldf.collect())

# result
'''
PassengerId shape: (0, 14)
┌─────────────┬────────────┬───────────┬───────┬───┬─────┬────────┬──────┬─────────────┐
│ PassengerId ┆ HomePlanet ┆ CryoSleep ┆ Cabin ┆ … ┆ Spa ┆ VRDeck ┆ Name ┆ Transported │
│ ---         ┆ ---        ┆ ---       ┆ ---   ┆   ┆ --- ┆ ---    ┆ ---  ┆ ---         │
│ str         ┆ str        ┆ bool      ┆ str   ┆   ┆ f64 ┆ f64    ┆ str  ┆ bool        │
╞═════════════╪════════════╪═══════════╪═══════╪═══╪═════╪════════╪══════╪═════════════╡
└─────────────┴────────────┴───────────┴───────┴───┴─────┴────────┴──────┴─────────────┘
HomePlanet shape: (201, 14)
┌─────────────┬────────────┬───────────┬──────────┬───┬───────┬────────┬─────────────┬─────────────┐
│ PassengerId ┆ HomePlanet ┆ CryoSleep ┆ Cabin    ┆ … ┆ Spa   ┆ VRDeck ┆ Name        ┆ Transported │
│ ---         ┆ ---        ┆ ---       ┆ ---      ┆   ┆ ---   ┆ ---    ┆ ---         ┆ ---         │
│ str         ┆ str        ┆ bool      ┆ str      ┆   ┆ f64   ┆ f64    ┆ str         ┆ bool        │
╞═════════════╪════════════╪═══════════╪══════════╪═══╪═══════╪════════╪═════════════╪═════════════╡
│ 0064_02     ┆ null       ┆ true      ┆ E/3/S    ┆ … ┆ 0.0   ┆ 0.0    ┆ Colatz Keen ┆ true        │
│ 0119_01     ┆ null       ┆ false     ┆ A/0/P    ┆ … ┆ 65.0  ┆ 6898.0 ┆ Batan       ┆ false       │
│             ┆            ┆           ┆          ┆   ┆       ┆        ┆ Coning      ┆             │
│ 0210_01     ┆ null       ┆ true      ┆ D/6/P    ┆ … ┆ 0.0   ┆ 0.0    ┆ Arraid      ┆ true        │
│             ┆            ┆           ┆          ┆   ┆       ┆        ┆ Inicont     ┆             │
│ 0242_01     ┆ null       ┆ false     ┆ F/46/S   ┆ … ┆ 283.0 ┆ 0.0    ┆ Almone Sté  ┆ false       │
│ …           ┆ …          ┆ …         ┆ …        ┆ … ┆ …     ┆ …      ┆ …           ┆ …           │
│ 9194_01     ┆ null       ┆ false     ┆ E/603/S  ┆ … ┆ 13.0  ┆ 3147.0 ┆ null        ┆ false       │
│ 9248_01     ┆ null       ┆ false     ┆ F/1792/S ┆ … ┆ 207.0 ┆ 0.0    ┆ Gian Perle  ┆ true        │
│ 9257_01     ┆ null       ┆ false     ┆ F/1892/P ┆ … ┆ 24.0  ┆ 0.0    ┆ Ties Apple  ┆ false       │
│ 9274_01     ┆ null       ┆ true      ┆ G/1508/P ┆ … ┆ 0.0   ┆ 0.0    ┆ Chelsa      ┆ true        │
│             ┆            ┆           ┆          ┆   ┆       ┆        ┆ Bullisey    ┆             │
└─────────────┴────────────┴───────────┴──────────┴───┴───────┴────────┴─────────────┴─────────────┘
以下略

'''

```

## 列の要素を確認

列ごとにユニークな要素を確認する。
全部出力すると大変なので headで先頭だけ表示する。

```python
for c in ldf.columns:
  unq_df = ldf.select(c).unique().collect()
  print(f"{c}\tsize:{len(unq_df)}\tsample{unq_df.head()}")

'''
PassengerId	size:8693	sampleshape: (5, 1)
┌─────────────┐
│ PassengerId │
│ ---         │
│ str         │
╞═════════════╡
│ 9179_03     │
│ 6399_01     │
│ 5535_03     │
│ 6593_02     │
│ 8521_01     │
└─────────────┘
HomePlanet	size:4	sampleshape: (4, 1)
┌────────────┐
│ HomePlanet │
│ ---        │
│ str        │
╞════════════╡
│ null       │
│ Earth      │
│ Europa     │
│ Mars       │
└────────────┘
以下略

'''


```

## 新たな特徴量を作る

Cabinは'/'で3つのブロックに分けられそう。

```python
def reconstruct(df):
  return df.with_columns(
      pl.col('Cabin')
      .str.splitn('/', 3)
      .struct.rename_fields(['Cabin_area', 'Cabin_number', 'Cabin_type'])
  ).unnest('Cabin').with_columns(
      pl.col('Cabin_number')
      .cast(int)
  )

appended_ldf = reconstruct(ldf).drop_nulls()
print(appended_ldf.select('PassengerId', 'Cabin_area', 'Cabin_number', 'Cabin_type').head(10).collect())

'''
shape: (10, 4)
┌─────────────┬────────────┬──────────────┬────────────┐
│ PassengerId ┆ Cabin_area ┆ Cabin_number ┆ Cabin_type │
│ ---         ┆ ---        ┆ ---          ┆ ---        │
│ str         ┆ str        ┆ i64          ┆ str        │
╞═════════════╪════════════╪══════════════╪════════════╡
│ 0001_01     ┆ B          ┆ 0            ┆ P          │
│ 0002_01     ┆ F          ┆ 0            ┆ S          │
│ 0003_01     ┆ A          ┆ 0            ┆ S          │
│ 0003_02     ┆ A          ┆ 0            ┆ S          │
│ …           ┆ …          ┆ …            ┆ …          │
│ 0006_01     ┆ F          ┆ 2            ┆ S          │
│ 0007_01     ┆ F          ┆ 3            ┆ S          │
│ 0008_01     ┆ B          ┆ 1            ┆ P          │
│ 0008_03     ┆ B          ┆ 1            ┆ P          │
└─────────────┴────────────┴──────────────┴────────────┘

'''


```

## データの分布を可視化する

列ごとにどの要素が多いか（少ないか）を集計する。
今回は関数を作ってみた。

```python 
def calc_distribution(data:pl.DataFrame, column_name:str) -> pl.DataFrame:
  return data.select(
      pl.col(column_name).value_counts()
      .struct.rename_fields(['value', 'count'])
  ).unnest(column_name)

print(calc_distribution(appended_ldf, "HomePlanet").collect())

'''
shape: (3, 2)
┌────────┬───────┐
│ value  ┆ count │
│ ---    ┆ ---   │
│ str    ┆ u32   │
╞════════╪═══════╡
│ Earth  ┆ 3566  │
│ Mars   ┆ 1367  │
│ Europa ┆ 1673  │
└────────┴───────┘
'''

```

## ダミー変数化する　

カテゴリデータはダミー変数にした方が扱いやすい。　
ダミー変換化したいcolumnをリストで指定して処理する例。

```python 
target_to_dummies = ["HomePlanet", "CryoSleep", "Cabin_area", "Cabin_type", "Destination", "VIP"]
dummied = appended_ldf.select(target_to_dummies).collect().to_dummies()
remaind = appended_ldf.drop(target_to_dummies).collect()
dummied_df = pl.concat([remaind, dummied], how="horizontal")
print(dummied_df)

'''
shape: (6_606, 30)
┌───────────┬────────────┬──────┬───────────┬───┬────────────┬──────────────┬───────────┬──────────┐
│ Passenger ┆ Cabin_numb ┆ Age  ┆ RoomServi ┆ … ┆ Destinatio ┆ Destination_ ┆ VIP_false ┆ VIP_true │
│ Id        ┆ er         ┆ ---  ┆ ce        ┆   ┆ n_PSO      ┆ TRAPPIST-1e  ┆ ---       ┆ ---      │
│ ---       ┆ ---        ┆ f64  ┆ ---       ┆   ┆ J318.5-22  ┆ ---          ┆ u8        ┆ u8       │
│ str       ┆ i64        ┆      ┆ f64       ┆   ┆ ---        ┆ u8           ┆           ┆          │
│           ┆            ┆      ┆           ┆   ┆ u8         ┆              ┆           ┆          │
╞═══════════╪════════════╪══════╪═══════════╪═══╪════════════╪══════════════╪═══════════╪══════════╡
│ 0001_01   ┆ 0          ┆ 39.0 ┆ 0.0       ┆ … ┆ 0          ┆ 1            ┆ 1         ┆ 0        │
│ 0002_01   ┆ 0          ┆ 24.0 ┆ 109.0     ┆ … ┆ 0          ┆ 1            ┆ 1         ┆ 0        │
│ 0003_01   ┆ 0          ┆ 58.0 ┆ 43.0      ┆ … ┆ 0          ┆ 1            ┆ 0         ┆ 1        │
│ 0003_02   ┆ 0          ┆ 33.0 ┆ 0.0       ┆ … ┆ 0          ┆ 1            ┆ 1         ┆ 0        │
│ …         ┆ …          ┆ …    ┆ …         ┆ … ┆ …          ┆ …            ┆ …         ┆ …        │
│ 9278_01   ┆ 1499       ┆ 18.0 ┆ 0.0       ┆ … ┆ 1          ┆ 0            ┆ 1         ┆ 0        │
│ 9279_01   ┆ 1500       ┆ 26.0 ┆ 0.0       ┆ … ┆ 0          ┆ 1            ┆ 1         ┆ 0        │
│ 9280_01   ┆ 608        ┆ 32.0 ┆ 0.0       ┆ … ┆ 0          ┆ 0            ┆ 1         ┆ 0        │
│ 9280_02   ┆ 608        ┆ 44.0 ┆ 126.0     ┆ … ┆ 0          ┆ 1            ┆ 1         ┆ 0        │
└───────────┴────────────┴──────┴───────────┴───┴────────────┴──────────────┴───────────┴──────────┘

'''

```

## 提出ファイルを作る　

値はランダム。
csvファイルを出力する前にpandasに変換したのは、提出ファイルのTrue, Falseの頭文字を大文字にするため。
polarsのままだと、true, falseとなってしまい、scoreが0になります。

```python 
import random

df = pl.read_csv('test.csv')
passenger = df.select('PassengerId')
transported = pl.DataFrame(
    {
        'Transported':[random.choice([True, False]) for _ in range(len(passenger))]
    }
)

submit = pl.concat([passenger, transported], how='horizontal')
submit.to_pandas().to_csv("submit.csv", index=False)

```

## 提出　

作ったファイルをkaggle APIを使用して提出する。

```sh
!kaggle competitions submit -c spaceship-titanic -f submit.csv -m "comment"
```

# 最後に　

大まかにやりたいことはできた。（と思う。）
早い、安い、うまいのでpolarsを流行らせたい！