SQLを用いたデータの前処理~データサイエンティスト向け～ 

#概要
##### 3GBを越えるcsvファイルの前処理のトレーニング備忘録

3GBを越えるcsvファイルは、Excelで開くことができない。
これをデータサイエンティストが分析に使えるようデータを整理し、300行のcsvファイルとして出力する。


### 前提知識
・SELECTやテーブルといったSQLの基礎知識が僅かにある
・MAMPなどでSQLにログインができる

### 使用する技術
1. データベースの作成
2. テーブルの作成
3. csvファイルを読み込み
4. 欠損値のあるレコード（行）の削除
5. 文字列に存在する邪魔な文字を指定して削除
6. varchar型からfloat型へ変換
7. varchar型からDate型へ変換
（'04/01/1998'という順序で記録されている文字列を、'1998-04-01' の順に変えて変換）
8. データを並べ替えてcsvファイルに出力


### 使用するcsvファイル
kaggleの[こちら](https://www.kaggle.com/residentmario/iowa-liquor-sales)からiowa-liquor-sales.csvをダウンロード

## 実践内容

#### ファイルのカラム（列）名の取得
SQLでは、テーブルのカラムを先に指定する必要がある。
今回はRを用いてカラム名と型を取得。

```R
## R
# check colmn name
# ファイルのカラム名を調べる
library(tidyverse)
Iowa <- read_csv("Iowa_Liquor_Sales.csv")
str(Iowa)

```
出力結果末尾
 - attr(*, "spec")=
  .. cols(
  ..   `Invoice/Item Number` = col_character(),
  ..   Date = col_character(),
  ..   `Store Number` = col_double(),
  ..   `Store Name` = col_character(),
  ..   Address = col_character(),
  ..   City = col_character(),
  ..   `Zip Code` = col_character(),
  ..   `Store Location` = col_character(),
  ..   `County Number` = col_character(),
  ..   County = col_character(),
  ..   Category = col_double(),
  ..   `Category Name` = col_character(),
  ..   `Vendor Number` = col_double(),
  ..   `Vendor Name` = col_character(),
  ..   `Item Number` = col_double(),
  ..   `Item Description` = col_character(),
  ..   Pack = col_double(),
  ..   `Bottle Volume (ml)` = col_double(),
  ..   `State Bottle Cost` = col_character(),
  ..   `State Bottle Retail` = col_character(),
  ..   `Bottles Sold` = col_double(),
  ..   `Sale (Dollars)` = col_character(),
  ..   `Volume Sold (Liters)` = col_double(),
  ..   `Volume Sold (Gallons)` = col_double()
  .. )

ほとんどのカラムが文字列だと分かる。
（DateやState Bottle Costなども）


### SQLへ移動
コマンドプロンプトを起動してSQLへ。

```
## cmd
# Mysqlのあるディレクトリに移動
cd C:\MAMP\bin\mysql\bin
```

```
# Mysqlにログイン
mysql -u root -proot
```

### 1. データベースの作成
trainという名のデータベースの作成

```sql

# trainという名のデータベースを作成
CREATE DATABASE train;
```

```sql

# 使用するデータベース'train'の指定
USE train;

# 使用するテーブル'iowa_liquor_2'が既にある場合は削除（無ければ以下の命令は無視）
DROP TABLE iowa_liquor_2
```

### 2. テーブルの作成
先程Rで判明したカラム名と型を用いて、テーブル'iowa_liquor_2'を作成する
欠損値を正確に処理するために一旦すべてのデータはvarchar型で定義する。

```

# 使用するテーブル'iowa_liquor_2'を作成
CREATE TABLE iowa_liquor_2 (
InvoiceItemNumber varchar(255),
Date varchar(20),
StoreNumber varchar(255),
StoreName varchar(255),
Address varchar(255),
City varchar(255),
ZipCode varchar(255),
StoreLocation varchar(255),
CountyNumber varchar(255),
County varchar(255),
Category varchar(20),
CategoryName varchar(255),
VendorNumber varchar(255),
VendorName varchar(255),
ItemNumber varchar(255),
ItemDescription varchar(255),
Pack varchar(255),
BottleVolume varchar(255),
StateBottleCost varchar(255),
StateBottleRetail varchar(255),
BottlesSold varchar(255),
Sale varchar(255),
VolumeSoldLiters varchar(255),
VolumeSoldGallons varchar(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

### 3. csvファイルをインポート
テーブル'iowa_liquor_2'にインポートする
INFILEの後ろに先程Kaggleでダウンロードした'Iowa_Liquor_Sales.csv'が保存されている場所を指定

```sql

# 作成したテーブル'iowa_liquor_2'に'Iowa_Liquor_Sales.csv'をインポート
LOAD DATA LOCAL INFILE 'C:/~~~~~~~~~~~~~~/Iowa_Liquor_Sales.csv'
into table iowa_liquor_2 
fields terminated BY ','
OPTIONALLY ENCLOSED BY '"'
IGNORE 1 LINES;
```

出力結果
Query OK, 12591077 rows affected, 65535 warnings (4 min 8.84 sec)
Records: 12591077  Deleted: 0  Skipped: 0  Warnings: 89541
読み込みに４分以上掛かかった。
データ数は12591077と判明

####上手くインポートできたか確認

```sql

# テーブルの列を確認
DESC iowa_liquor_2;

# テーブルの1行目を確認
SELECT *
FROM iowa_liquor_2
LIMIT 1;

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/f67eb979-35e7-c627-2e50-043c33efa897.png)

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/30365eab-5a2f-f288-c322-7a60ff47e63c.png)


