{ "cells": [ { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "1cf04953-2e3a-469a-bfac-e9f0ef6d2654" } }, "source": [ "# Python による「スクレイピング & 自然言語処理」入門" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "8bf08c21-ee84-4d70-bd11-5121df226180" } }, "source": [ "## 1. はじめに\n", " クローラーとはWeb上のデータを自動的に収集するための道具です。クローラーを活用することで、担当者が手動で行っていたWeb情報収集の効率化、また自社だけでは入手できないさまざまなデータを取得し自社データと結合することで新たな示唆を得ることが可能になります。\n", " \n", " 今回のセミナーでは初心者を対象にクローラーを作成し対象サイトのデータを収集、テキスト解析を行い、分析結果を得るまでの一連の流れについて、Python で使用するライブラリ、解析手法を交えて解説いたします。\n", " \n", " ※本発表は所属する組織とは一切関係がありません\n", "\n", " 今回は対象ページとして、[日本酒物語 日本酒ランキング(人数)](http://www.sakeno.com/followrank/) とそれに紐づく各銘柄の詳細を収集し、各種分析を行います。本解析の内容として次の項目を含みます。\n", "\n", " * 解析のための下準備\n", " * 使用するライブラリのインストール\n", " * 使用するライブラリの読み込み\n", " * 定数の設定\n", " * クローラーによるデータの収集\n", " * ランキング一覧の生の HTML 確認\n", " * ランキング一覧のテーブル要素の取得\n", " * 詳細ページのデータ取得\n", " * テキスト解析\n", " * TFIDF によるレビュー中の特徴的な形容詞の抽出\n", " * 単語ベースのクラスタリング" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. クローラーとは?\n", "\n", "(スライド参照)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 自然言語処理とは?\n", "\n", "(スライド参照)" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "59d5cb39-c80e-48c4-9f89-1d267945f8b3" } }, "source": [ "## 4. 解析のための下準備\n", "### 4. 1 使用するライブラリのインストール" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "b77d35ce-4bcf-4f8b-a636-173e83d1f3bc" } }, "source": [ " まずは形態素解析ツール MeCab のインストールを行います。ここでは Mac (OSX Sierra) を仮定して進めています。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "nbpresent": { "id": "d46d2ea9-f98d-465f-a93a-8f01b96047c0" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[33mWarning:\u001b[0m mecab-0.996 already installed\n", "\u001b[33mWarning:\u001b[0m mecab-ipadic-2.7.0-20070801 already installed\n", "Requirement already satisfied: mecab-python3 in /Users/tojima/anaconda3/lib/python3.5/site-packages\n" ] } ], "source": [ "!brew install mecab mecab-ipadic\n", "!pip install mecab-python3" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "8fc7fa85-df27-4b49-afb9-ce0f58e33fa5" } }, "source": [ " 次にクローラー関係のライブラリもインストールします。ここでは以下の3つのライブラリを導入しています。\n", "\n", "* html5lib\n", "* requests\n", "* BeautifulSoup" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "nbpresent": { "id": "72d4cb84-4bda-40a6-ac1c-0c4e92fe7d34" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fetching package metadata .........\n", "Solving package specifications: .\n", "\n", "# All requested packages already installed.\n", "# packages in environment at /Users/tojima/anaconda3:\n", "#\n", "html5lib 0.999 py35_0 \n", "Fetching package metadata .........\n", "Solving package specifications: .\n", "\n", "# All requested packages already installed.\n", "# packages in environment at /Users/tojima/anaconda3:\n", "#\n", "requests 2.13.0 py35_0 \n", "Fetching package metadata .........\n", "Solving package specifications: .\n", "\n", "# All requested packages already installed.\n", "# packages in environment at /Users/tojima/anaconda3:\n", "#\n", "beautifulsoup4 4.5.3 py35_0 \n" ] } ], "source": [ "!conda install -y html5lib \n", "!conda install -y requests\n", "!conda install -y BeautifulSoup4" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "4fc0feb3-1780-4762-b6b4-dc93644026f8" } }, "source": [ "### 4.2. 使用するライブラリの読み込み\n", "\n", " ここではこの解析に使用する各種ライブラリを読み込んでいます。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true, "nbpresent": { "id": "d284731e-a29b-43e6-a9a8-0a3bf834e900" } }, "outputs": [], "source": [ "# ファイル操作\n", "import glob\n", "import csv\n", "\n", "# データ処理・視覚化\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# クローラー\n", "import time\n", "from datetime import datetime\n", "from bs4 import BeautifulSoup\n", "import requests\n", "\n", "# テキスト解析\n", "import MeCab\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.feature_extraction.text import CountVectorizer\n", "from sklearn.preprocessing import StandardScaler\n", "from sklearn.decomposition import PCA\n", "from sklearn.cluster import AgglomerativeClustering" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "81db66b5-ff05-4bf3-b44f-ec3fc68d804b" } }, "source": [ "### 4.3. 定数の設定\n", "\n", " ここでは取得対象となっているページのURLやクローラーの待ち時間、各種データの出力先を定義しています。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "nbpresent": { "id": "d0450c37-ab4b-41b8-8170-7297d639fb1d" } }, "outputs": [], "source": [ "# 日本酒物語 日本酒ランキング(人数)の URL\n", "FOLLOWRANK_URL = \"http://www.sakeno.com/followrank/\"\n", "\n", "# クロール時の待ち時間\n", "WAIT_TIME = 5\n", "\n", "# 銘柄マスタの出力先\n", "MEIGARA_MASTER_PATH = \"../data/meigara_maseter.csv\"\n", "# 銘柄評価スコアの出力先ディレクトリ\n", "MEIGARA_SCORES_DIR = \"../data/meigara_scores/\"\n", "# 銘柄コメントの出力先ディレクトリ\n", "MEIGARA_COMMENTS_DIR = \"../data/meigara_comments/\"\n", "\n", "# TFIDF スコア算出後の結果出力先\n", "TFIDF_PATH = \"../data/tfidf.csv\"\n", "# クラスタリング結果の結果出力先\n", "CLUSTER_PATH = \"../data/cluster.csv\"" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "70fa09d6-b4a9-4ab9-9a72-ba9a20355e94" } }, "source": [ "## 5. クローラーによるデータの取得\n", "\n", " では、ここからクローラーでデータを取得するための流れを見ていきます。基本的な流れは次のようになります。\n", " \n", "1. 対象ページのHTMLの取得。\n", "2. 取得対象情報が含まれている部分のタグの特定。\n", "3. 取得対象情報のパース。\n", "4. 結果の保存。\n", "\n", "では実際にクローラーのコードを見ていきましょう。\n", "\n", "### 5.1 ランキング一覧の生の HTML 確認\n", "\n", " まずはランキングの一覧ページの HTML を取得します。ここでは `response` ライブラリを利用し取得します。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "nbpresent": { "id": "d4c2aa96-d669-4781-8bfd-a1afd5786e56" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OK.\n" ] } ], "source": [ "# ページの HTML を取得\n", "response = requests.get(FOLLOWRANK_URL)\n", "\n", "# 正しく取得できたかどうか HTTP ステータスコードで確認\n", "if not response.status_code == 200:\n", " raise ValueError(\"Invalid response\")\n", "else:\n", " print(\"OK.\")" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "63a25210-2464-4b81-9ce7-bf6a33800136" } }, "source": [ "正しくランキングページが取得されていれば `OK.` と出力されます。\n", "\n", "次は取得してきた HTML の先頭 1000 件を見てみると…" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "nbpresent": { "id": "1cee42a0-9cc7-429b-8618-9116a1e11184" }, "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "'\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\nÆüËܼò¥é¥ó¥\\xad¥ó¥°¡Ê¿Í¿ô¡Ë¡ÝÆüËܼòʪ¸ì\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n
\\r\\n\\r\\n\\t

\"ÆüËܼòʪ¸ì\"

\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "日本酒ランキング(人数)−日本酒物語\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "\r\n", "
\r\n", "\r\n", "\t

\"日本酒物語\"

〜 ` の部分を抜き出します。この処理には `BeautifulSoup` を利用します。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "nbpresent": { "id": "91c9334a-b059-4311-89af-8d8f9fe0db22" }, "scrolled": true }, "outputs": [], "source": [ "soup = BeautifulSoup(response.text, \"lxml\")\n", "table = soup.body.find(\"table\")" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "c016232f-b707-484f-9a0f-b3625659949b" } }, "source": [ " 次はテーブルの中の行要素を全て取得します。` 〜 ` の部分が各行に該当します。`find_all`メソッドを利用すれば各行が1要素となったリスト構造として取得可能です。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "nbpresent": { "id": "12dc125e-b6be-40a4-98a9-ff97b8eabf37" } }, "outputs": [], "source": [ "trs = table.find_all(\"tr\")[2:] # 先頭のゴミをカット" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "0fb296ad-3054-4bfc-b394-62b7b389515a" } }, "source": [ "各行から必要な数値、文字列を抜き出します。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "nbpresent": { "id": "09966a7c-616f-4b69-9482-230af60e3102" }, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[[1,\n", " '獺祭',\n", " 'だっさい',\n", " '旭酒造(山口県)',\n", " '山口県',\n", " '岩国市',\n", " 'http://www.sakeno.com/meigara/931'],\n", " [2,\n", " '醸し人九平次',\n", " 'かもしびとくへいじ',\n", " '萬乗醸造',\n", " '愛知県',\n", " '名古屋市',\n", " 'http://www.sakeno.com/meigara/735'],\n", " [3,\n", " '出羽桜',\n", " 'でわざくら',\n", " '出羽桜酒造',\n", " '山形県',\n", " '天童市',\n", " 'http://www.sakeno.com/meigara/219'],\n", " [4, '田酒', 'でんしゅ', '西田酒造店', '青森県', '青森市', 'http://www.sakeno.com/meigara/11'],\n", " [5, '黒龍', 'こくりゅう', '黒龍酒造', '福井県', '吉田郡', 'http://www.sakeno.com/meigara/667']]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ある行の情報をパースし以下の要素を取得する。\n", "#\n", "# [ 順位, 銘柄, 銘柄の読み, \n", "# 蔵元, 蔵元の県, 蔵元の市町村,\n", "# 銘柄詳細ページのURL ]\n", "#\n", "def parse_tr(tr):\n", " # 順位\n", " tds = tr.find_all(\"td\")\n", " rank = int(tds[0].get_text().split(\"位\")[0]) \n", "\n", " # 銘柄\n", " a = tds[1].find(\"a\")\n", " meigara = a.get_text()\n", " detail_url = a.get(\"href\")\n", " yomi = tds[1].find(\"div\").string\n", "\n", " # 酒造\n", " location = tds[2].find_all(\"a\")\n", " kuramoto = location[0].string\n", " prefecture = location[1].string\n", " city = location[2].string\n", " \n", " tr_l = [\n", " rank, meigara, yomi,\n", " kuramoto, prefecture, city,\n", " detail_url\n", " ]\n", " return tr_l\n", "\n", "\n", "ranking_list = [parse_tr(tr) for tr in trs]\n", "ranking_list[:5]" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true, "nbpresent": { "id": "5b25f615-b68e-472f-bd36-09c2f41525db" } }, "source": [ "あとの処理のためにデータフレームに変換します。この際にユニークな連番IDを付加します。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "nbpresent": { "id": "c1ff21dc-8407-4f05-b847-90a34f8a60d3" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idrankmeigarayomikuramotoprefecturecitydetail_url
011獺祭だっさい旭酒造(山口県)山口県岩国市http://www.sakeno.com/meigara/931
122醸し人九平次かもしびとくへいじ萬乗醸造愛知県名古屋市http://www.sakeno.com/meigara/735
233出羽桜でわざくら出羽桜酒造山形県天童市http://www.sakeno.com/meigara/219
344田酒でんしゅ西田酒造店青森県青森市http://www.sakeno.com/meigara/11
455黒龍こくりゅう黒龍酒造福井県吉田郡http://www.sakeno.com/meigara/667
\n", "
" ], "text/plain": [ " meigara_id rank meigara yomi kuramoto prefecture city \\\n", "0 1 1 獺祭 だっさい 旭酒造(山口県) 山口県 岩国市 \n", "1 2 2 醸し人九平次 かもしびとくへいじ 萬乗醸造 愛知県 名古屋市 \n", "2 3 3 出羽桜 でわざくら 出羽桜酒造 山形県 天童市 \n", "3 4 4 田酒 でんしゅ 西田酒造店 青森県 青森市 \n", "4 5 5 黒龍 こくりゅう 黒龍酒造 福井県 吉田郡 \n", "\n", " detail_url \n", "0 http://www.sakeno.com/meigara/931 \n", "1 http://www.sakeno.com/meigara/735 \n", "2 http://www.sakeno.com/meigara/219 \n", "3 http://www.sakeno.com/meigara/11 \n", "4 http://www.sakeno.com/meigara/667 " ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "meigara_master_df = pd.DataFrame(\n", " ranking_list,\n", " columns=[\"rank\", \"meigara\", \"yomi\",\n", " \"kuramoto\", \"prefecture\", \"city\",\n", " \"detail_url\"]\n", ")\n", "\n", "# ユニークな連番 ID を追加\n", "meigara_master_df[\"meigara_id\"] = meigara_master_df.index.to_series() + 1\n", "meigara_master_df = meigara_master_df[\n", " [\"meigara_id\",\n", " \"rank\", \"meigara\", \"yomi\",\n", " \"kuramoto\", \"prefecture\", \"city\",\n", " \"detail_url\"]\n", "]\n", "\n", "# 銘柄マスタデータの出力\n", "meigara_master_df.to_csv(\n", " MEIGARA_MASTER_PATH,\n", " encoding=\"utf-8\",\n", " sep=\",\",\n", " index=False\n", ")\n", "\n", "meigara_master_df.head()" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "7d2f2000-c6d8-4e98-abec-75c30a5f429d" } }, "source": [ "ここまでで、ランキング一覧の結果が無事取得できました。\n", "\n", "### 5.3 詳細ページのデータ取得\n", "\n", " 次は先程取得したランキング一覧のデータを利用して、詳細ページ(`detail_url`)から以下の情報を取得します。\n", " * 数値による評価データ (良い/悪い)\n", " * 味\n", " * 香り\n", " * 濃さ\n", " * 価格\n", " * デザイン\n", " * コメント一覧\n", " * 投稿ID\n", " * タイトル\n", " * 投稿日時\n", " * ユーザ名\n", " * テキスト\n", "\n", "これらのデータを集めるために、以下に次の3つの関数を記述しています。\n", "\n", "* 数値データによる評価データ取得用の関数\n", "* コメント一覧取得用の関数\n", "* すべての詳細ページからデータを取得するための関数" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "nbpresent": { "id": "744bc879-1474-45d7-8dac-13110dced92a" } }, "outputs": [], "source": [ "# 数値による評価データ取得関数\n", "def parse_scores_table(soup, meigara_id):\n", " scores_table = soup.body.find_all(\"form\")[1]\n", " trs = scores_table.find_all(\"tr\")\n", "\n", " # 味\n", " aji = trs[2].find_all(\"span\")\n", " aji_good = int(aji[0].string)\n", " aji_bad = int(aji[1].string)\n", "\n", " # 香り\n", " kaori = trs[3].find_all(\"span\")\n", " kaori_good = int(kaori[0].string)\n", " kaori_bad = int(kaori[1].string)\n", "\n", " # 濃さ\n", " kosa = trs[4].find_all(\"span\")\n", " kosa_good = int(kosa[0].string)\n", " kosa_bad = int(kosa[1].string)\n", "\n", " # 価格\n", " kakaku = trs[5].find_all(\"span\")\n", " kakaku_good = int(kakaku[0].string)\n", " kakaku_bad = int(kakaku[1].string)\n", "\n", " # デザイン\n", " design = trs[6].find_all(\"span\")\n", " design_good = int(design[0].string)\n", " design_bad = int(design[1].string)\n", "\n", " score_li = [\n", " [meigara_id, \"味\", aji_good, aji_bad],\n", " [meigara_id, \"香り\", kaori_good, kaori_bad],\n", " [meigara_id, \"濃さ\", kosa_good, kosa_bad],\n", " [meigara_id, \"価格\", kakaku_good, kakaku_bad],\n", " [meigara_id, \"デザイン\", design_good, design_bad]\n", " ]\n", "\n", " score_df = pd.DataFrame(\n", " score_li,\n", " columns=[\"meigara_id\", \"name\", \"good_score\", \"bad_score\"]\n", " )\n", " \n", " # (index) name good_score bad_score\n", " # 0 味 1123 260\n", " # 1 香り 1095 250\n", " # 2 濃さ 978 304\n", " # 3 価格 950 344\n", " # 4 デザイン 975 249\n", " \n", " return score_df" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false, "nbpresent": { "id": "5978c699-dfed-49ac-b6bc-a731fdee1632" } }, "outputs": [], "source": [ "# コメント一覧取得関数\n", "def parse_comments_table(soup, meigara_id):\n", " reviews_table = soup.body.find_all(\"table\")[-1]\n", "\n", " # 以下のような構造になっているため、dtのみ、ddのみで処理し、\n", " # 最後に concat で横方向に単純結合する\n", " #\n", " #
\n", " #
\n", " #
\n", " # ...\n", " #\n", "\n", " #
の処理\n", " dts = [\n", " [meigara_id,\n", " int(dt.contents[0].get(\"name\").replace(\"voice\", \"\")),\n", " dt.contents[3].string]\n", " for dt\n", " in reviews_table.find_all(\"dt\")\n", " ]\n", " dts_df = pd.DataFrame(\n", " dts,\n", " columns=[\"meigara_id\", \"toukou_id\", \"title\"]\n", " )\n", " \n", " #
の処理\n", " dds = [\n", " [dd.contents[-1].text.split(\"(\")[1].split(\")\")[0],\n", " dd.contents[-1].find(\"a\").string,\n", " dd.contents[-2].replace(\"\\n\", \" \")]\n", " for dd\n", " in reviews_table.find_all(\"dd\")\n", " ]\n", " dds_df = pd.DataFrame(\n", " dds,\n", " columns=[\"created_at\", \"user_name\", \"text\"]\n", " )\n", " dds_df[\"created_at\"] = dds_df[\"created_at\"].apply(\n", " lambda x: datetime.strptime(x, '%Y年%m月%d日 %H時%M分%S秒')\n", " )\n", " \n", " # 結合\n", " comments_df = pd.concat([dts_df, dds_df], axis=1)\n", " \n", " # (index) meigara_id toukou_id title created_at user_name text\n", " # 0 1 6193 すっきりして飲みやすい 2016-10-20 11:57:54 あいうそん おいしいです\n", " # ...\n", "\n", " return comments_df" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false, "nbpresent": { "id": "1ef57283-a949-40fe-8e1e-e27ebfec76b9" } }, "outputs": [], "source": [ "# すべての詳細ページからデータを取得するための関数\n", "def parse_maigara_detail_page(row):\n", " print(\n", " datetime.now().isoformat(sep=\" \"),\n", " row[\"meigara_id\"],\n", " row[\"meigara\"]\n", " )\n", " \n", " # 連続アクセス時の負荷軽減\n", " time.sleep(WAIT_TIME)\n", " \n", " # クローリング\n", " response = requests.get(row[\"detail_url\"])\n", " if not response.status_code == 200:\n", " raise ValueError(\"Invalid response\")\n", " response.encoding = 'euc_jp'\n", " # ゴミとなる文字群を除去\n", " preprocessed_html_string = response.text.replace(\"
\", \"\\n\")\n", " preprocessed_html_string = preprocessed_html_string.replace(\"\\r\", \"\")\n", " preprocessed_html_string = preprocessed_html_string.replace(\" \", \" \")\n", " soup = BeautifulSoup(preprocessed_html_string, \"lxml\")\n", " \n", " # 評価スコアの取得 & 出力\n", " score_df = parse_scores_table(soup, row[\"meigara_id\"])\n", " scores_path = MEIGARA_SCORES_DIR + str(row[\"meigara_id\"]) + \".csv\"\n", " score_df.to_csv(\n", " scores_path,\n", " encoding=\"utf-8\",\n", " sep=\",\",\n", " index=False,\n", " quoting=csv.QUOTE_NONNUMERIC\n", " )\n", " \n", " # 評価コメントの取得 & 出力\n", " comments_df = parse_comments_table(soup, row[\"meigara_id\"])\n", " comments_path = MEIGARA_COMMENTS_DIR + str(row[\"meigara_id\"]) + \".csv\"\n", " comments_df.to_csv(\n", " comments_path,\n", " encoding=\"utf-8\",\n", " sep=\",\",\n", " index=False,\n", " quoting=csv.QUOTE_NONNUMERIC\n", " )\n", " return" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "次にこれらのコードを全ての銘柄に対して実行します。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false, "nbpresent": { "id": "9c070306-0d95-4d95-94ff-0f3f0d62aee4" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2017-03-12 17:58:02.529907 1 獺祭\n", "2017-03-12 17:58:09.126493 2 醸し人九平次\n", "2017-03-12 17:58:15.797209 3 出羽桜\n", "2017-03-12 17:58:22.873022 4 田酒\n", "2017-03-12 17:58:29.302859 5 黒龍\n", "2017-03-12 17:58:35.890476 6 飛露喜\n", "2017-03-12 17:58:42.338474 7 新政\n", "2017-03-12 17:58:49.018110 8 雪の茅舎\n", "2017-03-12 17:58:55.609204 9 鳳凰美田\n", "2017-03-12 17:59:01.490036 10 風の森\n", "2017-03-12 17:59:07.875526 11 鍋島\n", "2017-03-12 17:59:14.489016 12 くどき上手\n", "2017-03-12 17:59:20.826484 13 十四代\n", "2017-03-12 17:59:27.388108 14 菊姫\n", "2017-03-12 17:59:34.022284 15 天狗舞\n", "2017-03-12 17:59:40.585017 16 神亀\n", "2017-03-12 17:59:46.673921 17 浦霞\n", "2017-03-12 17:59:52.898387 18 鶴齢\n", "2017-03-12 17:59:59.212296 19 楯野川\n", "2017-03-12 18:00:05.593475 20 八海山\n", "2017-03-12 18:00:12.830346 21 雁木\n", "2017-03-12 18:00:19.296840 22 開運\n", "2017-03-12 18:00:25.956883 23 大七\n", "2017-03-12 18:00:32.476801 24 〆張鶴\n", "2017-03-12 18:00:38.731137 25 久保田\n", "2017-03-12 18:00:45.247792 26 酔鯨\n", "2017-03-12 18:00:51.643924 27 手取川\n", "2017-03-12 18:00:58.161813 28 東一\n", "2017-03-12 18:01:07.472091 29 陸奥八仙\n", "2017-03-12 18:01:14.158923 30 写楽(寫樂)\n", "2017-03-12 18:01:20.226436 31 仙禽\n", "2017-03-12 18:01:26.551723 32 東洋美人\n", "2017-03-12 18:01:33.177094 33 花陽浴\n", "2017-03-12 18:01:39.551707 34 磯自慢\n", "2017-03-12 18:01:46.262988 35 梵\n", "2017-03-12 18:01:52.706725 36 悦凱陣\n", "2017-03-12 18:01:58.956448 37 真澄\n", "2017-03-12 18:02:05.618642 38 秋鹿\n", "2017-03-12 18:02:11.940649 39 奥播磨\n", "2017-03-12 18:02:18.542844 40 王祿\n", "2017-03-12 18:02:24.676691 41 臥龍梅\n", "2017-03-12 18:02:31.741734 42 豊盃\n", "2017-03-12 18:02:38.143491 43 黒牛\n", "2017-03-12 18:02:44.458652 44 伯楽星\n", "2017-03-12 18:02:50.960992 45 義侠\n", "2017-03-12 18:02:57.403678 46 上喜元\n", "2017-03-12 18:03:03.850065 47 日高見\n", "2017-03-12 18:03:10.586871 48 玉川(京都府)\n", "2017-03-12 18:03:16.942453 49 小左衛門\n", "2017-03-12 18:03:23.533112 50 一ノ蔵\n", "2017-03-12 18:03:29.888834 51 作\n", "2017-03-12 18:03:36.620805 52 奈良萬\n", "2017-03-12 18:03:43.160130 53 屋守\n", "2017-03-12 18:03:49.590359 54 村祐\n", "2017-03-12 18:03:55.606912 55 白瀑\n", "2017-03-12 18:04:02.068721 56 南部美人\n", "2017-03-12 18:04:08.546669 57 三芳菊\n", "2017-03-12 18:04:16.465088 58 七田\n", "2017-03-12 18:04:22.600221 59 雨後の月\n", "2017-03-12 18:04:28.704886 60 紀土 KID\n", "2017-03-12 18:04:34.915424 61 亀齢(広島県)\n", "2017-03-12 18:04:41.046368 62 天明\n", "2017-03-12 18:04:47.481380 63 満寿泉\n", "2017-03-12 18:04:53.624541 64 勝駒\n", "2017-03-12 18:04:59.531139 65 蓬莱泉\n", "2017-03-12 18:05:05.942901 66 東光\n", "2017-03-12 18:05:12.123451 67 雅山流\n", "2017-03-12 18:05:18.514607 68 緑川\n", "2017-03-12 18:05:24.588177 69 正雪\n", "2017-03-12 18:05:30.825473 70 まんさくの花\n", "2017-03-12 18:05:37.373877 71 飛良泉\n", "2017-03-12 18:05:43.456885 72 七本槍\n", "2017-03-12 18:05:49.441395 73 澤乃井\n", "2017-03-12 18:05:55.667049 74 遊穂\n", "2017-03-12 18:06:01.888673 75 あさ開\n", "2017-03-12 18:06:08.607785 76 姿\n", "2017-03-12 18:06:15.474695 77 南\n", "2017-03-12 18:06:21.992489 78 宝剣\n", "2017-03-12 18:06:28.424933 79 大那\n", "2017-03-12 18:06:35.182849 80 越乃寒梅\n", "2017-03-12 18:06:41.723710 81 春鹿\n", "2017-03-12 18:06:48.032324 82 常きげん\n", "2017-03-12 18:06:54.589442 83 五橋\n", "2017-03-12 18:07:00.893189 84 加賀鳶\n", "2017-03-12 18:07:07.456926 85 国権\n", "2017-03-12 18:07:14.117180 86 刈穂\n", "2017-03-12 18:07:20.776054 87 松の司\n", "2017-03-12 18:07:27.020833 88 ロ万\n", "2017-03-12 18:07:33.502105 89 剣菱\n", "2017-03-12 18:07:39.757611 90 綿屋\n", "2017-03-12 18:07:45.877950 91 山間\n", "2017-03-12 18:07:52.074591 92 秀鳳\n", "2017-03-12 18:07:58.190948 93 越乃景虎\n", "2017-03-12 18:08:04.403371 94 菊水\n", "2017-03-12 18:08:10.592417 95 亀泉\n", "2017-03-12 18:08:17.113787 96 銀嶺立山\n", "2017-03-12 18:08:23.281406 97 三千盛\n", "2017-03-12 18:08:30.810961 98 尾瀬の雪どけ\n", "2017-03-12 18:08:37.117417 99 ひこ孫\n", "2017-03-12 18:08:43.595980 100 庭のうぐいす\n", "2017-03-12 18:08:49.925061 101 麒麟山\n", "2017-03-12 18:08:56.533390 102 来福\n", "2017-03-12 18:09:04.490698 103 乾坤一\n", "2017-03-12 18:09:10.272546 104 墨廼江\n", "2017-03-12 18:09:16.541895 105 石鎚\n", "2017-03-12 18:09:22.834053 106 初亀\n", "2017-03-12 18:09:29.015368 107 初孫\n", "2017-03-12 18:09:35.386027 108 早瀬浦\n", "2017-03-12 18:09:41.710258 109 羽根屋\n", "2017-03-12 18:09:47.951677 110 天の戸\n", "2017-03-12 18:09:54.279195 111 佐久乃花\n", "2017-03-12 18:10:00.824845 112 幻\n", "2017-03-12 18:10:06.994982 113 郷乃誉\n", "2017-03-12 18:10:13.265061 114 諏訪泉\n", "2017-03-12 18:10:19.696445 115 李白\n", "2017-03-12 18:10:25.903834 116 酔心\n", "2017-03-12 18:10:32.087111 117 長珍\n", "2017-03-12 18:10:38.637781 118 呉春\n", "2017-03-12 18:10:44.877142 119 一白水成\n", "2017-03-12 18:10:50.838438 120 不動\n", "2017-03-12 18:10:56.848904 121 繁桝\n", "2017-03-12 18:11:03.375572 122 蒼空\n", "2017-03-12 18:11:09.275653 123 雪中梅\n", "2017-03-12 18:11:16.715891 124 喜久酔\n", "2017-03-12 18:11:23.209904 125 泉川\n", "2017-03-12 18:11:28.979427 126 車坂\n", "2017-03-12 18:11:35.139811 127 梅乃宿\n", "2017-03-12 18:11:41.624577 128 いづみ橋\n", "2017-03-12 18:11:47.942934 129 大信州\n", "2017-03-12 18:11:54.041809 130 龍力\n", "2017-03-12 18:12:00.506518 131 嘉美心\n", "2017-03-12 18:12:07.910173 132 残草蓬莱\n", "2017-03-12 18:12:14.389312 133 栄光冨士\n", "2017-03-12 18:12:20.607095 134 貴\n", "2017-03-12 18:12:26.494997 135 水芭蕉\n", "2017-03-12 18:12:32.842182 136 萩の鶴\n", "2017-03-12 18:12:39.052504 137 篠峯\n", "2017-03-12 18:12:45.484481 138 日置桜\n", "2017-03-12 18:12:51.802755 139 龍神丸\n", "2017-03-12 18:12:58.025424 140 北雪\n", "2017-03-12 18:13:04.397964 141 鷹勇\n", "2017-03-12 18:13:10.444564 142 上善如水\n", "2017-03-12 18:13:16.541637 143 たかちよ\n", "2017-03-12 18:13:22.887476 144 宗玄\n", "2017-03-12 18:13:30.389642 145 小鼓\n", "2017-03-12 18:13:37.043704 146 鯉川\n", "2017-03-12 18:13:43.258559 147 不老泉\n", "2017-03-12 18:13:49.701382 148 磐城壽\n", "2017-03-12 18:13:55.527370 149 群馬泉\n", "2017-03-12 18:14:01.875800 150 龍勢\n", "2017-03-12 18:14:08.031016 151 鏡山\n", "2017-03-12 18:14:14.378424 152 花垣\n", "2017-03-12 18:14:20.753708 153 白岳仙\n", "2017-03-12 18:14:27.017937 154 笑四季\n", "2017-03-12 18:14:33.638816 155 萩乃露\n", "2017-03-12 18:14:39.690084 156 醴泉\n", "2017-03-12 18:14:46.411015 157 花泉\n", "2017-03-12 18:14:52.805909 158 大山\n", "2017-03-12 18:14:59.338377 159 鳴海\n", "2017-03-12 18:15:07.233578 160 帰山\n", "2017-03-12 18:15:13.895577 161 賀茂金秀\n", "2017-03-12 18:15:20.145517 162 奥の松\n", "2017-03-12 18:15:26.563100 163 宮寒梅\n", "2017-03-12 18:15:32.422080 164 るみ子の酒\n", "2017-03-12 18:15:38.670114 165 甲子\n", "2017-03-12 18:15:44.928295 166 誠鏡\n", "2017-03-12 18:15:51.241363 167 旭日\n", "2017-03-12 18:15:57.226133 168 あぶくま\n", "2017-03-12 18:16:03.706618 169 町田酒造\n", "2017-03-12 18:16:10.099683 170 春霞\n", "2017-03-12 18:16:16.087101 171 賀儀屋\n", "2017-03-12 18:16:22.474515 172 川鶴\n", "2017-03-12 18:16:28.903583 173 明鏡止水\n", "2017-03-12 18:16:35.099438 174 洗心\n", "2017-03-12 18:16:41.198932 175 出雲富士\n", "2017-03-12 18:16:47.213342 176 会津娘\n", "2017-03-12 18:16:52.938671 177 住吉\n", "2017-03-12 18:16:59.059578 178 天吹\n", "2017-03-12 18:17:06.178848 179 会津中将\n", "2017-03-12 18:17:12.259170 180 豊賀\n", "2017-03-12 18:17:18.051022 181 美丈夫\n", "2017-03-12 18:17:24.358635 182 菊正宗\n", "2017-03-12 18:17:30.542959 183 富久長\n", "2017-03-12 18:17:37.288783 184 れいざん\n", "2017-03-12 18:17:43.221305 185 一本義\n", "2017-03-12 18:17:49.443888 186 竹鶴\n", "2017-03-12 18:17:55.976943 187 丹沢山\n", "2017-03-12 18:18:02.464572 188 奥\n", "2017-03-12 18:18:08.285727 189 十九\n", "2017-03-12 18:18:14.013907 190 長陽福娘\n", "2017-03-12 18:18:20.125712 191 白龍(福井県)\n", "2017-03-12 18:18:26.727557 192 美寿々\n", "2017-03-12 18:18:32.445372 193 三連星\n", "2017-03-12 18:18:38.574551 194 天寿\n", "2017-03-12 18:18:45.343555 195 天覧山\n", "2017-03-12 18:18:51.417105 196 酔右衛門\n", "2017-03-12 18:18:57.799544 197 大倉\n", "2017-03-12 18:19:04.069903 198 高千代\n", "2017-03-12 18:19:10.391022 199 相模灘\n", "2017-03-12 18:19:16.763779 200 七賢\n" ] } ], "source": [ "for idx, row in meigara_master_df.iterrows():\n", " parse_maigara_detail_page(row)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここまでで、銘柄のマスタデータ、詳細ページの評価、詳細ページのコメントの情報が手に入りました。" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "nbpresent": { "id": "920d3218-ef66-48ae-98af-3b0e8d3b585e" } }, "source": [ "## 6. テキスト解析\n", "\n", " ここからは先程クローラーで収集したデータを利用して、TFIDFによるレビュー中の特徴的な形容詞の抽出と単語ベースのクラスタリングを行っていきます。\n", "\n", "### 6.1 TFIDF によるレビュー中の特徴的な形容詞の抽出\n", "\n", " この解析では、あるドキュメント中における特徴的な単語(特徴語)の抽出を行います。集めたデータを各種統計処理で扱えるようにするためには、行列形式に変換する必要があります。今回は Bag-of-Words モデルを用いて、単語を行列の形に変換します。Bag-of-Words とは、文章に単語が含まれているかどうかのみを考え、単語の並び方などは考慮しないモデルのことです。一番シンプルなモデルは単語があれば 1、なければ 0 となります。また、単語の出現回数をそのまま使う (Term Frequency) という方法もあります。これは文書中にある単語が含まれている回数をそのまま値として用います。\n", "\n", "```\n", "すもももももももものうち (1)\n", "↓\n", "[すもも, も, もも, も, もも, の, うち] (2)\n", "↓\n", "{すもも: 1, も:2, もも: 2, の: 1, うち:1} (3)\n", "```\n", "\n", " そして各ドキュメントに含まれる単語を列に、文書を行とすると単語の出現回数を要素とした行列形式に変換できます。例として、 (a) 「すもももももももものうち」、(b) 「料理も景色もすばらしい」、(c) 「私の趣味は写真撮影です」という3つの文書を考えます。列のラベルは単語の出現の早い順に [すもも, も, もも, の, うち, 料理, 景色, 素晴らしい, 私, 趣味, は, 写真撮影, です] とすると、文書行列は下記のようになります。\n", "\n", "```\n", "[[1,2,2,1,1,0,0,0,0,0,0,0,0], # (a)\n", " [0,2,0,0,0,1,1,1,0,0,0,0,0], # (b)\n", " [0,0,0,1,0,0,0,0,1,1,1,1,1]] # (c)\n", "```\n", " \n", " 今回は数値に Term Frequency を用います。この変換を行うためには、元のテキストデータを単語単位に分割する必要があります。これを行うためには形態素解析ツールのMeCabを利用します。\n", " \n", " 次に特徴量の計算には TFIDF を用います。TFIDF は TF と IDF というの2つの値を掛けあわせた指標のことです。TF は文書内における単語の出現頻度を表します。これは「ある文書中である単語が何回出現したか」で定義されます。1つの文書に多く出現する単語ほど重要度が高くなります。IDF は多数の文書に出現する単語ほど重要度が低くなるようなスコアです。「ある単語が含まれている文書数を全ての文書数で割ったものの逆数」で定義されます。つまり、TFIDF が大きな値になるということは、「文書内である特定の単語が多く出現し、かつその単語は他の文書ではほとんど出現しない」ということを表します。例えば、「私」という単語は、各文書内における出現回数は多いですが、多くの文書に出現するので重要度は下がります。逆に「特許」という単語は、「特許」を話題の中心にしている特定の文書には文書内には多く現れ、一般的な文書には現れない単語なので重要度は上がります。\n", "\n", " これらの解析を行うためのコードを見ていきましょう。まずは単語の分割を行うための関数を定義します。ここでは対象の品詞のみに絞り込んで、原形のみを抽出するようにしています。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": true, "nbpresent": { "id": "de878479-7f96-484d-88e9-e1d551d38825" } }, "outputs": [], "source": [ "def split_text(text, target_pos=[\"形容詞\"]):\n", " tagger = MeCab.Tagger()\n", " text_str = text\n", " tagger.parse('')\n", " node = tagger.parseToNode(text_str)\n", "\n", " words = []\n", " while node:\n", " l = node.feature.split(\",\")\n", " pos = l[0]\n", " if pos in target_pos:\n", " # unicode 型に戻す\n", " if l[6] == \"*\":\n", " word = node.surface # 変化しない語は表層形をそのまま使う\n", " else:\n", " word = l[6] # 動詞や形容詞は原形を使う\n", " words.append(word)\n", " node = node.next\n", " return \" \".join(words) # スペース区切りで単語を結合し返す" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " では実際に全銘柄のコメントを読み込んで、単語単位に分割し必要な単語のみを取り出してみましょう。" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false, "nbpresent": { "id": "3d9fc4ef-ccbf-4406-b41f-849b10a88d44" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
indexmeigara_idtoukou_idtitlecreated_atuser_nametextsplit
0016193すっきりして飲みやすい2016-10-20 11:57:54あいうそんおいしいですおいしい
1116126獺祭 等外232016-08-08 22:20:28富牟谷欠獺祭 等外23 山田錦23 生酒 27BY ライチ様な立ち香、含み香。抜ける香りはやや甘く。...甘い 強い 淡い ない 濃い
2215992獺祭502016-04-26 19:24:02季がらし獺祭と言えば高精米、磨きが強調され、50%はその最低ランクである しかし全国の銘酒蔵もこの5...美味い 美味い
3315946獺祭等外2016-03-18 20:37:29季がらし旭酒造蔵本の売店で買った普通酒! 山田錦は栽培時、5%以上の等外米(規格外)が出てしまい純米...美味しい 悪い
4415940スパークリング、うすにごり2016-03-13 23:16:16nomuyoshi抜栓直後、瓶から上る香りは若々しく青いような香り。 上る香りはさっぱりとした果物のよう。 口...若々しい 青い 鋭い
\n", "
" ], "text/plain": [ " index meigara_id toukou_id title created_at \\\n", "0 0 1 6193 すっきりして飲みやすい 2016-10-20 11:57:54 \n", "1 1 1 6126 獺祭 等外23 2016-08-08 22:20:28 \n", "2 2 1 5992 獺祭50 2016-04-26 19:24:02 \n", "3 3 1 5946 獺祭等外 2016-03-18 20:37:29 \n", "4 4 1 5940 スパークリング、うすにごり 2016-03-13 23:16:16 \n", "\n", " user_name text \\\n", "0 あいうそん おいしいです \n", "1 富牟谷欠 獺祭 等外23 山田錦23 生酒 27BY ライチ様な立ち香、含み香。抜ける香りはやや甘く。... \n", "2 季がらし 獺祭と言えば高精米、磨きが強調され、50%はその最低ランクである しかし全国の銘酒蔵もこの5... \n", "3 季がらし 旭酒造蔵本の売店で買った普通酒! 山田錦は栽培時、5%以上の等外米(規格外)が出てしまい純米... \n", "4 nomuyoshi 抜栓直後、瓶から上る香りは若々しく青いような香り。 上る香りはさっぱりとした果物のよう。 口... \n", "\n", " split \n", "0 おいしい \n", "1 甘い 強い 淡い ない 濃い \n", "2 美味い 美味い \n", "3 美味しい 悪い \n", "4 若々しい 青い 鋭い " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 全ファイル読み込み\n", "comment_files = glob.glob(\"../data/meigara_comments/*.csv\")\n", "\n", "# 縦方向に単純結合\n", "comment_df = pd.concat([pd.read_csv(f) for f in comment_files])\n", "comment_df = comment_df.reset_index()\n", "\n", "# 全ての text に対して形容詞の抽出を行う\n", "comment_df[\"split\"] = comment_df[\"text\"].map(split_text)\n", "comment_df.head()" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "b02e3e17-d10b-483e-874f-09eca6b784d6" } }, "source": [ "銘柄別に全単語を結合します。" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false, "nbpresent": { "id": "5370a251-d436-41a2-b920-0a7e38f5d92b" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idsplit
01おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い...
12ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ...
23甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ...
34いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良...
45軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい...
\n", "
" ], "text/plain": [ " meigara_id split\n", "0 1 おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い...\n", "1 2 ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ...\n", "2 3 甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ...\n", "3 4 いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良...\n", "4 5 軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい..." ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "meigara_comments_df = comment_df\\\n", " .groupby(\"meigara_id\")[\"split\"]\\\n", " .apply(lambda x: \"%s\" % ' '.join(x))\\\n", " .reset_index()\n", "meigara_comments_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " 今回は数値に Term Frequency を用います。scikit-learn に [CountVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html)というものがあり、単語と列番号の対応付けなどの作業をまとめて行うことが出来ます。この次に TFIDF の計算を行う場合、さらに簡易化したライブラリが存在します。\n", " \n", " TFIDF の計算は scikit-learn の [TfidfVectorizer](http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html) を用いれば、前述の CountVectorizer による行列化と TFIDF の計算を同時に行うことが出来るので、今回はこれを用いて TFIDF の計算を行います。では実際に TfidfVectorizer を利用して、TFIDF の計算を行ってみましょう。" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false, "nbpresent": { "id": "92a65c18-2b62-4821-9b5c-4b5309d74814" } }, "outputs": [ { "data": { "text/plain": [ "<200x292 sparse matrix of type ''\n", "\twith 4099 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vectorizer = TfidfVectorizer()\n", "tfidfs = vectorizer.fit_transform(meigara_comments_df[\"split\"])\n", "tfidfs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "では計算結果から、各銘柄におけるスコアの上位から5単語ずつ取り出してみましょう。" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": true, "nbpresent": { "id": "5fa431d6-952f-40d5-b275-be0bb586217c" } }, "outputs": [], "source": [ "## TFIDF の結果からi 番目のドキュメントの特徴的な上位 n 語を取り出す\n", "def extract_feature_words(terms, tfidfs, i, n):\n", " tfidf_array = tfidfs[i]\n", " top_n_idx = tfidf_array.argsort()[-n:][::-1]\n", " words = [terms[idx] for idx in top_n_idx]\n", " return words" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false, "nbpresent": { "id": "53c35b0b-9ed2-4a4f-9fcb-0b715fb81f62" } }, "outputs": [], "source": [ "# index 順の単語のリスト\n", "terms = vectorizer.get_feature_names()" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false, "nbpresent": { "id": "ca659afd-cb44-4f01-a582-c996048aa598" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idsplithighscore_words
01おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い...良い 美味しい 悪い ない うまい
12ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ...美味い 不味い ない 甘い 旨い
23甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ...素晴らしい 甘い やすい イイ 美味しい
34いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良...良い 旨い 無い うまい ない
45軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい...良い うまい おいしい ない いい
\n", "
" ], "text/plain": [ " meigara_id split \\\n", "0 1 おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い... \n", "1 2 ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ... \n", "2 3 甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ... \n", "3 4 いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良... \n", "4 5 軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい... \n", "\n", " highscore_words \n", "0 良い 美味しい 悪い ない うまい \n", "1 美味い 不味い ない 甘い 旨い \n", "2 素晴らしい 甘い やすい イイ 美味しい \n", "3 良い 旨い 無い うまい ない \n", "4 良い うまい おいしい ない いい " ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "top_n = 5 # 上位5件\n", "highscore_words = [\n", " extract_feature_words(terms, tfidfs.toarray(), i, top_n)\n", " for i\n", " in range(len(meigara_comments_df.index))\n", "]\n", "highscore_words_str = [\" \".join(l) for l in highscore_words]\n", "meigara_comments_df[\"highscore_words\"] = highscore_words_str\n", "meigara_comments_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "銘柄マスタと結合して、結果を確認してみましょう。" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false, "nbpresent": { "id": "4c0c85a9-f4a3-4e4a-a152-7caeb1281b35" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idrankmeigarayomikuramotoprefecturecitydetail_urlsplithighscore_words
011獺祭だっさい旭酒造(山口県)山口県岩国市http://www.sakeno.com/meigara/931おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い...良い 美味しい 悪い ない うまい
122醸し人九平次かもしびとくへいじ萬乗醸造愛知県名古屋市http://www.sakeno.com/meigara/735ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ...美味い 不味い ない 甘い 旨い
233出羽桜でわざくら出羽桜酒造山形県天童市http://www.sakeno.com/meigara/219甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ...素晴らしい 甘い やすい イイ 美味しい
344田酒でんしゅ西田酒造店青森県青森市http://www.sakeno.com/meigara/11いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良...良い 旨い 無い うまい ない
455黒龍こくりゅう黒龍酒造福井県吉田郡http://www.sakeno.com/meigara/667軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい...良い うまい おいしい ない いい
\n", "
" ], "text/plain": [ " meigara_id rank meigara yomi kuramoto prefecture city \\\n", "0 1 1 獺祭 だっさい 旭酒造(山口県) 山口県 岩国市 \n", "1 2 2 醸し人九平次 かもしびとくへいじ 萬乗醸造 愛知県 名古屋市 \n", "2 3 3 出羽桜 でわざくら 出羽桜酒造 山形県 天童市 \n", "3 4 4 田酒 でんしゅ 西田酒造店 青森県 青森市 \n", "4 5 5 黒龍 こくりゅう 黒龍酒造 福井県 吉田郡 \n", "\n", " detail_url \\\n", "0 http://www.sakeno.com/meigara/931 \n", "1 http://www.sakeno.com/meigara/735 \n", "2 http://www.sakeno.com/meigara/219 \n", "3 http://www.sakeno.com/meigara/11 \n", "4 http://www.sakeno.com/meigara/667 \n", "\n", " split highscore_words \n", "0 おいしい 甘い 強い 淡い ない 濃い 美味い 美味い 美味しい 悪い 若々しい 青い 鋭い... 良い 美味しい 悪い ない うまい \n", "1 ない ない 美味い 美味い ない 早い 早い 若い 美味い 高い たまらない いい 甘い ... 美味い 不味い ない 甘い 旨い \n", "2 甘い 柔らかい 無い 美味い イイ やすい 力強い 鋭い 美味い 甘い くどい 旨い 甘い ... 素晴らしい 甘い やすい イイ 美味しい \n", "3 いい 若い 深い 無い 新しい 甘い 濃い 旨い 美味い 高い 物足りない 甘酸っぱい 良... 良い 旨い 無い うまい ない \n", "4 軽い 良い ほしい 強い うまい 不味い 苦い 良い 良い 広い 美味しい ない 美味しい... 良い うまい おいしい ない いい " ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# マスタデータの JOIN\n", "master_df = pd.read_csv(MEIGARA_MASTER_PATH)\n", "result_df = master_df.merge(meigara_comments_df, on=\"meigara_id\", how=\"inner\")\n", "result_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最後に結果をCSVファイルとして出力します。" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false, "nbpresent": { "id": "b02142eb-b6f7-4cf0-af5f-2626ed04ddff" } }, "outputs": [], "source": [ "target_cols = [\"rank\", \"meigara\", \"kuramoto\", \"detail_url\", \"highscore_words\"]\n", "result_df[target_cols].to_csv(TFIDF_PATH, index=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここまでが特徴語抽出を行うまでの一連の流れとなります。" ] }, { "cell_type": "markdown", "metadata": { "nbpresent": { "id": "47ca9a4e-3ee0-483a-8e7e-304e215d1aad" } }, "source": [ "### 6.2 単語ベースのクラスタリング\n", "\n", " 次は評価が似た銘柄同士をまとめる方法を見ていきたいと思います。サンプルデータが大量にある場合、似た者同士をまとめることで、新たな知見が得られる可能性があります。ここではその一連の流れを見ていきます。\n", "\n", " まず最初行わなければならないのは、特徴語の抽出の場合と同じく各文書に含まれる単語を行列形式に変換することです。今回も Term Frequency を用いた Bag-of-Words モデルで変換します。\n", " \n", " 次にクラスタリングを行う前に、行列に対していくつか前処理を行う必要があります。まずレビューの件数が大きく異なるので、数値を標準化してやる必要があります。今回は「Zスコア」という標準化を行います。これは各要素から平均を引いて、標準偏差で割ったものです。この変換を行うと、平均が 0 で標準偏差・分散が 1 になります。この変換を行うためのライブラリとして scikit-learn には StandardScaler があります。また、行列は疎な状態となっています。このような場合は次元圧縮を行うことで、より効率が良く、直感に近いクラスタリング結果を得られます。今回は[主成分分析:PCA](http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html)を利用して次元を圧縮しています。\n", " \n", " 最後にクラスタリングを行います。今回はコサイン類似度を距離の基準とした階層型クラスタリングを行いクラスタを決定しています。\n", " \n", "\n", " では実際のコードを確認していきましょう。まずは対象の単語の抽出です。今回は名詞、動詞、形容詞を抽出しています。\n", " " ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false, "nbpresent": { "id": "8b946015-ef47-4fb4-b577-c1a4e38f6507" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idsplit_noun_verb_adj
01おいしい 獺 祭 等外 2 3 山田 錦 2 3 生酒 2 7 BY ライチ 様 立ち 香 ...
12Wow very oishii Sake ! 米 吟醸 aka 醸す 人 九 平次 彼 地 ...
23上 立つ 仄か 甘い 香り 含み 柔らかい 入る 派手 さ 無い 純大 吟 旨み 特徴 個 ...
34好き 酒 一つ する コク ある いい 感じ 田 酒 特 純生 飲む 開 栓 直後 含む す...
45甘み 感じる する 辛口 。 後味 軽い 酸味 ある 。 ラベル 飲む 方 「 冷やす 」 ...
\n", "
" ], "text/plain": [ " meigara_id split_noun_verb_adj\n", "0 1 おいしい 獺 祭 等外 2 3 山田 錦 2 3 生酒 2 7 BY ライチ 様 立ち 香 ...\n", "1 2 Wow very oishii Sake ! 米 吟醸 aka 醸す 人 九 平次 彼 地 ...\n", "2 3 上 立つ 仄か 甘い 香り 含み 柔らかい 入る 派手 さ 無い 純大 吟 旨み 特徴 個 ...\n", "3 4 好き 酒 一つ する コク ある いい 感じ 田 酒 特 純生 飲む 開 栓 直後 含む す...\n", "4 5 甘み 感じる する 辛口 。 後味 軽い 酸味 ある 。 ラベル 飲む 方 「 冷やす 」 ..." ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "noun_verb_adj_words = comment_df[\"text\"]\\\n", " .apply(lambda x: split_text(x, target_pos=[\"名詞\", \"動詞\", \"形容詞\"]))\n", "comment_df[\"split_noun_verb_adj\"] = noun_verb_adj_words\n", "meigara_comments_df = comment_df\\\n", " .groupby(\"meigara_id\")[\"split_noun_verb_adj\"]\\\n", " .apply(lambda x: \"%s\" % ' '.join(x))\\\n", " .reset_index()\n", "\n", "meigara_comments_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "次に行列への変換です。前述の CountVectorizer を利用することで、簡単に変換できます。" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false, "nbpresent": { "id": "87ca2e2e-c29b-4513-bca1-442fce314571" } }, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0, ..., 0, 0, 1],\n", " [0, 0, 0, ..., 0, 1, 0],\n", " [0, 0, 0, ..., 0, 0, 0],\n", " ..., \n", " [0, 0, 0, ..., 0, 0, 0],\n", " [0, 0, 0, ..., 0, 0, 0],\n", " [0, 0, 0, ..., 0, 0, 0]], dtype=int64)" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vectorizer = CountVectorizer()\n", "word_counts = vectorizer.fit_transform(meigara_comments_df.split_noun_verb_adj)\n", "wca = word_counts.toarray()\n", "wca" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "StandardScaler を利用することで標準化も簡単に行えます(int 型から float 型への変換は意図通りなので警告内容については問題ない)。" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false, "nbpresent": { "id": "17db465f-1601-4817-abd9-951641652391" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/tojima/anaconda3/lib/python3.5/site-packages/sklearn/utils/validation.py:429: DataConversionWarning: Data with input dtype int64 was converted to float64 by StandardScaler.\n", " warnings.warn(msg, _DataConversionWarning)\n" ] } ], "source": [ "sds = StandardScaler()\n", "X = sds.fit_transform(wca)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "次は次元圧縮を行います。今回は30次元に圧縮しています。この圧縮された行列の各行は、それぞれの文書が表す概念を表現しているものとなり、概念ベクトルと呼べるものとなります。" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false, "nbpresent": { "id": "256fcb8a-adea-4753-ba98-cc2b8240d36f" } }, "outputs": [ { "data": { "text/plain": [ "array([[ 2.28167681e+02, -1.16255034e+02, -4.89383813e+01, ...,\n", " 9.14023707e-02, -8.60942515e-01, -6.03637929e-01],\n", " [ 1.12847840e+02, 1.94123781e+02, -6.26221348e+01, ...,\n", " 3.46364954e-01, -1.24565097e+00, -6.24311590e-01],\n", " [ 2.25193159e+01, 6.46982151e+00, 1.69020459e+01, ...,\n", " 1.00232518e+00, 6.03836210e-02, -5.87677278e-02],\n", " ..., \n", " [ -8.47630942e+00, -2.13891471e+00, -4.14508268e+00, ...,\n", " -8.44277795e-02, -4.82077079e-01, -4.45857641e-01],\n", " [ -4.62522339e+00, -3.79801541e-01, -3.06852519e+00, ...,\n", " 4.29486114e-01, -1.49074427e+00, 2.41093999e-01],\n", " [ -6.23976436e+00, -1.28528158e+00, -2.87711006e+00, ...,\n", " 1.53063625e-01, 9.22628033e-02, -9.44972731e-01]])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = PCA(n_components=30)\n", "X_decomp = model.fit_transform(X)\n", "X_decomp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "さて、ここまでで下準備が終わったのでクラスタリングを実行しましょう。今回はコサイン類似度を基準とした6つのクラスタに分けています。" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false, "nbpresent": { "id": "1ea45725-6f05-43e0-8acd-a085f2aff878" } }, "outputs": [ { "data": { "text/plain": [ "array([2, 2, 2, 2, 2, 5, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 1, 3, 0, 0,\n", " 1, 2, 3, 3, 3, 3, 0, 0, 0, 3, 0, 2, 0, 2, 1, 3, 3, 2, 1, 3, 3, 3, 0,\n", " 0, 3, 3, 3, 3, 3, 3, 3, 1, 2, 0, 3, 3, 3, 3, 0, 0, 3, 2, 3, 3, 3, 3,\n", " 3, 3, 0, 0, 3, 2, 3, 3, 0, 3, 2, 3, 3, 3, 3, 3, 0, 4, 3, 2, 3, 3, 3,\n", " 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 2, 3, 3, 3, 3, 3, 0, 3, 3,\n", " 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 3, 3, 3, 3,\n", " 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,\n", " 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,\n", " 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3])" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = AgglomerativeClustering(n_clusters=6, linkage=\"average\", affinity=\"cosine\")\n", "y = model.fit_predict(X_decomp)\n", "y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "クラスタ番号が算出できたので、銘柄マスタデータと結合し結果を確認してみましょう。" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false, "nbpresent": { "id": "9b01167e-a6ef-48db-aaa0-34f2f0c689e1" }, "scrolled": false }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
meigara_idrankmeigarayomikuramotoprefecturecitydetail_urlsplit_noun_verb_adjcluster
677新政あらまさ新政酒造秋田県秋田市http://www.sakeno.com/meigara/55冷蔵 する いる 気 つける 開 栓 する プシュー 吹き出す しまう 半年 寝かせる いる...0
897鳳凰美田ほうおうびでん小林酒造(栃木県)栃木県小山市http://www.sakeno.com/meigara/95鳳凰 美田 濾過 本 生 2 0 1 7 / 2 頂く アル 添 フルーティー 香り ラベル...0
91010風の森かぜのもり油長酒造奈良県御所市http://www.sakeno.com/meigara/898風 森 ALPHA TYPE 3 米 吟醸 八 錦 5 0 2 7 BY 軽い 吟醸 香 含...0
101110鍋島なべしま富久千代酒造佐賀県鹿島市http://www.sakeno.com/meigara/1482春先 購入 する 半年 寝かせる もの タッチ チリ する 白 ワイン 的 ブドウ シャープ...0
161717浦霞うらかすみ佐浦宮城県塩竈市http://www.sakeno.com/meigara/47素っ気 ラベル 純 米 事 原酒 事 わかる メチャクチャ ぶっきらぼう 強面 塩釜 漁師 ...0
171818鶴齢かくれい青木酒造(新潟県)新潟県南魚沼市http://www.sakeno.com/meigara/583鶴 齢 特別 米 越 淡い 麗 5 5 % 濾過 生 原酒 2 8 BY 綺麗 果実 香 含...0
181918楯野川たてのかわ楯の川酒造山形県酒田市http://www.sakeno.com/meigara/229楯 野川 純 米 吟醸 山田 5 0 汲む 夏 熟 2 7 BY 好い 熟れる 甘い 果実 ...0
212221開運かいうん土井酒造場静岡県掛川市http://www.sakeno.com/meigara/729酒屋 特 本 思う 特 純 特 純 包み 紙 オレンジ ん 思い返す 飲 購入 する みる ...0
222321大七だいしち大七酒造福島県二本松市http://www.sakeno.com/meigara/381全体 的 濃い 醇 いう もと 生まれる コシ 力強い さ ある いい ん じん わり 感じ...0
293030写楽(寫樂)しゃらく宮泉銘醸福島県会津若松市http://www.sakeno.com/meigara/1804一 回 火入れ 米 酒 バランス いい 言う の 特徴 感じる られる の 甘い さ 旨み ...0
303130仙禽せんきん株式会社せんきん栃木県さくら市http://www.sakeno.com/meigara/457米 栃木 県 さくら 市 山田 錦 仕込 水 地域 地下 水 使用 ドメーヌ 化 する 米 ...0
313232東洋美人とうようびじん澄川酒造場山口県萩市http://www.sakeno.com/meigara/1094シリーズ 使用 米 ラベル 記載 する れる いる 筈 ん コレ 米 品種 名 書く れる ...0
333432磯自慢いそじまん磯自慢酒造静岡県焼津市http://www.sakeno.com/meigara/725磯 自慢 特別 醸造 山田 5 5 6 0 2 7 BY メロン 様 優しい 清楚 香り 柔...0
353636悦凱陣よろこびがいじん丸尾本店香川県仲多度郡http://www.sakeno.com/meigara/325悦 凱陣 山 廃 米 ろ過 生 赤磐 雄 町 6 8 2 6 BY 微か 酸 感じる させる...0
454645上喜元じょうきげん酒田酒造山形県酒田市http://www.sakeno.com/meigara/224上 喜 米 吟醸 仕込 五 五 号 濾過 生 原酒 杜氏 佐藤 正一 渾身 山田 白玉 5 ...0
464747日高見ひたかみ平孝酒造宮城県石巻市http://www.sakeno.com/meigara/184うすい にごる フレッシュ テクスチャー 林檎 的 香味 マッチング 素晴らしい 生原 酒 ...0
565757三芳菊みよしきく三芳菊酒造徳島県三好市http://www.sakeno.com/meigara/1456グラス 注ぐ 瞬間 立ちあがる 華やか 芳香 含む 甘酸っぱい パイナップル 系 甘味 口 ...0
616261天明てんめい曙酒造福島県河沼郡http://www.sakeno.com/meigara/404天明 通常 飲む こと ない 本来 ノーマル 飲む 比較 する タイプ の 情報 なる 思う...0
626361満寿泉ますいずみ桝田酒造店富山県富山市http://www.sakeno.com/meigara/621米 酒 生酒 生酒 言う フレッシュ 甘い イメージ ある の 違う 印象 若干 香り 高い...0
717270七本槍しちほんやり冨田酒造滋賀県長浜市http://www.sakeno.com/meigara/1211何 気 飲む スペック 飲む 瞬間 器 大きい さ 感じる 精白 ら する さ イメージ す...0
727370澤乃井さわのい小澤酒造東京都青梅市http://www.sakeno.com/meigara/112淡い 麗 辛口 飲む 口 いい クセ ない 素直 味 旨み する いる 呑む やすい コスト...0
777877宝剣ほうけん宝剣酒造広島県呉市http://www.sakeno.com/meigara/1020含む 意外 熟れる 果実 香 ムン 感じる 生 フレッシュ さ イチゴ マスカット 様 甘味...0
858677刈穂かりほ刈穂酒造秋田県大仙市http://www.sakeno.com/meigara/198含む フレッシュ 直球 的 香味 感じる 日ハム 大谷 投手 オーラ ある 味わい バスンバ...0
102103101乾坤一けんこんいち大沼酒造店宮城県柴田郡http://www.sakeno.com/meigara/1153含む ハチミツ よう する 甘味 口 中 フワ ッ 波打つ よう 広がる いく 酸 弱める ...0
112113113郷乃誉さとのほまれ須藤本家(茨城県)茨城県笠間市http://www.sakeno.com/meigara/1081香り 控え目 特有 フレッシュ 甘味 ある する 口当たり ドライテイスト 余韻 する 酸 ...0
118119113一白水成いっぱくすいせい福禄寿酒造秋田県南秋田郡http://www.sakeno.com/meigara/1753目 とまる 一 白水 成 四 合 瓶 見る ラベル 左上 plus +」 ある 店員 さん ...0
129130128龍力たつりき本田商店兵庫県姫路市http://www.sakeno.com/meigara/871ひる 燻蒸 香 太い すぎる よい うまい さ 燗 する 燻蒸 クセ 和らぐ よう メロン ...0
133134128たか永山本家酒造場山口県宇部市http://www.sakeno.com/meigara/1085オリ 甘い さ 中 バナナ 感じる 甘味 中盤 活性 にごる 来る 炭酸 押し寄せる 強い ...0
153154144笑四季えみしき笑四季酒造滋賀県甲賀市http://www.sakeno.com/meigara/784笑 四季 特別 米 Polynesia 生 アル 原酒 2 7 BY 熟れる 果実 濃厚 甘...0
192020八海山はっかいさん八海醸造新潟県南魚沼市http://www.sakeno.com/meigara/593八海山 言う こと 味 薄い そう 思う いる 米 原酒 。 する 旨み コク ある これ ...1
.................................
170171168賀儀屋かぎや成龍酒造愛媛県西条市http://www.sakeno.com/meigara/1536伊予 賀 儀 屋 純大 しずく 媛 4 5 生原 2 6 BY 控えめ 香り 甘い 穏やか ...3
171172168川鶴かわつる川鶴酒造香川県観音寺市http://www.sakeno.com/meigara/337川鶴 特別 米 Wisdom 限定 生 原酒 山田 / オオセト ・ 5 0 5 5 2 8...3
172173168明鏡止水めいきょうしすい大澤酒造長野県佐久市http://www.sakeno.com/meigara/525これ 当たり 旨味 濃い フルーティー 色 黄み かう 1 日 目 尖る 3 日 目 マイル...3
173174168洗心せんしん朝日酒造(新潟県)新潟県長岡市http://www.sakeno.com/meigara/1171私 1 2 年 前 プロデュース する 下田 富士 米 吟醸 富士 錦 洗心 飲む 比べる ...3
174175168出雲富士いずもふじ富士酒造島根県出雲市http://www.sakeno.com/meigara/971出雲 市 契約 農家 さん 特別 栽培 山田 錦 100 % 使用 35 % 贅沢 磨く 上...3
175176168会津娘あいづむすめ高橋庄作酒造店福島県会津若松市http://www.sakeno.com/meigara/80含む リンゴ パイナップル 感じる ドライ フルーツ よう 上品 果実 的 香味 バランス ...3
176177168住吉すみよし樽平酒造山形県東置賜郡http://www.sakeno.com/meigara/247かすか 杉 香り ある 酒 自体 冷 辛口 角 トンガッタ よう 感じ 痛い なる よう 味...3
177178168天吹あまぶき天吹酒造佐賀県三養基郡http://www.sakeno.com/meigara/288精米 歩合 60 % 米 酒 酸味 ある 辛口 うまい 。 温める うまみ 消える 酸味 残...3
178179168会津中将あいづちゅうじょう鶴乃江酒造福島県会津若松市http://www.sakeno.com/meigara/396うまい すごい きれい 酒 食 中 うまい 美味しい 香り 穏やか 甘味 酸味 旨味 バラン...3
179180168豊賀とよか高沢酒造長野県上高井郡http://www.sakeno.com/meigara/1601豊 賀 米 吟醸 天女 しずく 中 取る 濾過 生 原酒 美山 錦 5 9 長野 酵母 2 ...3
180181168美丈夫びじょうぶ浜川商店高知県安芸郡http://www.sakeno.com/meigara/375間違い ない 上品 酒 質 値段 安い たまらない 辛口 おとなしい 酒 味 辛口 クリア ...3
181182168菊正宗きくまさむね菊正宗酒造兵庫県神戸市http://www.sakeno.com/meigara/842何 よい の 菊 正 純 米 目 入る 買う みる 冷やす 書く ある 冷やす 飲む みる ...3
182183168富久長ふくちょう今田酒造本店広島県東広島市http://www.sakeno.com/meigara/1040甘み ある 有名 米 吟醸 みたい 女性 勧める 地元 広島 大手 スーパー 配置 する れ...3
184185168一本義いっぽんぎ一本義久保本店福井県勝山市http://www.sakeno.com/meigara/1083文句 ない 爽快 感 至高 廉価 (^∇^) 淡い 麗 辛口 香り 昔 ある 日本 酒 ( ...3
185186168竹鶴たけつる竹鶴酒造広島県竹原市http://www.sakeno.com/meigara/1036竹 鶴 純 米 熱 燗冷まし 新た 飲む 方 教える くれる にごり酒 純 米 にごる 冷 ...3
186187168丹沢山たんざわさん川西屋酒造店神奈川県足柄上郡http://www.sakeno.com/meigara/498すう 私 蔵 訪問 する 吟 旨い さ 惚れ込む 販売 上 策 名 する その後 当時 2 ...3
187188168おく山崎合資愛知県西尾市http://www.sakeno.com/meigara/1253さわやか ラムネ 香 しつこい 甘味 酸味 する 炭酸 夏 酒 する ドライ 仕上がり ソフ...3
188189168十九じゅうく尾澤酒造場長野県長野市http://www.sakeno.com/meigara/1106最初 感じる の かすか 吟醸 香 中 甘い さ 味 濃い 酒 味 強い すぎる 寿司 一緒...3
189190168長陽福娘ちょうようふくむすめ岩崎酒造山口県萩市http://www.sakeno.com/meigara/1742日本 酒 度 甘い ほう 夏みかん よう 香り のどごし いい 甘い 酒 飲む 進む くどい...3
190191168白龍(福井県)はくりゅう吉田酒造(福井県)福井県吉田郡http://www.sakeno.com/meigara/1288米 吟醸 頂く 米 吟醸 飲める 味 辛口 飲める すぎる ちゃう 気 する 値段 する 満足3
192193168三連星さんれんせい美冨久酒造滋賀県甲賀市http://www.sakeno.com/meigara/1653含む 生らす いる フレッシュ 風味 綺麗 質感 中盤 余韻 甘酸っぱい 酸味 印象 的 酸...3
193194168天寿てんじゅ天寿酒造秋田県由利本荘市http://www.sakeno.com/meigara/197原酒 香り 少ない 旨味 アルコール 感 強い 涼 冷え 常温 非常 旨い ここ 酒 近い ...3
194195168天覧山てんらんざん五十嵐酒造埼玉県飯能市http://www.sakeno.com/meigara/1120美山 錦 精米 歩合 65 %、 日本 酒 度 - 5 酸度 1 . 8 アルコール 15 ...3
195196168酔右衛門よえもん川村酒造店岩手県花巻市http://www.sakeno.com/meigara/1269アルコール 度数 17 18 精米 歩合 50 % 発泡 炭酸 これ 華やか フレッシュ 吟...3
196197168大倉おおくら大倉本家奈良県香芝市http://www.sakeno.com/meigara/1273雄 町 ひる ひかり オオセト 3 種類 米 作る 酒 責め ところ ブレンド 責め 責め ...3
197198168高千代たかちよ高千代酒造新潟県南魚沼市http://www.sakeno.com/meigara/14422 年 前 飲む 感動 する 日本 酒 香り メロン 味 重い 飲める いる かちよ 濃厚 ...3
198199168相模灘さがみなだ久保田酒造(神奈川県)神奈川県相模原市http://www.sakeno.com/meigara/1241開 栓 直後 含む ピリリ ガス 感 原酒 濃厚 フレッシュ バター 甘味 口 中 一 杯 ...3
199200168七賢しちけん山梨銘醸山梨県北杜市http://www.sakeno.com/meigara/506今 日本 酒 苦手 敬遠 する 毎年 お世話 なる いる 山中湖 民宿 主人 すすめ 試す ...3
868777松の司まつのつかさ松瀬酒造滋賀県蒲生郡http://www.sakeno.com/meigara/793松 司 純 米 吟醸 楽 しぼる たて 山田 錦 吟 吹雪 6 0 2 8 BY 僅か セメ...4
566飛露喜ひろき廣木酒造本店福島県河沼郡http://www.sakeno.com/meigara/409今宵 息子 家 飲む こと 飛 露 喜 特別 純 米 セレクト 予想 する れる 飲む 飽き...5
\n", "

200 rows × 10 columns

\n", "
" ], "text/plain": [ " meigara_id rank meigara yomi kuramoto prefecture city \\\n", "6 7 7 新政 あらまさ 新政酒造 秋田県 秋田市 \n", "8 9 7 鳳凰美田 ほうおうびでん 小林酒造(栃木県) 栃木県 小山市 \n", "9 10 10 風の森 かぜのもり 油長酒造 奈良県 御所市 \n", "10 11 10 鍋島 なべしま 富久千代酒造 佐賀県 鹿島市 \n", "16 17 17 浦霞 うらかすみ 佐浦 宮城県 塩竈市 \n", "17 18 18 鶴齢 かくれい 青木酒造(新潟県) 新潟県 南魚沼市 \n", "18 19 18 楯野川 たてのかわ 楯の川酒造 山形県 酒田市 \n", "21 22 21 開運 かいうん 土井酒造場 静岡県 掛川市 \n", "22 23 21 大七 だいしち 大七酒造 福島県 二本松市 \n", "29 30 30 写楽(寫樂) しゃらく 宮泉銘醸 福島県 会津若松市 \n", "30 31 30 仙禽 せんきん 株式会社せんきん 栃木県 さくら市 \n", "31 32 32 東洋美人 とうようびじん 澄川酒造場 山口県 萩市 \n", "33 34 32 磯自慢 いそじまん 磯自慢酒造 静岡県 焼津市 \n", "35 36 36 悦凱陣 よろこびがいじん 丸尾本店 香川県 仲多度郡 \n", "45 46 45 上喜元 じょうきげん 酒田酒造 山形県 酒田市 \n", "46 47 47 日高見 ひたかみ 平孝酒造 宮城県 石巻市 \n", "56 57 57 三芳菊 みよしきく 三芳菊酒造 徳島県 三好市 \n", "61 62 61 天明 てんめい 曙酒造 福島県 河沼郡 \n", "62 63 61 満寿泉 ますいずみ 桝田酒造店 富山県 富山市 \n", "71 72 70 七本槍 しちほんやり 冨田酒造 滋賀県 長浜市 \n", "72 73 70 澤乃井 さわのい 小澤酒造 東京都 青梅市 \n", "77 78 77 宝剣 ほうけん 宝剣酒造 広島県 呉市 \n", "85 86 77 刈穂 かりほ 刈穂酒造 秋田県 大仙市 \n", "102 103 101 乾坤一 けんこんいち 大沼酒造店 宮城県 柴田郡 \n", "112 113 113 郷乃誉 さとのほまれ 須藤本家(茨城県) 茨城県 笠間市 \n", "118 119 113 一白水成 いっぱくすいせい 福禄寿酒造 秋田県 南秋田郡 \n", "129 130 128 龍力 たつりき 本田商店 兵庫県 姫路市 \n", "133 134 128 貴 たか 永山本家酒造場 山口県 宇部市 \n", "153 154 144 笑四季 えみしき 笑四季酒造 滋賀県 甲賀市 \n", "19 20 20 八海山 はっかいさん 八海醸造 新潟県 南魚沼市 \n", ".. ... ... ... ... ... ... ... \n", "170 171 168 賀儀屋 かぎや 成龍酒造 愛媛県 西条市 \n", "171 172 168 川鶴 かわつる 川鶴酒造 香川県 観音寺市 \n", "172 173 168 明鏡止水 めいきょうしすい 大澤酒造 長野県 佐久市 \n", "173 174 168 洗心 せんしん 朝日酒造(新潟県) 新潟県 長岡市 \n", "174 175 168 出雲富士 いずもふじ 富士酒造 島根県 出雲市 \n", "175 176 168 会津娘 あいづむすめ 高橋庄作酒造店 福島県 会津若松市 \n", "176 177 168 住吉 すみよし 樽平酒造 山形県 東置賜郡 \n", "177 178 168 天吹 あまぶき 天吹酒造 佐賀県 三養基郡 \n", "178 179 168 会津中将 あいづちゅうじょう 鶴乃江酒造 福島県 会津若松市 \n", "179 180 168 豊賀 とよか 高沢酒造 長野県 上高井郡 \n", "180 181 168 美丈夫 びじょうぶ 浜川商店 高知県 安芸郡 \n", "181 182 168 菊正宗 きくまさむね 菊正宗酒造 兵庫県 神戸市 \n", "182 183 168 富久長 ふくちょう 今田酒造本店 広島県 東広島市 \n", "184 185 168 一本義 いっぽんぎ 一本義久保本店 福井県 勝山市 \n", "185 186 168 竹鶴 たけつる 竹鶴酒造 広島県 竹原市 \n", "186 187 168 丹沢山 たんざわさん 川西屋酒造店 神奈川県 足柄上郡 \n", "187 188 168 奥 おく 山崎合資 愛知県 西尾市 \n", "188 189 168 十九 じゅうく 尾澤酒造場 長野県 長野市 \n", "189 190 168 長陽福娘 ちょうようふくむすめ 岩崎酒造 山口県 萩市 \n", "190 191 168 白龍(福井県) はくりゅう 吉田酒造(福井県) 福井県 吉田郡 \n", "192 193 168 三連星 さんれんせい 美冨久酒造 滋賀県 甲賀市 \n", "193 194 168 天寿 てんじゅ 天寿酒造 秋田県 由利本荘市 \n", "194 195 168 天覧山 てんらんざん 五十嵐酒造 埼玉県 飯能市 \n", "195 196 168 酔右衛門 よえもん 川村酒造店 岩手県 花巻市 \n", "196 197 168 大倉 おおくら 大倉本家 奈良県 香芝市 \n", "197 198 168 高千代 たかちよ 高千代酒造 新潟県 南魚沼市 \n", "198 199 168 相模灘 さがみなだ 久保田酒造(神奈川県) 神奈川県 相模原市 \n", "199 200 168 七賢 しちけん 山梨銘醸 山梨県 北杜市 \n", "86 87 77 松の司 まつのつかさ 松瀬酒造 滋賀県 蒲生郡 \n", "5 6 6 飛露喜 ひろき 廣木酒造本店 福島県 河沼郡 \n", "\n", " detail_url \\\n", "6 http://www.sakeno.com/meigara/55 \n", "8 http://www.sakeno.com/meigara/95 \n", "9 http://www.sakeno.com/meigara/898 \n", "10 http://www.sakeno.com/meigara/1482 \n", "16 http://www.sakeno.com/meigara/47 \n", "17 http://www.sakeno.com/meigara/583 \n", "18 http://www.sakeno.com/meigara/229 \n", "21 http://www.sakeno.com/meigara/729 \n", "22 http://www.sakeno.com/meigara/381 \n", "29 http://www.sakeno.com/meigara/1804 \n", "30 http://www.sakeno.com/meigara/457 \n", "31 http://www.sakeno.com/meigara/1094 \n", "33 http://www.sakeno.com/meigara/725 \n", "35 http://www.sakeno.com/meigara/325 \n", "45 http://www.sakeno.com/meigara/224 \n", "46 http://www.sakeno.com/meigara/184 \n", "56 http://www.sakeno.com/meigara/1456 \n", "61 http://www.sakeno.com/meigara/404 \n", "62 http://www.sakeno.com/meigara/621 \n", "71 http://www.sakeno.com/meigara/1211 \n", "72 http://www.sakeno.com/meigara/112 \n", "77 http://www.sakeno.com/meigara/1020 \n", "85 http://www.sakeno.com/meigara/198 \n", "102 http://www.sakeno.com/meigara/1153 \n", "112 http://www.sakeno.com/meigara/1081 \n", "118 http://www.sakeno.com/meigara/1753 \n", "129 http://www.sakeno.com/meigara/871 \n", "133 http://www.sakeno.com/meigara/1085 \n", "153 http://www.sakeno.com/meigara/784 \n", "19 http://www.sakeno.com/meigara/593 \n", ".. ... \n", "170 http://www.sakeno.com/meigara/1536 \n", "171 http://www.sakeno.com/meigara/337 \n", "172 http://www.sakeno.com/meigara/525 \n", "173 http://www.sakeno.com/meigara/1171 \n", "174 http://www.sakeno.com/meigara/971 \n", "175 http://www.sakeno.com/meigara/80 \n", "176 http://www.sakeno.com/meigara/247 \n", "177 http://www.sakeno.com/meigara/288 \n", "178 http://www.sakeno.com/meigara/396 \n", "179 http://www.sakeno.com/meigara/1601 \n", "180 http://www.sakeno.com/meigara/375 \n", "181 http://www.sakeno.com/meigara/842 \n", "182 http://www.sakeno.com/meigara/1040 \n", "184 http://www.sakeno.com/meigara/1083 \n", "185 http://www.sakeno.com/meigara/1036 \n", "186 http://www.sakeno.com/meigara/498 \n", "187 http://www.sakeno.com/meigara/1253 \n", "188 http://www.sakeno.com/meigara/1106 \n", "189 http://www.sakeno.com/meigara/1742 \n", "190 http://www.sakeno.com/meigara/1288 \n", "192 http://www.sakeno.com/meigara/1653 \n", "193 http://www.sakeno.com/meigara/197 \n", "194 http://www.sakeno.com/meigara/1120 \n", "195 http://www.sakeno.com/meigara/1269 \n", "196 http://www.sakeno.com/meigara/1273 \n", "197 http://www.sakeno.com/meigara/1442 \n", "198 http://www.sakeno.com/meigara/1241 \n", "199 http://www.sakeno.com/meigara/506 \n", "86 http://www.sakeno.com/meigara/793 \n", "5 http://www.sakeno.com/meigara/409 \n", "\n", " split_noun_verb_adj cluster \n", "6 冷蔵 する いる 気 つける 開 栓 する プシュー 吹き出す しまう 半年 寝かせる いる... 0 \n", "8 鳳凰 美田 濾過 本 生 2 0 1 7 / 2 頂く アル 添 フルーティー 香り ラベル... 0 \n", "9 風 森 ALPHA TYPE 3 米 吟醸 八 錦 5 0 2 7 BY 軽い 吟醸 香 含... 0 \n", "10 春先 購入 する 半年 寝かせる もの タッチ チリ する 白 ワイン 的 ブドウ シャープ... 0 \n", "16 素っ気 ラベル 純 米 事 原酒 事 わかる メチャクチャ ぶっきらぼう 強面 塩釜 漁師 ... 0 \n", "17 鶴 齢 特別 米 越 淡い 麗 5 5 % 濾過 生 原酒 2 8 BY 綺麗 果実 香 含... 0 \n", "18 楯 野川 純 米 吟醸 山田 5 0 汲む 夏 熟 2 7 BY 好い 熟れる 甘い 果実 ... 0 \n", "21 酒屋 特 本 思う 特 純 特 純 包み 紙 オレンジ ん 思い返す 飲 購入 する みる ... 0 \n", "22 全体 的 濃い 醇 いう もと 生まれる コシ 力強い さ ある いい ん じん わり 感じ... 0 \n", "29 一 回 火入れ 米 酒 バランス いい 言う の 特徴 感じる られる の 甘い さ 旨み ... 0 \n", "30 米 栃木 県 さくら 市 山田 錦 仕込 水 地域 地下 水 使用 ドメーヌ 化 する 米 ... 0 \n", "31 シリーズ 使用 米 ラベル 記載 する れる いる 筈 ん コレ 米 品種 名 書く れる ... 0 \n", "33 磯 自慢 特別 醸造 山田 5 5 6 0 2 7 BY メロン 様 優しい 清楚 香り 柔... 0 \n", "35 悦 凱陣 山 廃 米 ろ過 生 赤磐 雄 町 6 8 2 6 BY 微か 酸 感じる させる... 0 \n", "45 上 喜 米 吟醸 仕込 五 五 号 濾過 生 原酒 杜氏 佐藤 正一 渾身 山田 白玉 5 ... 0 \n", "46 うすい にごる フレッシュ テクスチャー 林檎 的 香味 マッチング 素晴らしい 生原 酒 ... 0 \n", "56 グラス 注ぐ 瞬間 立ちあがる 華やか 芳香 含む 甘酸っぱい パイナップル 系 甘味 口 ... 0 \n", "61 天明 通常 飲む こと ない 本来 ノーマル 飲む 比較 する タイプ の 情報 なる 思う... 0 \n", "62 米 酒 生酒 生酒 言う フレッシュ 甘い イメージ ある の 違う 印象 若干 香り 高い... 0 \n", "71 何 気 飲む スペック 飲む 瞬間 器 大きい さ 感じる 精白 ら する さ イメージ す... 0 \n", "72 淡い 麗 辛口 飲む 口 いい クセ ない 素直 味 旨み する いる 呑む やすい コスト... 0 \n", "77 含む 意外 熟れる 果実 香 ムン 感じる 生 フレッシュ さ イチゴ マスカット 様 甘味... 0 \n", "85 含む フレッシュ 直球 的 香味 感じる 日ハム 大谷 投手 オーラ ある 味わい バスンバ... 0 \n", "102 含む ハチミツ よう する 甘味 口 中 フワ ッ 波打つ よう 広がる いく 酸 弱める ... 0 \n", "112 香り 控え目 特有 フレッシュ 甘味 ある する 口当たり ドライテイスト 余韻 する 酸 ... 0 \n", "118 目 とまる 一 白水 成 四 合 瓶 見る ラベル 左上 plus +」 ある 店員 さん ... 0 \n", "129 ひる 燻蒸 香 太い すぎる よい うまい さ 燗 する 燻蒸 クセ 和らぐ よう メロン ... 0 \n", "133 オリ 甘い さ 中 バナナ 感じる 甘味 中盤 活性 にごる 来る 炭酸 押し寄せる 強い ... 0 \n", "153 笑 四季 特別 米 Polynesia 生 アル 原酒 2 7 BY 熟れる 果実 濃厚 甘... 0 \n", "19 八海山 言う こと 味 薄い そう 思う いる 米 原酒 。 する 旨み コク ある これ ... 1 \n", ".. ... ... \n", "170 伊予 賀 儀 屋 純大 しずく 媛 4 5 生原 2 6 BY 控えめ 香り 甘い 穏やか ... 3 \n", "171 川鶴 特別 米 Wisdom 限定 生 原酒 山田 / オオセト ・ 5 0 5 5 2 8... 3 \n", "172 これ 当たり 旨味 濃い フルーティー 色 黄み かう 1 日 目 尖る 3 日 目 マイル... 3 \n", "173 私 1 2 年 前 プロデュース する 下田 富士 米 吟醸 富士 錦 洗心 飲む 比べる ... 3 \n", "174 出雲 市 契約 農家 さん 特別 栽培 山田 錦 100 % 使用 35 % 贅沢 磨く 上... 3 \n", "175 含む リンゴ パイナップル 感じる ドライ フルーツ よう 上品 果実 的 香味 バランス ... 3 \n", "176 かすか 杉 香り ある 酒 自体 冷 辛口 角 トンガッタ よう 感じ 痛い なる よう 味... 3 \n", "177 精米 歩合 60 % 米 酒 酸味 ある 辛口 うまい 。 温める うまみ 消える 酸味 残... 3 \n", "178 うまい すごい きれい 酒 食 中 うまい 美味しい 香り 穏やか 甘味 酸味 旨味 バラン... 3 \n", "179 豊 賀 米 吟醸 天女 しずく 中 取る 濾過 生 原酒 美山 錦 5 9 長野 酵母 2 ... 3 \n", "180 間違い ない 上品 酒 質 値段 安い たまらない 辛口 おとなしい 酒 味 辛口 クリア ... 3 \n", "181 何 よい の 菊 正 純 米 目 入る 買う みる 冷やす 書く ある 冷やす 飲む みる ... 3 \n", "182 甘み ある 有名 米 吟醸 みたい 女性 勧める 地元 広島 大手 スーパー 配置 する れ... 3 \n", "184 文句 ない 爽快 感 至高 廉価 (^∇^) 淡い 麗 辛口 香り 昔 ある 日本 酒 ( ... 3 \n", "185 竹 鶴 純 米 熱 燗冷まし 新た 飲む 方 教える くれる にごり酒 純 米 にごる 冷 ... 3 \n", "186 すう 私 蔵 訪問 する 吟 旨い さ 惚れ込む 販売 上 策 名 する その後 当時 2 ... 3 \n", "187 さわやか ラムネ 香 しつこい 甘味 酸味 する 炭酸 夏 酒 する ドライ 仕上がり ソフ... 3 \n", "188 最初 感じる の かすか 吟醸 香 中 甘い さ 味 濃い 酒 味 強い すぎる 寿司 一緒... 3 \n", "189 日本 酒 度 甘い ほう 夏みかん よう 香り のどごし いい 甘い 酒 飲む 進む くどい... 3 \n", "190 米 吟醸 頂く 米 吟醸 飲める 味 辛口 飲める すぎる ちゃう 気 する 値段 する 満足 3 \n", "192 含む 生らす いる フレッシュ 風味 綺麗 質感 中盤 余韻 甘酸っぱい 酸味 印象 的 酸... 3 \n", "193 原酒 香り 少ない 旨味 アルコール 感 強い 涼 冷え 常温 非常 旨い ここ 酒 近い ... 3 \n", "194 美山 錦 精米 歩合 65 %、 日本 酒 度 - 5 酸度 1 . 8 アルコール 15 ... 3 \n", "195 アルコール 度数 17 18 精米 歩合 50 % 発泡 炭酸 これ 華やか フレッシュ 吟... 3 \n", "196 雄 町 ひる ひかり オオセト 3 種類 米 作る 酒 責め ところ ブレンド 責め 責め ... 3 \n", "197 2 年 前 飲む 感動 する 日本 酒 香り メロン 味 重い 飲める いる かちよ 濃厚 ... 3 \n", "198 開 栓 直後 含む ピリリ ガス 感 原酒 濃厚 フレッシュ バター 甘味 口 中 一 杯 ... 3 \n", "199 今 日本 酒 苦手 敬遠 する 毎年 お世話 なる いる 山中湖 民宿 主人 すすめ 試す ... 3 \n", "86 松 司 純 米 吟醸 楽 しぼる たて 山田 錦 吟 吹雪 6 0 2 8 BY 僅か セメ... 4 \n", "5 今宵 息子 家 飲む こと 飛 露 喜 特別 純 米 セレクト 予想 する れる 飲む 飽き... 5 \n", "\n", "[200 rows x 10 columns]" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# マスタデータの JOIN\n", "master_df = pd.read_csv(MEIGARA_MASTER_PATH)\n", "result_df = master_df.merge(meigara_comments_df, on=\"meigara_id\", how=\"inner\")\n", "\n", "result_df[\"cluster\"] = y\n", "result_df.sort_values(by=[\"cluster\", \"rank\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最後に結果を出力します。" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [], "source": [ "target_cols = [\"rank\", \"meigara\", \"kuramoto\", \"detail_url\", \"cluster\"]\n", "result_df[target_cols].to_csv(CLUSTER_PATH, index=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このような流れで単語ベースのクラスタリングが行えます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7. おわりに\n", "\n", " 今回の解析はまだまだ不足している点があります。例えば以下のような点を考慮しませんでした。\n", " \n", " * 形態素解析用辞書の改善\n", " * デフォルトのままだと例えば「山田錦」が「山田」+「錦」に分割されてしまう。\n", " * 否定語の扱い\n", " * 美味しくない → 美味しい + ない と分割され、このままでは「美味しい」としてカウントされてしまう。\n", " * 数値を含んだ単語の取扱\n", " * 例えばアルコール度数を表すような数値や、精米歩合の数値などがうまく扱えていない。\n", " * ノイズとなるような単語のカット\n", " * 「Wow very oishii Sake ! 」などのテキストが含まれているが、このようなものが含まれていてもあまり有益な結果をえられないので、カットすべき。\n", " \n", "これらの点などを改善していくことにより、より我々の感覚と近い結果を得られるようになります。" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python [conda root]", "language": "python", "name": "conda-root-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" }, "nbpresent": { "slides": {}, "themes": {} } }, "nbformat": 4, "nbformat_minor": 1 }