polars-dsでEDAする

この記事は [Polars Advent Calendar 2024](https://qiita.com/advent-calendar/2024/polars) 2日目の記事です。  

# はじめに
今年の8月に開催された [Polars Data Crunch #3](https://polars-ja.connpass.com/event/325132/)で、polarsのudfとかpluginについてLTをしました。  
<script defer class="speakerdeck-embed" data-slide="1" data-id="7441cae43e1247cba1881888a869c64a" data-ratio="1.7777777777777777" src="//speakerdeck.com/assets/embed.js"></script>  

この発表で少し触れた、polars-dsのDIAの機能がEDAに結構便利そうなので、もう少し触ってみて紹介したいと思います。  

# polars-dsとは
polarsコミュニティで作成されたプラグインの一つです。データ分析に関する多数の機能が備わっています。  

# DIA (Data Inspection Assistant)  
polars-dsのクラスの一つで、EDA関連の機能が備わっています。  
https://polars-ds-extension.readthedocs.io/en/latest/

# DIAでEDAしてみる
サンプルコードは[kaggleのnotebook](https://www.kaggle.com/code/t88take/polars-ds-eda-sample)として公開しています。これに沿ってこの記事でも説明していきます。  

# [準備] ライブラリのインストール
```python
pip install polars==1.1.0 polars-ds[plot]==0.6.2 vegafusion[embed]==1.6.9
```
polarsのバージョン指定、polars-dsのinstallを行います。  
また、一部の可視化機能で必要になるvegafusionも入れておきます。  

```python
import polars as pl
import polars.selectors as cs
import polars_ds as pds
from polars_ds.diagnosis import DIA
import warnings
warnings.simplefilter('ignore')

pl.Config.set_tbl_rows(20)
```
ライブラリのインポート。


# [準備] データ読み込み
```python
# データセットとして関西kaggler会コンペ2023秋のデータを使用します
# https://www.kaggle.com/competitions/kansai-kaggler-autumn-2023
df = pl.read_csv('/kaggle/input/kansai-kaggler-autumn-2023/train.csv')
```

[関西kaggler会コンペ2023秋](https://www.kaggle.com/competitions/kansai-kaggler-autumn-2023) のデータセットを使います。近畿地方の住宅情報から取引価格を予測する回帰タスクで、trainに全く含まれていない大阪のデータがtestデータとなっている等、面白いコンペでしたが、今回はとくに関係しません。  

# [準備] DIAクラスのインスタンス作成
```python
# dataframeを与えてDIAクラスをインスタンス化します
dia = DIA(df)
```

DIAクラスのインスタンスを作成します。このインスタンスのメソッドで各機能を利用します。 

# データの概観を確認する系
```
# 各列の欠損値やinfの数を確認
dia.special_values_report()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/c6ad211b-cd66-4727-f78f-2e7d331dd512.png)

```python
# str列の統計量を表示
dia.str_stats()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/1fbfdbf5-f52f-98e9-15a7-b7cec6110869.png)

```python
# 数値列の統計量を表示
dia.numeric_profile(histogram=True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/d2bf8304-930f-2e20-0c23-7db4c45a3661.png)

pandasのdescribeのように各列の統計量などでデータの概観を確認できる機能群です。  
str_stats()で文字列の長さの統計量が確認できたり、numeric_profile()で各列の分布まで確認できるのは地味に嬉しい機能かなと思います。  

# 列の特性を推定する系
```python
# バイナリ値であると推定される列を抽出
dia.infer_binary()
```
```python:output
 ['AreaIsGreaterFlag',
 'FrontageIsGreaterFlag',
 'TotalFloorAreaIsGreaterFlag',
 'PrewarBuilding']
```  
<br>

```python
# 定数値であると推定される列を抽出
dia.infer_const()
```
```python:output
[]
```
<br>

```python
# 離散値であると推定される列を抽出
dia.infer_discrete()
```
```python:output
['Type',
 'Region',
 'Prefecture',
 'Municipality',
 'DistrictName',
 'NearestStation',
 'TimeToNearestStation',
 'MinTimeToNearestStation',
 'MaxTimeToNearestStation',
 'TradePrice',
 'FloorPlan',
 'Area',
 'AreaIsGreaterFlag',
 'LandShape',
 'Frontage',
 'TotalFloorArea',
 'TotalFloorAreaIsGreaterFlag',
 'BuildingYear',
 'PrewarBuilding',
 'Structure',
 'Use',
 'Purpose',
 'Direction',
 'Classification',
 'Breadth',
 'CityPlanning',
 'CoverageRatio',
 'FloorAreaRatio',
 'Year',
 'Quarter',
 'Renovation',
 'Remarks']
```
<br>

```python
# 確率値であると推定される列を抽出
dia.infer_prob()
```
```python:output
[]
```
<br>

```python
# 欠損が多い列を抽出
dia.infer_high_null(threshold=0.75)
```
```python:output
['FloorPlan', 'Renovation', 'Remarks']
```  

使いどころがあるようなないような...。  

# 可視化系

```python
numeric_columns = [
    'MinTimeToNearestStation',
    'MaxTimeToNearestStation',
    'TradePrice',
    'Area',
    'Frontage',
    'TotalFloorArea',
    'BuildingYear',
    'Breadth',
    'CoverageRatio',
    'FloorAreaRatio',
    'Year',
    'Quarter'
]
```
下準備として数値列を定義しておきます。  

```python
# 相関係数行列
dia.plot_corr(subset=numeric_columns, method='spearman')
```  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/6b6f9b21-e2b3-acf9-10d5-abe37443f24d.png)  

お手軽に相関係数行列のヒートマップが作成できます。速度も気になりません。

```python
# 欠損値の分布
dia.plot_null_distribution(
    cs.numeric(), 
    row_group_size=5_000,
    sort = 'row_id'
)
```  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/371691da-cfa0-2ac5-1a3e-bb625ba64906.png)

```python
# 分布の可視化
df_bins, plot = dia.plot_dist(
    "BuildingYear", 
    n_bins=100, 
    density=False, 
)
plot.interactive()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/e6c05643-3b84-1079-73ed-cc697395dee0.png)

```python
# グループごとの分布比較
dia.compare_dist_on_segment(
    'BuildingYear', 
    by='Prefecture',
    n_bins=100, 
    density=False, 
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/dc26f4e7-06ca-6cc9-543b-51ca7213e1b4.png)


分布の可視化も簡単にできます。  
各カテゴリごとの分布比較は絶対やるので、嬉しい機能。

```python
# 線形回帰の結果を表示
dia.plot_lin_reg(
    x = 'TotalFloorArea', 
    target = 'TradePrice', 
    add_bias=True,
).interactive()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160102/31850218-5ba7-bea7-2f08-b25250023ad2.png)

# おわりに
polars-dsのDIAという機能の紹介でした。  
データ処理だけじゃなくて、EDAでもpolars使っていきたいと思います。