pandas.DataFrame で数値以外の要素の抽出

この記事は古川研究室 [Advent_calendar](https://qiita.com/advent-calendar/2019/flab "Advent_calendar") 18日目の記事です。

本記事は古川研究室の学生が学習の一環として書いたものです．内容が曖昧であったり表現が多少異なったりする場合があります．

# はじめに
pandas で前処理していたときに，データに数値以外の要素が混入していて歯がゆい思いをしたので，記事としてまとめます．
また，本記事のコードは jupyter notebook を用いたものです． 

# 扱うデータ
kaggle の [2,2k+ Scotch Whisky Reviews データセット](https://www.kaggle.com/koki25ando/22000-scotch-whisky-reviews/data#)

## データの説明
スコッチウイスキーについてレビュワーが評価しているデータセットです．
データ数 2247，項目数 7 のデータです．

## データの確認

```python
# ライブラリの import
import pandas as pd
import numpy as np
# csv ファイルの読み取り
data = pd.read_csv('scotch_review.csv')
# データの表示
data.head()
```
![](https://i.imgur.com/LLAFlM7.jpg)

# 本題
## 扱うデータに数値以外の型を持つ要素があるか確認

今回は項目の 'review.point'(評価点) と 'price'(価格) のみを使います．
それぞれの列のデータ型を見ていきます

```python
# 型の確認
data[['review.point','price']].dtypes
```

![kata.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463356/ab57922a-0aec-9a22-9f0a-15e3941bcbad.jpeg)


'review.point'列に数値以外の要素が混ざっていそうですね．
**次のコードで該当する列に数値以外の要素があるか判定できます．（※str 型の数値は True を返します）**

```python
#'price', 'review.point'列に数値型に変換できない要素があれば 'False' を返す．
data[['review.point', 'price']].apply(lambda s:pd.to_numeric(s, errors='coerce')).notnull().all()
```

![kata.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463356/5942d9e7-82ba-9408-4dc4-097748eb409a.jpeg)



# 数値以外の型を持つ要素の抽出及び変換

ここからは 'price' 列から，数値以外の型を持つ要素を抽出し，置換していきます．
まずは抽出です．

```python
# 数値ではない型の要素の抽出
pic = data[['price']][data['price'].apply(lambda s:pd.to_numeric(s, errors='coerce')).isnull()]
pic
```

![](https://i.imgur.com/2mnxgzF.jpg)!


ここでは /set と /liter のデータは欠損値として扱い，他は数値型に変換します．

```python
# ',' の削除，'/' が含まれる要素を欠損値で置換
change_data = pic['price'].str.replace(',','').mask(pic['price'].str.contains('/'), np.nan) 
change_data
```
![str_data.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463356/6c42e979-2755-3506-3d79-5e0705481b8f.jpeg)

元データに変更を反映させます．

```python
# 元データのコピーを作成し，該当箇所を置換
data_c = data.copy()
data_c.loc[pic.index,'price'] = change_data
```

最後に 'price' 列の数値を数値型に変換し，欠損値を含む行を削除します．

```python
data_c['price'] = pd.to_numeric(data_c['price'], errors = 'ignore')
df  = data_c.dropna()
```

# 終わりに
今回は pandas.DataFrame で数値以外の要素の抽出し，置換をしました．次回の記事では，この前処理をした Whisky Reviews データセットを可視化してみます． 

# 付録
自分用にコードに使用していた関数の説明を載せておきます．

## 数値型に変換できない要素があるかどうかの判定
```python
#'price', 'review.point'列に数値型に変換できない要素があれば 'False' を返す．
data[['review.point', 'price']].apply(lambda s:pd.to_numeric(s, errors='coerce')).notnull().all()
```
- to_numeric(arg, errors = 'coerce')
    - arg (pd.Series) の各要素を数値型に変換する．変換できないときは，errors に渡す引数に応じて処理をする．今回は 'corece' なので，変換できない要素を欠損値 NaN に置換する．(default は 'raise')
- 関数名 = lamba 引数: 式
    - 無名関数．以下関数と同じ働きをする． 
   
```python       
def 関数名(引数):
    return 式
```

- DataFrame.apply(関数, axis = 0)
    - 関数の引数に DataFrame の要素を渡す．axis で渡し方（行方向か列方向）を選択する．
- DataFrame.notnull()
    - pandas.DataFrame の各要素に対して欠損値ではないなら True，欠損値なら False を返す．
- DataFrame.all(axis = None)
    - 行または列のすべての要素が True なら True を返す．

## 各要素の文字列の部分的な置換と，値そのものの置換
```python
# ',' の削除，'/' が含まれる要素を欠損値で置換
change_data = pic['price'].str.replace(',','').mask(pic['price'].str.contains('/'), np.nan) 
change_data
```

- Series.str.replace('文字列A', '文字列B')
    - Series の各要素に含まれる '文字列A' を '文字列B' に変換
- Series.str.contains('文字列A')
    - '文字列A' を含む要素を True，それ以外の要素を False として返す．
- Series.mask(arg, '値')
    - arg(Series) の True の要素を '値' で置き換え，'False' の要素は変更しない．