インポートはできたが、欠損値を発見。
### 4. 欠損値があるレコード（列）を削除してデータを整える。

```sql


# 欠損値があるレコード（行）を削除
DELETE
FROM iowa_liquor_2
WHERE
    InvoiceItemNumber         = ''
         OR Date              = ''
         OR StoreNumber       = ''
         OR StoreName         = ''
         OR Address           = ''
         OR City              = ''
         OR ZipCode           = ''
         OR StoreLocation     = ''
         OR CountyNumber      = ''
         OR County            = ''
         OR Category          = ''
         OR CategoryName      = ''
         OR VendorNumber      = ''
         OR VendorName        = ''
         OR ItemNumber        = ''
         OR ItemDescription   = ''
         OR Pack              = ''
         OR BottleVolume      = ''
         OR StateBottleCost   = ''
         OR StateBottleRetail = ''
         OR BottlesSold       = ''
         OR Sale              = ''
         OR VolumeSoldLiters  = ''
         OR VolumeSoldGallons = ''
;

```

### 5~7. varchara型のカラムを別の型に変換
数字で表されているものは文字でなく数字として分析で使用したいので、数字型に変換する。
下記の８つのカラムのデータの型を変更する
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/4c8d486e-849b-a20d-e45f-ebd749f08ab4.png)

```sql

# データの型を変更できるものは変更する
# カラムStoreNumber, CountyNumber, VendorNumber, ItemNumber, Pack, BottlesSoldをINT型に変更
ALTER TABLE
    iowa_liquor_2
    MODIFY StoreNumber INT
    , MODIFY CountyNumber INT
    , MODIFY VendorNumber INT
    , MODIFY ItemNumber INT
    , MODIFY Pack INT
    , MODIFY BottlesSold INT
;

# カラムVolumeSoldLiters float, VolumeSoldGallonsをfloat型に変更
ALTER TABLE
    iowa_liquor_2
    MODIFY VolumeSoldLiters FLOAT
    , MODIFY VolumeSoldGallons FLOAT
;

# テーブルの列の型を確認
DESC iowa_liquor_2;

```
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/b6441fc1-9e19-916c-8028-9b55b6b6d74e.png)
上記７つのカラムのデータの型を変更できた。


#### 単純な変換ができない変換
カラム（列）'StateBottleCost','StateBottleRetail','Sale'は文字'$'が数字の前に存在しているため、変換ができない。
![4a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/a9776381-fa92-8035-8129-e37f065fc141.png)

