pythonによる紳士達の性癖分析：データ分析編

### スクレイピング結果の分析
- [前の記事](https://qiita.com/we-yu/items/7d7c60dc5e302e728e85)で生成した２つのCSVファイルの情報を分析する。
- やれることは多々あるのだろうけど、とりあえず関連度が高い組み合わせを調べてみる。
 - たとえば「人妻」タグは「ロリ」系のタグとの関連度が低いだろうし、逆に「熟女」系とは関連度が高いだろう。
- **引き続き尾籠な単語がたくさん出てくるのでご注意を。**

#### 処理の流れ
- 処理としてはまず、以下のような二次元配列を作成する。

| - | ポニーテール | 輪姦 |  スーツ  | メガネ | 盗撮 | 陵辱 | … |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| ポニーテール | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 輪姦 | 0 | 0 | 0 | 0 | 0 | 0 | … |
| スーツ | 0 | 0 | 0 | 0 | 0 | 0 | … |
| メガネ | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 盗撮 | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 陵辱 | 0 | 0 | 0 | 0 | 0 | 0 | … |
| : | : | : | : | : | : | : | |

- 縦軸/横軸共に要素は同一となり、結果的に正方かつ零行列となる。この要素名は出現しているタグを網羅している。
 - 前回の記事で作成した`TagList.csv`から出現済みのタグ群は取得可能。
- 「各作品に含まれるタグ」(`RankingList.csv`)を順次読みながら、二次元配列内の該当する箇所のカウントを+1する。
 - たとえば上記初期状態の後、`[輪姦, 陵辱, ポニーテール]`が付いた「ポニーテールの女の子がなんかひどい目に遭うんだろう」作品を読み込んだ後は以下のようになる。

| - | ポニーテール | 輪姦 |  スーツ  | メガネ | 盗撮 | 陵辱 | … |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| ポニーテール | 0 | 1 | 0 | 0 | 0 | 1 | … |
| 輪姦 | 1 | 0 | 0 | 0 | 0 | 1 | … |
| スーツ | 0 | 0 | 0 | 0 | 0 | 0 | … |
| メガネ | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 盗撮 | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 陵辱 | 1 | 1 | 0 | 0 | 0 | 0 | … |
| : | : | : | : | : | : | : | |

- ここからさらに「スーツの女性がやっぱりひどい目に遭う」`[スーツ, 輪姦, 陵辱]`が付いた作品を読み込むと加算される。

| - | ポニーテール | 輪姦 |  スーツ  | メガネ | 盗撮 | 陵辱 | … |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| ポニーテール | 0 | 1 | 0 | 0 | 0 | 1 | … |
| 輪姦 | 1 | 0 | 1 | 0 | 0 | 2 | … |
| スーツ | 0 | 1 | 0 | 0 | 0 | 1 | … |
| メガネ | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 盗撮 | 0 | 0 | 0 | 0 | 0 | 0 | … |
| 陵辱 | 1 | 2 | 1 | 0 | 0 | 0 | … |
| : | : | : | : | : | : | : | |

- 以上の作業をランキングされている全作品に行うことで、どの組み合わせのタグが一番仲が良いかを調べる。

```py
# coding: UTF-8
import csv
import copy
from collections import deque

# CSV読み込み・配列の添字・二次元辞書・キュー
# ランキング製品のタグの組み合わせの数をカウントする。

RANKING_CSV_FILENAME = 'RankingList.csv'
DLTAGS_CSV_FILENAME = 'TagList.csv'

# 頻出すぎたり性癖に直結しない系統のタグはいったん外す。
IGNORE_TAGS = ("バイノーラル", "中出し")

# ランキング作品のタグ群を配列に読み込む。
csv_file = open(RANKING_CSV_FILENAME, "r")
f = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)
header = next(f)
productMatrix = []
for row in f:
    # 左端はタイトルなので今回は不要。読み飛ばすため[1:]で指定。
    t = row[1:]
    # タグが無い作品については無視する
    if (len(t) > 0) :
        productMatrix.append(t)

# タグリストを読み込む（以降これは変更されない）
tags = []
csv_file = open(DLTAGS_CSV_FILENAME, "r")
f = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)
header = next(f)
for row in f:
    tags.append(row[0])

# [tags][tags]の二次元辞書を生成する。
tagMatrix = {}
for x in tags :
    for y in tags :
        if not (x in tagMatrix):
            tagMatrix[x] = {}
        # 初期値は0とする
        tagMatrix[x][y] = 0

# productMatrixの1rowが「1製品の持つタグ群」になる。
for x in productMatrix :
    # 暫定処理。現状、タグの設定が["xxx", "yyy]だった場合はtagMatrix["xxx"]["yyy"]と["yyy"]["xxx"]の両方でカウントする。
    # 必要なのは「組み合わせの数」なのでどちらかだけでよい。（最終的には疎行列かつ三角行列になるはず）
    for y in x :
        for z in x :
            tagMatrix[y][z] += 1
    for z in x:
        for y in x:
            tagMatrix[y][z] += 1

# 頻出ランキング作成のためにサイズ5のキューを作成する。
ranq = deque([], 5)
# Qへの初期値設定
qInit = {"val": -1, "x": "", "y": ""}
ranq.appendleft(qInit)

# tax*tagでマトリクスの全要素へアクセス可能。
for x in tags :
    for y in tags :

        is_continue = False

        # 対角要素は意味がないのですべて-1とする。
        if (x == y) :
            tagMatrix[x][y] = -1
        # Qの左端要素の「出現回数」を超過するものがあった場合、当該要素をQの左側から入れる。
        if (tagMatrix[x][y] > ranq[0]["val"]) :

            for ignore in IGNORE_TAGS :
                if(x == ignore or y == ignore) :
                    is_continue = True
                    break

            if(is_continue) :
                continue

            max = {"val":tagMatrix[x][y], "x":x, "y":y}
            ranq.appendleft(max)

# CSVのヘッダ行生成。左端に"-"を入れる。
h = copy.deepcopy(tags) # 強いコピー
h.insert(0, "-")
csvHeader = ",".join(h)

# タグの出現数をまとめた二次元配列出力
f = open("TagMatrix.csv", "w")
writer = csv.writer(f, lineterminator="\n")
writer.writerow(h)

csvLine = []
for x in tags :
    # csvLine = [tag名, tag1との組み合わせ数, tag2との組み合わせ数, tag3との組み合わせの数…]という構成になる。
    csvLine.append(x)
    for y in tags :
        csvLine.append(tagMatrix[x][y])
    writer.writerow(csvLine)
    csvLine.clear()
f.close()

# 頻出の組み合わせを出力
for rq in ranq :
    print(rq["val"], rq["x"], rq["y"])
```
- 以上がコードになる。pythonのお作法的に綺麗な書き方ではないと思うがご容赦を。
- 冒頭で示した二次元配列は表計算ソフトで扱い易くするため、CSV形式で出力する。以下が例。
 - 条件付き書式で背景色を変えてあるが、データとしては生のまま。
- 対角線要素はなんでもいいのだが、分析には不要なので-1で埋めてある。

![image](https://qiita-image-store.s3.amazonaws.com/0/135051/0b9b469c-9acf-a681-3341-5252dddd7223.png)

#### データを眺める
- とりあえずデータは取れたので色々やって見てみよう。
- 頻出タグ上位からいくつかピックアップしてみる。
 - →`巨乳/爆乳, 陵辱, ラブラブ/あまあま, 男性受け, 人外娘/モンスター娘`
- これらを縦軸に置いた時、頻出するタグをDソートしてみる。（組み合わせて使われてるケースが多いタグが現れる）

![image](https://qiita-image-store.s3.amazonaws.com/0/135051/1b89c708-d094-3e0e-0498-c0d7c001257b.png)
わからんでもない。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/520b8106-a30d-6f60-b049-e1446861fa2e.png)
触手レイプは浮世絵からの伝統ということか。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/6d2897db-b14c-e075-9145-4c8e539a7f5d.png)
さすがにポジティブなタグが多い。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/1a1e4c9a-e1fc-4e14-bb51-6084bcb812bc.png)
ああ、うん、なるほど（黙って頷く）
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/0d9acec3-0e39-533e-c1a3-9f97f274622e.png)
タグが混沌としてきた！

