python jupyter notebook データ前処理選手権(対象サイト:ビックカメラ)

# やること
こんな感じのことをやる。
今回は「3, データ前処理」をやっていく。
【データ分析基礎】
1, データ収集（スクレイピング）
2, データの保存
3, データ前処理
4, データの可視化および考察
5, データに対しての結論および施策

ちなみに前回はこちら「1, データ収集（スクレイピング）」。
[前回記事](https://qiita.com/LightSign/items/b5c3abf530f82797c7eb0)
[動画で見たい人用リンク](https://www.youtube.com/watch?v=PNX42aixBkY&list=PLzPCqF-heFHzsphoemA9v6_jYyRldM1aH)

上記スクレイピングで集めたデータを用いて、前処理を行っていく。
読んでない方は流れが把握しづらいと思うので、ざっくり前回の記事を読んでいただけるとよいかと。

# 作成背景
「python データ前処理」と検索しても、いっつも同じタイタニックやらのscikit-learnデータばかりで、
クソつまらんかったから、誰もやってないデータで、誰もやってないデータ前処理をやってみたかった。

# 環境
* python3.7.0
* jupyter notebook
* ビックカメラcsv<br>
    * ダウンロードリンク：https://drive.google.com/open?id=1-PguvcT5kRIrvBifXhdiJDli2zyNsg62
* jupyberコード
    * ダウンロードリンク：https://drive.google.com/open?id=1pogwpzM4HHwTHrSDIsvXr8r79EDr4WhX

# 必要スキル&環境
* pythonの基礎構文は知っている
* pandasちょびっと知っている
* jupyter notebook or google colaboratory環境がある

# 1, データを観察する
### ■まずはcsvファイルをpandas dataframeとして読み込む。<br>
まあ自分でデータ取ったから、内容は楽勝やで。

```python:
import pandas as pd
df = pd.read_csv("biccamera_all_laptop.csv")
df.head()
```
![スクリーンショット 2019-11-09 20.09.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/76079a3a-4d03-3088-349b-ea1535e5f8d3.png)

### ■行数を取得, カラム名を確認, nullの個数を確認する
通常データ見るまでわからんけど、nullの数もゼロなのをワイが事前に処理。

```python:
# 行数を取得
print(len(df))
# カラム名を確認
print(df.columns)
# nullの個数を確認する
print(df.isnull().sum())
```
![スクリーンショット 2019-11-09 20.10.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/785245c9-59cf-fe6a-5973-6761e258738d.png)

### ■dataframeの情報を確認, カラムごとのユニーク数をカウント
info()も知っている。<br>
ただ、ユニーク数は見てみると、titleがちょびっと重複してるなと。<br>
makerは30社で、priceとpointの数が異なるなと。<br>
事前にpoint見てて「ポイント(10%)」ってなってたから、<br>
全部価格の10%かと思ってたけど、違うんかなと<br>
なんて考えた。

```python:
# dataframeの情報を確認
print(df.info())
# カラムごとのユニーク数をカウント
print(df.nunique())
```
![スクリーンショット 2019-11-09 20.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/c01bc2b2-8ba6-3253-b136-de163f786a99.png)

### ■各種value_counts()してみる
まあ、こんな感じか。<br>
あんまり言うことないな（笑）

```python:
# 配達期日
print(df.terms.value_counts())
# 在庫情報
print(df.stock.value_counts())
# メーカー名
print(df.maker.value_counts())
```

# 2, 必要なデータを抽出する
### ■タイトルが怪しいので見てみる

```python:
for t in df.title:
    print(t)
    print(len(t))
    print("*" * 100)
```
![スクリーンショット 2019-11-09 20.25.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/c80a2a94-06ca-1b01-3d07-fc46899b70d0.png)

### ■全角半角入り混じってるから、統一させる関数作った
半角カタカナを全角にし、全角英数字を半角にする関数。<br>
個人的によく使用する。

```python:
import re
import jctconv

def han2zen2han(string):
    """
    半角カタカナを全角にし、
    全角英数字を半角にする
    :param string: string text
    :return: string text
    """
    string = jctconv.h2z(string, kana=True, digit=False, ascii=False)
    string = jctconv.z2h(string, kana=False, digit=True, ascii=True)
    return string
```
### ■タイトルから[XXXX/XXXX/]みたいなリスト取得。
一旦正規表現r"\[.+?\]"で[]を全部取得。<br>
タイトルの中に複数個[]があるパターンもある。<br>
で、ノートPCの画面サイズも正規表現で取る。<br>
[]の中にサイズがあるものもあれば、無いものもある。<br>

```python:
# Seriesで取得してみる
df.title.apply(get_spec_list)
# 1つ取り出して中を確認
df.title.apply(get_spec_list)[0]
```

![スクリーンショット 2019-11-09 20.30.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/58526f36-42b0-14c3-110c-e644b4c7b4ea.png)

関数は下記。

```python:
def get_spec_list(title):
    """
    spec_list = 商品タイトルから[]を中身と一緒に抽出する
    inch_list = 商品タイトルからPCの画面インチテキストを抽出する
    l = spec_listから抽出したPCスペックをリストに入れ直す
    :param title: string text
    :return: list
    """
    l = []
    t = han2zen2han(title)
    spec_list = re.findall(r"\[.+?\]", t)
    inch_list = re.findall(r"(\d\d\.\d|\d\d|\d\..|\d)(インチ|型)", t)
    inch = "".join(inch_list[0]) if inch_list else ""
    for spec in spec_list:
        specs = spec.replace("[", "").replace("]", "").replace(" ", "").replace("・", "/").replace(":", "").split("/")
        for s in specs:
            l.append(s)
    if inch:
        l.append(inch)
    return list(set(l))
```

### ■もう書くのとスクショ貼り付けるの大変やから、このあたりにしとく。
とりあえず下の実行してみてほしい。

```python:
# PCスペックのベースとなるリストを抽出する
df["spec_list"] = df.title.apply(get_spec_list)

# CPUデータを取得
df["intel_cpu"] = df.spec_list.apply(get_intelcpu)
df["amd_cpu"] = df.spec_list.apply(lambda x: "".join([i for i in x if re.search(r"amd", i.lower())]))

# メモリデータ取得(int)
df["memory"] = df.spec_list.apply(get_memory)

# HDDデータ取得(int)
df["hdd"] = df.spec_list.apply(get_hdd)

# SSDデータ取得(int)
df["ssd"] = df.spec_list.apply(get_ssd)

# eMMCデータ取得(int)
df["emmc"] = df.spec_list.apply(get_emmc)

# インチ、型データ取得(float)
df["inch"] = df.spec_list.apply(get_inch)

# インチ、型データ取得(int)
df["int_inch"] = df.inch.astype("int")

# メーカー名取得(str)
df["new_maker"] = df.maker.apply(get_maker)

# PC価格を取得(int)
df["new_price"] = df.price.str.replace(r"\D", "").astype("int")

# PC購入時のポイントを取得(int)
df["new_point"] = df.point.str.replace(r"(ポイント|\n).*", "").str.replace(",", "").astype("int")

# PCの評価を取得(int)
df["new_ratings"] = df.ratings.str.replace(r"\D", "").astype("int")

# PCのタイトルの文字数を取得(int)
df["string_len"] = df.title.str.len()

# PCのタイトルの単語数を取得(int)
df["words_len"] = df.title.str.split().str.len()
```

最終こんな感じになる。

![スクリーンショット 2019-11-09 20.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180478/67b76c05-75ea-57a7-f6fa-5c52ba03513e.png)

# 終わりに
動画としてに置いといたから、処理の流れを実際に見たい人はyoutubeで見て頂戴な。

[動画リンク](https://www.youtube.com/watch?v=PNX42aixBkY&list=PLzPCqF-heFHzsphoemA9v6_jYyRldM1aH)

コードを動かしているのを見たい方は上記リンクの「data processing 02」からどうぞ。
結構説明長いので、早送りおすすめ。