また、日付を表すであろうカラムDateも文字列として扱われているので変換したい。
以下の４カラムは一工夫施してから型変換が行うことができる。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/1968fbbe-f23f-e295-20d0-c3572fa49347.png)

### 5. 文字列に存在する邪魔な文字を指定して削除
値段のカラムは数字として扱いたいので、文字'$'を除去し、float型へ変換できるようにする。

```sql

# カラム（列）'StateBottleCost','StateBottleRetail','Sale'の文字'$'を除去
UPDATE `iowa_liquor_2` SET StateBottleCost=REPLACE(StateBottleCost, "$", "");
# remove $ StateBottleRetail & Sale
UPDATE `iowa_liquor_2` SET StateBottleRetail=REPLACE(StateBottleRetail, "$", "");
UPDATE `iowa_liquor_2` SET Sale=REPLACE(Sale, "$", "");


# テーブルの1行目を確認
SELECT *
FROM iowa_liquor_2
LIMIT 1;
```

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/33ab6ff6-47e7-f961-4897-2e2b602014cc.png)
無事'$'を全て消せたことを確認。

### 6. varchar型からFLOAT型へ変換
'$'を除去した3列の型をvrchar型からFLOAT型に変更し、数値として扱えるようにする。

```sql

# 先程、文字'$'を除去した3列の型をvrchar型からFLOAT型に変更
/* 列の型を変える */
ALTER TABLE
    iowa_liquor_2
    MODIFY StateBottleCost FLOAT
    , MODIFY StateBottleRetail FLOAT
    , MODIFY Sale FLOAT
;


# テーブルの列の型を確認
DESC iowa_liquor_2;

```
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/6ebf6b00-14cb-bb9a-fba2-fd5be643a0b0.png)
3列の型をFLOAT型に変更できた。


### 7. DATA型に変換
文字として扱われているカラム'Date'を、日付として扱えるようにDATE型に変換。
'MM/DD/YYYY'という順序で記録されているため、'YYYY-MM-DD' に変える。

```sql

# DATA型に変換できるように'MM/DD/YYYY'から'YYYY-MM-DD' に変える
UPDATE `iowa_liquor_2` SET Date=STR_TO_DATE(Date,'%m/%d/%Y');

# テーブルの1行目を確認
SELECT *
FROM iowa_liquor_2
LIMIT 1;
```
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/77d4af78-91e7-66fc-1807-d5433106912b.png)
Dateが'YYYY-MM-DD'の順番に変更された。

```sql
# 列'Date'をvrchar型からDATA型に変更
ALTER TABLE iowa_liquor_2 MODIFY DATE DATE;

# テーブルの列の型を確認
DESC iowa_liquor_2;

```
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479337/941766a9-a787-b39f-aea5-6afc1a77631b.png)
カラムDATEの型がdate型に変更された。

### 8. csvファイルにエクスポート
StoreName, InvoiceItemNumber, Dateの順に優先して列を昇順に並び替えた最初の300行をcsvファイルへ書き出し

```sql

# sort by StoreName, InvoiceItemNumber, Date
/*
StoreName, InvoiceItemNumber, Dateの順に優先して列を昇順に並び替えて、
300行まで取ってきたものをiowa_liquor_2.csvとして書き出し
*/
# 最初のSELECT文で、出力するCSVファイルの列名を支持
(SELECT 
'InvoiceItemNumber',
'Date',
'StoreNumber',
'StoreName',
'Address',
'City',
'ZipCode',
'StoreLocation',
'CountyNumber',
'County',
'Category',
'CategoryName',
'VendorNumber',
'VendorName',
'ItemNumber',
'ItemDescription',
'Pack',
'BottleVolume',
'StateBottleCost',
'StateBottleRetail',
'BottlesSold',
'Sale',
'VolumeSoldLiters',
'VolumeSoldGallons'
) 
UNION(SELECT *
INTO OUTFILE 'iowa_liquor_2.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
ESCAPED BY '"'
LINES TERMINATED BY '\r\n'
FROM iowa_liquor_2
ORDER BY StoreName, InvoiceItemNumber, Date
LIMIT 300
);
# C:\MAMP\db\mysql\trainにできる
```