{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# データの前処理\n", "* 本ハンズオンでは Personalize の使い方にフォーカスするため、データの前処理を事前に行っている\n", "* データソースからデータをダウンロードして、処理する方法をこのノートブックに記載する" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## データの準備\n", "### サンプルデータのダウンロード\n", "* ここではMovielensの10万件の評価データを利用する\n", "* このnotebookではインタラクション、ユーザー、アイテムの3種類のデータを利用する\n", "* まずはデータのダウンロードから\n", "* movielens の ml-100k 10万行のデータセットをダウンロードする" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!wget -N http://files.grouplens.org/datasets/movielens/ml-100k.zip\n", "!unzip -o ml-100k.zip" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## データ前処理方針\n", "* ml-100k の中から 3 種類のデータを作成する。\n", " * ユーザが映画に対して、どの時系列で、どんな評価を下したのか、というインタラクションデータ\n", " * 映画を閲覧したユーザがどのような属性(年齢、性別など)なのかをマスタとして持つユーザデータ\n", " * 各映画がどのジャンルに属するかをマスタとして持つアイテムデータ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### データの加工\n", "#### インタラクションデータの加工\n", "Personalizeのインタラクションスキーマ定義に合わせるため、EVENT_TYPE,EVENT_VALUEカラムを設定 \n", "ここではEVENT_TYPEにRATING、EVENT_VALUEにRATINGの値を設定している \n", "\n", "※Personalizeのレコメンデーションは基本的に特定のイベントの発生可能性が高い順でレコメンデーションのリストを返す形となっており \n", " サンプルデータを例とした場合、予想されるRATINGが高い順で結果リストを返すわけではない。 \n", " 例.ユーザーAが映画1を見たか見ていないか?が考慮され、RATINGが高かったどうかはレコメンデーションのリストに反映されない \n", " このため、ユーザーが良いRATINGをしそうな映画のみをレコメンデーションのリストに含めたい場合、 \n", " RATINGが良いレコード(例えば3.5以上)のみを学習データに含める必要がある\n", " また、ここではEVENT_TYPEを指定しているが、1種類しかイベントの種類が存在しない場合、EVENT_TYPEを使用しないことも可能\n", "  " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head -n5 ./ml-100k/u.data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# データの読み込み\n", "data = pd.read_csv('./ml-100k/u.data', sep='\\t', names=['USER_ID', 'ITEM_ID', 'RATING', 'TIMESTAMP'])\n", "pd.set_option('display.max_rows', 10)\n", "data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# スキーマ定義に合わせたデータ修正\n", "data['EVENT_TYPE'] = 'RATING'\n", "data = data.rename(columns={'RATING': 'EVENT_VALUE'})\n", "data = data.loc[:, [\"USER_ID\", \"ITEM_ID\", \"EVENT_TYPE\", \"EVENT_VALUE\", \"TIMESTAMP\"]]\n", "data.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 3.5以上のRATINGのデータのみに絞る\n", "data = data[data['EVENT_VALUE'] >= 3.5] \n", "\n", "# ここではトレーニング時間を短くするため、利用するデータ量を1万件に絞る場合はコメントアウトを外す\n", "data_sampled = data#.sample(n=10000)\n", "data_sampled.to_csv('data/interaction.csv', index=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head data/interaction.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### ユーザデータの加工\n", "csv形式で出力するのみ" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head -n5 ./ml-100k/u.user" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "user_data = pd.read_csv('./ml-100k/u.user', sep='|', names=['USER_ID', 'AGE', 'GENDER', 'JOB', 'ZIP'])\n", "user_data.to_csv('data/user.csv', index=False)\n", "user_data.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head data/user.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### アイテムデータの加工\n", "* ITEM_ID, GENRE の csv 形式にする必要がある\n", " * GENRE については複数の GENRE を保つ場合があるが(「子供向け」と「コメディ」など)、その場合は GENRE カラム内にパイプ(|)で区切って複数格納する\n", "* u.item ファイルに、アイテムID,アイテム名、属するジャンル の ID が格納されている\n", "* u.genre ファイルに、各ジャンルのIDとジャンル名のマスタデータが格納されている\n", "* 上記 2 ファイルをマージしてアイテムデータを作成する" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head -n5 ./ml-100k/u.item" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "item_data = pd.read_csv('./ml-100k/u.item', sep='|', encoding='latin-1', header=None)\n", "item_data = item_data.drop(3, axis=1)\n", "item_data.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!cat ml-100k/u.genre" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "genre_data = pd.read_csv('./ml-100k/u.genre', sep='|', encoding='latin-1', header=None)\n", "genre_data.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "column_names = genre_data[0].tolist()\n", "print(column_names)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "column_names.insert(0, 'ITEM_ID')\n", "column_names.insert(1, 'TITLE')\n", "column_names.insert(2, 'RELEASE')\n", "column_names.insert(3, 'IMDB_URL')\n", "print(column_names)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "item_data.columns = column_names\n", "item_data.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def process_genre(item_df):\n", "\n", " genre_list = []\n", " if(item_df[\"unknown\"] == 1):\n", " genre_list.append(\"unknown\")\n", " if(item_df[\"Action\"] == 1):\n", " genre_list.append(\"Action\")\n", " if(item_df[\"Adventure\"] == 1):\n", " genre_list.append(\"Adventure\")\n", " if(item_df[\"Animation\"] == 1):\n", " genre_list.append(\"Animation\")\n", " if(item_df[\"Children's\"] == 1):\n", " genre_list.append(\"Children's\")\n", " if(item_df[\"Comedy\"] == 1):\n", " genre_list.append(\"Comedy\")\n", " if(item_df[\"Crime\"] == 1):\n", " genre_list.append(\"Crime\")\n", " if(item_df[\"Documentary\"] == 1):\n", " genre_list.append(\"Documentary\")\n", " if(item_df[\"Drama\"] == 1):\n", " genre_list.append(\"Drama\")\n", " if(item_df[\"Fantasy\"] == 1):\n", " genre_list.append(\"Fantasy\")\n", " if(item_df[\"Film-Noir\"] == 1):\n", " genre_list.append(\"Film-Noir\")\n", " if(item_df[\"Horror\"] == 1):\n", " genre_list.append(\"Horror\")\n", " if(item_df[\"Musical\"] == 1):\n", " genre_list.append(\"Musical\")\n", " if(item_df[\"Mystery\"] == 1):\n", " genre_list.append(\"Mystery\")\n", " if(item_df[\"Romance\"] == 1):\n", " genre_list.append(\"Romance\")\n", " if(item_df[\"Sci-Fi\"] == 1):\n", " genre_list.append(\"Sci-Fi\")\n", " if(item_df[\"Thriller\"] == 1):\n", " genre_list.append(\"Thriller\")\n", " if(item_df[\"War\"] == 1):\n", " genre_list.append(\"War\")\n", " if(item_df[\"Western\"] == 1):\n", " genre_list.append(\"Western\")\n", " \n", " return '|'.join(genre_list)\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "item_data[\"GENRE\"] = item_data.apply( process_genre, axis=1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "item_data_csv = item_data[['ITEM_ID', 'GENRE']]\n", "item_data_csv.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "item_data_csv.to_csv('data/item.csv', index=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!head data/item.csv" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "conda_python3", "language": "python", "name": "conda_python3" }, "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.6.13" } }, "nbformat": 4, "nbformat_minor": 4 }