リストワイズ除去のツリーを簡単に計算する関数（Python）

#本記事について
* 疫学研究等で目にする、**Fig1.本研究の解析対象者** のツリーを素早く計算する関数を書いた。
* リストワイズ除去を行った際のツリーを想定する。
* コードはPythonであるが、記事内にてRより呼び出す方法も追記した。 
* SQLでいう所の分析関数をpythonで実装ただけである。


## そもそもリストワイズ除去のツリーって？


こんなヤツ↓　エクセルでシコシコ関数組んでると平気で半時間とかかかったり。（目や腰もいたくなるね）

![risttttt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961784/0a205f03-603b-01da-a7f2-4c1139c29e2c.png)


（この図に正式名称があればご教示ください）

#　何が面倒くさいの？
単にそれぞれの変数の欠測数を乗せるだけなら**df.isnull.sum()**で終了ですが、、

1. **x1**の欠測は●人でした。
2. **x1**の欠測を除いたデータにおいて、**x2**の欠測は▲人でした。
3. **x1**と、**x2**の欠測を除いたデータにおいて**x3**の欠測は■人でした。
4. ・・・

SQLで言うところの分析関数のような物を書く必要があるんですよね。

あぁ面倒くさい（pythonでは）。

---


## では、本題へ

```python:
import pandas as pd
import numpy as np

def caluculate_missing_tree(df):
    d ={}
    d[0]= df.loc[df[df.columns[0]].isnull() != True]
    for i in range(len(df.columns)-1):
        d[1+i]= d[i].loc[d[i][d[i].columns[1+i]].isnull() != True]

    le = []
    colnames = []
    missing_tree = pd.DataFrame()

    for i in range(len(df.columns)):
        le.append(len(d[i]))
    for i in range(len(df.columns)):
        colnames.append(df.columns[i])


    missing_tree['col_name'] = colnames
    missing_tree['Size'] = le

    return missing_tree


```

**caluculate_missing_tree()** の引数に、ツリーを描きたい順番に変数が入っているデータフレームをぶち込むだけです。

たとえば、**titanic**のデータで試してみる。






```python:

import pandas as pd 
import numpy as np
import os 

df = pd.read_csv("train.csv")
df.shape #(891, 12)

df.isnull().sum()  # それぞれの変数の欠測数

--------------------------------
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

```
こいつを今回の関数に食わせると・・

```python:


caluculate_missing_tree(df)

--------------------------------------

	col_name	Size
0	PassengerId	891
1	Survived	891
2	Pclass  	891
3	Name	    891
4	Sex	        891
5	Age	        714
6	SibSp	    714
7	Parch	    714
8	Ticket	    714
9	Fare	    714
10	Cabin	    185
11	Embarked	183


```

一瞬で計算できた。嬉しい。



# 中身の説明

**.loc** を使用して、条件に合う（欠測していない）データセットをどんどん作っていけば良いのではという発想。

```python:
df  <- 元のデータ
df1 = df.loc[df['x1'].isnull() != True]] <- x1 の欠測が除かれたデータ
df2 = df1.loc[df1['x2'].isnull() != True]] <- x1, x2の欠測が除かれたデータ
df3 = df2.loc[df2['x3'].isnull() != True]] <- x1, x2, x3の欠測が除かれたデータ
...
...
```
こんな感じ。

さらに、**for文**書くことまで考えてみるとこんな感じ。

```python:
d[0]= df.loc[df[df.columns[0]].isnull() != True]　<- ここはfor文の外

--- こっからfor のイメージ ---
d[1]= d[0].loc[d[0][d[0].columns[1]].isnull() != True]
d[2]= d[2-1].loc[d[2-1][d[2-1].columns[2]].isnull() != True]
d[3]= d[3-1].loc[d[3-1][d[3-1].columns[3]].isnull() != True]

```


しかし、**for文**でdfの作成を自動化するのが少々難儀だった。

複数のデータフレームを格納するリストを作成。そこに、それぞれの変数に対応するデータフレームを格納していくという手法を使用した。

```python:
    d ={}
    d[0]= df.loc[df[df.columns[0]].isnull() != True]
    for i in range(len(df.columns)-1):
        d[1+i]= d[i].loc[d[i][d[i].columns[1+i]].isnull() != True]

```
こんな感じ。例えば**titanic**のデータでは、
 ・**d[0]はPassengerID** 
・**d[1]はPassengerID, Survived**
 ・**d[2]はpassengerID, Survived, Pclass**　の欠測に対応している。


その後、確認を容易にするために、**変数名**と**サンプルサイズ**を列名に持つデータフレームを作ろうという発想になるのは**必然**であろう。


```python:

    le = []
    colnames = []
    missing_tree = pd.DataFrame()

    for i in range(len(df.columns)):
        le.append(len(d[i]))
    for i in range(len(df.columns)):
        colnames.append(df.columns[i])


    missing_tree['col_name'] = colnames
    missing_tree['Size'] = le

    return missing_tree

```
**le**にそれぞれの変数の欠測値を消去したデータフレームのlen(df.columns)を格納していった。同様に、**colnames**にそれぞれのデータフレームに対応する変数名を格納し、可視化した。

```python:


caluculate_missing_tree(df)

--------------------------------------

	col_name	Size
0	PassengerId	891
1	Survived	891
2	Pclass  	891
3	Name	    891
4	Sex	        891
5	Age	        714
6	SibSp	    714
7	Parch	    714
8	Ticket	    714
9	Fare	    714
10	Cabin	    185
11	Embarked	183


```
ジャジャーン（2度目）

# Rでの実装方法
 * Rnotebookと **reticulateライブラリ** を使用する。（追記するかも）