【ファッション×テック】pythonの力でファッショニスタを目指す part1

#はじめに
先日、[「お前らの服装がダサいのでおしゃれ判定アプリを作ってみた」](https://qiita.com/NakaokaRei/items/595fcb21ff10b84919ab)という面白い記事を拝見しました。エンジニアもお洒落をしていく！素晴らしいです！私もとてもお洒落を楽しんでいます。~~ZOZO就職したいです。~~さて、記事中に
>エンジニアはおしゃれが苦手でどうしてもダサい服装になりがちです(偏見)

とありましたが、私の様な（？）お洒落エンジニアも沢山いると思います、そんなお洒落エンジニアが、pythonのお勉強もしつつ、更にお洒落レベルを上げて、ファッショニスタを目指すぜ！的な記事を書いていきます。

#今回すること
~~[ WEAR ](https://wear.jp/)ではなく~~ 有名ファッション情報サイト[ FASHIONSNAP.COM ](https://www.fashionsnap.com)に投稿されているファッショニスタのストリートスナップから着用ブランドをスクレイピングしたデータを利用し、**「ブランドAを着た時、おすすめのブランドは何？」**という問いに対するベイズ理論に基づいたレコメンド機能の実装を行います

コードは[こちら](https://github.com/cocet33000/brand_recommend)

#そもそもファッショニスタとは
実装前にファッショニスタについて軽く説明します。[コトバンク](https://kotobank.jp/word/%E3%83%95%E3%82%A1%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8B%E3%82%B9%E3%82%BF-690061)には以下の通り書かれていました。まあ、お洒落な人が憧れる、感度の高い**、めっちゃお洒落な人**です(たぶん)。
> 
最先端のファッションを追い求める人。おしゃれに敏感な人。また、ファッション業界人

参考までに[「FASHIONSNAP.COMが原宿から選んだ今年注目のネクストアイコン5人【2019年版】」](https://www.fashionsnap.com/article/harajuku-next-icon-2019/)という記事で紹介されていたファッショニスタはこんな感じです. This is Fashionista.

![ファッショニスタ.jpg](https://qiita-image-store.s3.amazonaws.com/0/330036/b054e5cc-e8ca-b79c-e9f7-d8e748f0b375.jpeg)

それでは、いってみよお⤴︎⤴︎⤴︎

#環境
```bash
$ sw_vers
roductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65

$ python -V
Python 3.4.3
```

#スクレイピングの実装
[「PythonとBeautiful Soupでスクレイピング」](https://qiita.com/itkr/items/513318a9b5b92bd56185)を参考に~~[ WEAR ](https://wear.jp/)
ではなく~~[ FASHIONSNAP.COM ](https://www.fashionsnap.com)の[ ストリートスナップ ](https://www.fashion-press.net/snaps/sex/mens)を対象にデータを集めていきます。対象のページは画像の様になっており、この中から名前と着用ブランドをスクレイピングし、最終的にはpandasのDataFrameにし、csv形式で保存します。

![streetsnap.png](https://qiita-image-store.s3.amazonaws.com/0/330036/651e4af2-8343-c673-b83d-dc51a94fb877.png)


コードは以下です。

```python

import re
import uuid

from bs4 import BeautifulSoup
import requests
import numpy as np
import pandas as pd
import collections

# URLからbs4オブジェクトを生成
def openURL(URL:int): 
    res =  requests.get(URL)
    if res.status_code != requests.codes.ok:
        print('Error')
        return(False)
    return BeautifulSoup(res.text)

# bs4オブジェクトから着用ブランドのリストを取得
def getBrandList(snaps):
    BrandList = []
    for snap in snaps:
        BrandList.append(list(set(map(lambda x: x.text, snap.find_all('a', attrs = {'href':re.compile('^/snaps/brand')}))))) #1コーデに同ブランドは1つまで

    return BrandList

# bs4オブジェクトから名前を取得
def getNameList(snaps):
    NameList = []
    for snap in snaps:
        NameList.append(snap.find('a')['title'])
    return NameList

if __name__ == '__main__':
    flag = True
    page = 1
    BrandList = []
    NameList = []

    while flag:
        URL = 'https://www.fashion-press.net/snaps/sex/mens?page=' + str(page)
        soup = openURL(URL)
        snaps = soup.find_all(attrs = {'class':'fp_media_tile'})


        if len(snaps) != 0: #写真がある場合はブランドを取得
            tmpBrandList = getBrandList(snaps)
            BrandList.extend(tmpBrandList)
            NameList.extend(getNameList(snaps))
            print('get page' + str(page))
            page += 1

        else:  #写真がない場合は終了
            flag = False
            print('END')

    df = pd.DataFrame(data=BrandList, index=NameList)　#pandasのDataFrame型に
    df.to_csv('StreetSnapMen.csv') # csv形式で保存
```

得られたデータはこちら。jupyter notebook で表示してみました。全部で1492件のデータを取得成功、最大で8つのブランドが登録されてる様子。

![df.png](https://qiita-image-store.s3.amazonaws.com/0/330036/b3dc1153-4af6-876e-5c21-d757c40c4e31.png)

```bash

>>> df.shape
(1492, 8)
```
#レコメンド機能の実装
集めたデータセットを利用し、**「ブランドAを着た時、おすすめのブランドは何か？」**をレコメンドする実装を行います。今回は、最もシンプルにベイズの定理を用いた推定を行います。

##ベイズの定理
みなさんご存知かとは思いますが、以下の通り。

```math
P(A|B) = \frac{P(B|A)P(A)}{P(B)} = \frac{P(A,B)}{P(B)}
```
* $P(B)$: 事象$A$が起きる前の、事象$B$の確率(事前確率)
* $P(B|A)$: 事象$A$が起きた後での、事象$B$の確率(事後確率)

事象$B$が起こった時に,事象$A$が起きる確率(左辺)を、事象$B$が起きる確率と、事象$A$と事象$B$が同時に起こる確率(一番右の辺)から算出できるというもの。

##2ブランドの共起確率の算出
あるブランドBを着用している人が、あるブランドAを着用している確率を算出します。これはベイズの定理から、ブランドBを着用している確率とブランドAとブランドBを同時に着用している確率から算出可能です。

先ほど作ったデータセットにおいて、ベイズの定理に基づき確率を求めるコードがこちら

```python

def bays(model, A, B = None):
    pB =  (model == B).sum().sum()
    pAB =  (((model == A)| (model == B)).sum(axis = 1) > 1).sum()
    return  pAB  / pB
```

では、リーバイスを着用している人が、コンバースを着用している確率を求めてみます。

```bash
>>> bays(df, 'コンバース','リーバイス')
0.13636363636363635
```
13.6% それっぽいです(圧倒的感覚判断)

##実装
全ブランドの共起確率を算出し、上位3つのブランドをレコメンド機能を実装します(力技)

コードはこちら(被りのないブランドリストみたいなのを作ってぶん回します。。。)

```python

# 被りのないブランドリストを作る
def getuniqueList(df):
    uniqueList = [] 
    
    for columns in df.columns.values:
        uniqueList.extend(df[columns])
        
    return collections.Counter(uniqueList)

brand = getuniqueList(df)
brand_df = pd.DataFrame(index = list(brand.keys()), data = list(brand.values()))
brand_df = brand_df.drop(np.nan) # NAN　を削除 

# 共起確率が上位kまでのブランドリストを返す
def predict(df, brand_df, wear, k = 3):
    prob = []
    
    for brand in brand_df.index:
        prob.append(bays(df, brand, wear))
        
    best_k = sorted(range(len(prob)), key=lambda i: prob[i], reverse=True)[:k]
    return list(map(lambda k:brand_df.index[k], best_k))
```

#評価

さて、では超絶主観で評価（？）していきます。
まずは、先ほど例にも挙げたリーバイスに合うブランドを

```bash
>>> predict(df, brand_df, 'リーバイス') 
['古着(ユーズド)', 'ヴィンテージ(年代物)', 'コンバース']
```

①古着　②ヴィンテージ　③コンバース 

これは、それっぽい！！！！！！！！！　主観でしかないですがそれっぽいです！！！！！
しかし、当たり前だが処理時間が結構かかる。。

その他の結果をいくつかどうぞ

```bash
>>> predict(df, brand_df, 'シュプリーム')
['ナイキ', 'コンバース', '古着(ユーズド)']

>>> predict(df, brand_df, 'エイチ＆エム')
['古着(ユーズド)', 'ユニクロ', 'ドクターマーチン']

>>> predict(df, brand_df, 'ジョルジオ アルマーニ')
['古着(ユーズド)', 'アクネ ストゥディオズ', 'Vestal']

>>> predict(df, brand_df, 'バーバリー')
['コム デ ギャルソン', 'ユニクロ', '古着(ユーズド)']

>>> predict(df, brand_df, 'パラブーツ')
['アー・ペー・セー', 'リーバイス', 'アンユーズド']

>>> predict(df, brand_df, 'キコ コスタディノフ')
['アクネ ストゥディオズ', 'マルケス アルメイダ', 'プラダ']

>>> predict(df, brand_df, 'ロエベ')
['セリーヌ', 'ベッドフォード', 'グッチ']

>>> predict(df, brand_df, 'サマンサタバサ')
['メゾン キツネ', 'ブルガリ', 'チャーチ']
```

それっぽいですねえ(主観)
シュプリームに対してナイキ、ロエベに対してセリーヌをレコメンドしている辺りとてもそれっぽいです。

是非、お洒落エンジニアさんはコメントお願いします❤︎

#まとめ
[ FASHIONSNAP.COM ](https://www.fashionsnap.com)の[ ストリートスナップ ](https://www.fashion-press.net/snaps/sex/mens)から着用ブランドをスクレイピングしてデータセットを作成。そして、ベイズの定理に基づたそれっぽいレコメンド機能的な物を実装してみました。

手法もなんの工夫もないですし、データセットは数も少なく、ブランド偏りが凄い等の問題があるので、もう少し工夫して第二弾やってみます。

とりあえず、入力ブランド数を複数にしたものを実装したいですね。そしてもう少し定量的な評価を目指します。。

#追伸
[ part2 ](https://qiita.com/cocet33000/items/f13ba60d3d7845dd05c9)もよろしくお願いします。