【決定木】特徴量に文字列を使いたい場合、BoWとtf-idfを使って特徴量を無理やり作成する

# はじめに
昨日と同じく決定木をコーディングしてみます。
昨日と違うところは、特徴量に文字列を使いたい場合、BoWとtf-idfを使って特徴量を無理やり作成することをしたいと思います。どこまで意味があるのかわかりませんが、やってみます。
※正直意味はないかもしれませんが、文字列データも分析に使用できるようにしていきたいなと思いました。

# 使用するデータ
使用するデータは、
https://qiita.com/iwasaki_kenichi/items/43ba6e5d037b0edd247f
の記事でも使用している、kaggleのHuman Resource DataSetを使います。

https://www.kaggle.com/rhuebner/human-resources-data-set
このデータの、HRDataset_v9.csvを使用します。

# コーディング
```python
import numpy as np
import pandas as pd
from sklearn import tree
from sklearn.tree import export_graphviz
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
```

まずは各種インポートします。
今回は新しく、`from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer`をインポートしています。

CountVectorizerはBoW（Bag of Words）、
TfidVectorizerはTF-IDF（Term-frequency-inverse document frequency）です。
とても簡単にいうと
BoW：ある文書における単語の出現回数を数えるアルゴリズム
TF-IDF：ある文書における、単語の重要度を計算するアルゴリズム

単語数（BoW）か、単語の重要度（TF-IDF）になります。

```python
df = pd.read_csv("human-resources-data-set/HRDataset_v9.csv")

#notebookの場合、columns数が多いと途中が省略されてしまうので、set_optionにて見れるcolumns数を変更しております。
pd.set_option('display.max_columns', 50)
df.head()

```

ここは、[前回の記事](https://qiita.com/iwasaki_kenichi/items/43ba6e5d037b0edd247f)でも書きましたが、データは下記のような感じです。
<img width="702" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/f2455180-6152-77d6-754a-5ee7fb06d401.png">

<img width="702" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/808021ba-5817-2a41-ba52-dcc6a4af92d1.png">

<img width="705" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/29a99ef7-429f-7aee-a38b-52bd2ddd314d.png">

今回は、これらのデータの中でも`Employee Name`を特徴量にしたいと考えます。

その前に、この`Employee Name`がカテゴリカル変数じゃないことを確認してみます。

```python
len(df)
df["Employee Name"].nunique()
```
`df`全体の大きさと、`df["Employee Name"]`を確認してみましたが、どちらも310でした。これでカテゴリカル変数じゃないことを確認できたので、特徴量に使えるよう、文字列を数値に変換していきたいと思います。

```python
countvectorizer = CountVectorizer(min_df=2)
countvectorizer = countvectorizer.fit(df["Employee Name"])

tfidvectorizer = TfidfVectorizer(min_df=2)
tfidvectorizer = tfidvectorizer.fit(df["Employee Name"])
```
countvectorizerとtfidvectorizerをを作成し、`fit`関数を適用します。

<img width="467" alt="１.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/c09e9b74-f15e-433b-c106-114b07519278.png">

```python
print(len(countvectorizer.vocabulary_))
print(dict(list(countvectorizer.vocabulary_.items())))
```
<img width="763" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/c8de1186-6d65-ad34-3c02-154d1ed39814.png">



```python
print(len(tfidvectorizer.vocabulary_))
print(dict(list(tfidvectorizer.vocabulary_.items())))
```
<img width="762" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/c290eb5f-2506-e14e-ae35-78853be56b60.png">

CountvectorizerもTfidVectorizerもデータの中身は同じみたいです。

具体的にデータの中身を見てみます。

```python
df_vec = countvectorizer.transform(df)
df_tfid = tfidvectorizer.transform(df)

ddf_vec = pd.DataFrame(df_vec.toarray(),columns=countvectorizer.get_feature_names())
ddf_tfid = pd.DataFrame(df_tfid.toarray(),columns=tfidvectorizer.get_feature_names())
```

```python
ddf_vec
```
<img width="740" alt="4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/b2873277-abfd-5b7c-72d2-56a43c02d10e.png">


```python
ddf_tfid
```
<img width="739" alt="5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/5a74178b-4ec8-ef3b-700c-952b55f273b8.png">

CountVectorizerは整数でTfidVectorizerは実数だということがわかりました。
ただ、データの中身はほとんど同じみたいですね。

以上で、データの中身はわかりました。
今回は、あくまでも文字列データを数値（BoWやTFD-IDによる数値変換）に変換し、変換したデータをトレーニングデータ、テストデータとして使います。
なので、`fit_transform関数`を使います。

```python
cv = countvectorizer.fit_transform(df["Employee Name"])
tfid = tfidvectorizer.fit_transform(df["Employee Name"])
```

Sparse型はほとんどゼロの疎行列になります。
<img width="748" alt="6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/2a9f262f-cc05-f846-efba-53e2a78c8f6b.png">


このままだとSparse型なので、toarray()でndarray型に変換します。

```python
dffcv = cv.toarray()
dfftfid = tfid.toarray()
```

疎行列のままだと分析に使いにくい形なので、各行の総和を`sum`関数にて計算します。さらに、カラム名を”EmployeeNameFeature”と変更します。

変更したあとは`pd.concat関数`にて、データフレームを横方向にくっつけています。

```python
dffcv_sum = pd.DataFrame(np.sum(dffcv,axis=1))
dfftfid_sum = pd.DataFrame(np.sum(dfftfid,axis=1))

dffcv_sum.columns=['EmployeeNameFeature']
dfftfid_sum.columns=["EmployeeNameFeature"]

df_cv = pd.concat([df,dffcv_sum],axis=1)
df_tfid = pd.concat([df,dfftfid_sum],axis=1)
```

<img width="743" alt="7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284911/c96f7d26-0d47-3ee3-488a-e28fafc86d16.png">

赤枠が、今回新しく作った特徴量です。

今回の分析では、以下のように説明変数や目的変数を設定します。

特徴変数：MarriedID以外
目的変数：GenderID,DeptID,Perf_ScoreID,EmployeeNameFeature

```python
feature=["GenderID","DeptID","Perf_ScoreID","EmployeeNameFeature","EmployeeNameFeature"]

y_cv = df_cv["MarriedID"]
X_cv = df_cv[feature]

y_tfid = df_tfid["MarriedID"]
X_tfid = df_tfid[feature]
```

処理を実行します。

```python
#CountVectorizer
X_train,X_test,Y_train,Y_test = train_test_split(X_cv,y_cv,train_size=0.7,random_state=0)
tree_clf = tree.DecisionTreeClassifier().fit(X_train, Y_train)
print(tree_clf.score(X_test, Y_test))

#TfidVectorizer
X_train,X_test,Y_train,Y_test = train_test_split(X_tfid,y_tfid,train_size=0.7,random_state=0)
tree_clf = tree.DecisionTreeClassifier().fit(X_train, Y_train)
print(tree_clf.score(X_test, Y_test))

```

結果は下記のようになりました。
・0.4731182795698925
・0.4731182795698925
全く同じ値になりました。


#おわりに
文字列の変換を今回新しくやってみました。たくさんしてみたいですが、そもそもデータセットが無いような気もします。。。