- あとは個人的に、「こだわり」が強い人が多い（タグによる住み分けが重要）気がする項目をピックアップしてみる。

![image](https://qiita-image-store.s3.amazonaws.com/0/135051/9ad9b4f3-8005-c8b7-be98-b4d8f26c3de4.png)
NTRと寝取りは表裏一体ということか。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/6079a581-961b-2f69-6c22-79c27566eaa7.png)
敵女性幹部がヒロインを悪落ちさせてくるような展開が多いと思われる。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/81248dff-c9f7-188c-373a-00c3134fb4c7.png)
こっちからみても、やっぱり変身ヒロインが洗脳されるのはお決まりのようだ。
![image](https://qiita-image-store.s3.amazonaws.com/0/135051/d90cf96a-0f9c-c5a4-593c-b3d813c1caa9.png)
力強いまでの「逆転無し・男性受け・逆レイプ」の組み合わせ。

#### 総評
- 割と面白かった。

#### 残課題
- dequeを使って頻出タグの上位を取得してるが、現状十全に動いていないので改良が必要。
- pythonだと、おそらく「データフォーマット」を使うことで簡単に行列を作れる？
- あくまで知りたいのは「タグの組み合わせ」なので、例えば`tagMatrix["陵辱"]["ポニーテール"]`と`tagMatrix["ポニーテール"]["陵辱"]`はどちらかだけでよいはず。
 - 現状、対角行列になっているが上か下の三角は不要になると思われる。
- おそらく１つの作品に対するタグは６つが上限。そんなに大量の組み合わせは現れない。
 - 調査対象の作品が増えれば増えるほど行列自体は肥大化するが、そのほとんどは0で埋められる疎行列になると思われる。
 - べつに1000個2000個のタグで現代PCのメモリが飛ぶとは思えないが、それでも無駄遣いはよろしくないので疎行列対策のアルゴリズムも組み込みたい。
 - 昔の仕事でY行列作ったみたいに。
- ランキングの順位や売上数値を含んで調べることで、各タグの組み合わせに重み付けとかできないだろうか。
- pythonの練習にはなったが、**それでこれをどう機械学習に生かせばいいのか思いつかない。**
 - 上手く勉強させれば次に売れる作品の傾向とか読めるんだろうか…
- なんかこのデータ使って面白いことできそうだったらコメント投げてみてください。
 - もしくは、[上で出力したCSV](https://docs.google.com/spreadsheets/d/12zU3987Ehv8CT4zhfZ9JmVMUmMGDqUhYBnUZyNfavUM/edit?usp=sharing)置いとくので、手元で開いてみると暇つぶしにはなるかもしれません。