diff --git "a/WorkSpace/\355\206\265\355\225\251/EDA_ML \354\231\204\353\243\214.ipynb" "b/WorkSpace/\355\206\265\355\225\251/EDA_ML \354\231\204\353\243\214.ipynb" index a04583d..a3a441b 100644 --- "a/WorkSpace/\355\206\265\355\225\251/EDA_ML \354\231\204\353\243\214.ipynb" +++ "b/WorkSpace/\355\206\265\355\225\251/EDA_ML \354\231\204\353\243\214.ipynb" @@ -8,14 +8,38 @@ "# 데이터 불러오기" ] }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4b3dfdb4", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T10:13:14.504497Z", + "start_time": "2022-10-27T10:13:13.560520Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import rc\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "import pickle\n", + "\n", + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, { "cell_type": "code", "execution_count": 2, "id": "e0dee546", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T06:59:46.358142Z", - "start_time": "2022-10-27T06:59:37.492273Z" + "end_time": "2022-10-27T10:13:18.625393Z", + "start_time": "2022-10-27T10:13:14.505555Z" } }, "outputs": [ @@ -1007,18 +1031,6 @@ } ], "source": [ - "from matplotlib import rc\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "import pandas as pd\n", - "import numpy as np\n", - "import os\n", - "import pickle\n", - "\n", - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "\n", - "\n", "rc('font', family='AppleGothic')\n", "plt.rcParams['axes.unicode_minus'] = False\n", "\n", @@ -1066,8 +1078,8 @@ "id": "37a98d33", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T06:59:48.618092Z", - "start_time": "2022-10-27T06:59:46.360006Z" + "end_time": "2022-10-27T10:13:20.126892Z", + "start_time": "2022-10-27T10:13:18.626291Z" } }, "outputs": [ @@ -1423,8 +1435,8 @@ "id": "fd78798f", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T06:59:48.621577Z", - "start_time": "2022-10-27T06:59:48.619269Z" + "end_time": "2022-10-27T10:13:20.130211Z", + "start_time": "2022-10-27T10:13:20.128217Z" } }, "outputs": [], @@ -1451,8 +1463,8 @@ "id": "9d6bf280", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:00:46.471155Z", - "start_time": "2022-10-27T06:59:48.623458Z" + "end_time": "2022-10-27T10:13:47.968725Z", + "start_time": "2022-10-27T10:13:20.130867Z" } }, "outputs": [ @@ -1957,8 +1969,8 @@ "id": "027530b5", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:00:46.828951Z", - "start_time": "2022-10-27T07:00:46.472994Z" + "end_time": "2022-10-27T10:13:48.243666Z", + "start_time": "2022-10-27T10:13:47.969661Z" } }, "outputs": [], @@ -1993,8 +2005,8 @@ "id": "21a2d543", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:00:46.833439Z", - "start_time": "2022-10-27T07:00:46.830208Z" + "end_time": "2022-10-27T10:13:48.247105Z", + "start_time": "2022-10-27T10:13:48.244602Z" } }, "outputs": [], @@ -2028,8 +2040,8 @@ "id": "a9214a20", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:01:29.224418Z", - "start_time": "2022-10-27T07:00:46.834731Z" + "end_time": "2022-10-27T10:14:21.057838Z", + "start_time": "2022-10-27T10:13:48.248174Z" } }, "outputs": [ @@ -2423,19 +2435,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "[16:00:54] WARNING: ../src/learner.cc:1115: Starting in XGBoost 1.3.0, the default evaluation metric used with the objective 'binary:logistic' was changed from 'error' to 'logloss'. Explicitly set eval_metric if you'd like to restore the old behavior.\n", "오차 행렬\n", - "[[7852 2250]\n", - " [3089 6192]]\n", - "정확도: 0.7246, 정밀도: 0.7335, 재현율: 0.6672, F1: 0.6988, AUC:0.8072\n", + "[[7900 2202]\n", + " [2972 6309]]\n", + "정확도: 0.7331, 정밀도: 0.7413, 재현율: 0.6798, F1: 0.7092, AUC:0.8169\n", "오차 행렬\n", - "[[6190 3912]\n", - " [3988 5293]]\n", - "정확도: 0.5924, 정밀도: 0.5750, 재현율: 0.5703, F1: 0.5726, AUC:0.5915\n", + "[[6208 3894]\n", + " [4014 5267]]\n", + "정확도: 0.5920, 정밀도: 0.5749, 재현율: 0.5675, F1: 0.5712, AUC:0.5910\n", "오차 행렬\n", - "[[7473 2629]\n", - " [3855 5426]]\n", - "정확도: 0.6655, 정밀도: 0.6736, 재현율: 0.5846, F1: 0.6260, AUC:0.7354\n", + "[[7479 2623]\n", + " [3832 5449]]\n", + "정확도: 0.6670, 정밀도: 0.6750, 재현율: 0.5871, F1: 0.6280, AUC:0.7363\n", "오차 행렬\n", "[[7498 2604]\n", " [3187 6094]]\n", @@ -2524,8 +2535,8 @@ "id": "071b8317", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:01:29.247956Z", - "start_time": "2022-10-27T07:01:29.226073Z" + "end_time": "2022-10-27T10:14:21.073053Z", + "start_time": "2022-10-27T10:14:21.058792Z" }, "scrolled": false }, @@ -2932,183 +2943,20 @@ }, { "cell_type": "markdown", - "id": "6b8242b3", - "metadata": {}, - "source": [ - "## 고객별 대중분류명별 분기별 총매출" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "de520817", - "metadata": { - "ExecuteTime": { - "end_time": "2022-10-27T08:27:12.889150Z", - "start_time": "2022-10-27T08:27:12.863878Z" - } - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'pd' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [2]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# 추천 시스템용 데이터(recommend)\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m reco \u001b[38;5;241m=\u001b[39m \u001b[43mpd\u001b[49m\u001b[38;5;241m.\u001b[39mmerge(pp_demo, pp_datasets, on\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m, how\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mleft\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 3\u001b[0m reco_cust \u001b[38;5;241m=\u001b[39m reco[pred_lr\u001b[38;5;241m==\u001b[39m\u001b[38;5;241m1\u001b[39m]\n\u001b[1;32m 4\u001b[0m reco_cust_num \u001b[38;5;241m=\u001b[39m reco_cust[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mvalues\n", - "\u001b[0;31mNameError\u001b[0m: name 'pd' is not defined" - ] - } - ], - "source": [ - "# 추천 시스템용 데이터(recommend)\n", - "reco = pd.merge(pp_demo, pp_datasets, on='고객번호', how='left')\n", - "reco_cust = reco[pred_lr==1]\n", - "reco_cust_num = reco_cust['고객번호'].values\n", - "reco_cust_num" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "1ff25b70", - "metadata": { - "ExecuteTime": { - "end_time": "2022-10-27T08:27:13.040659Z", - "start_time": "2022-10-27T08:27:13.013587Z" - }, - "scrolled": false - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'pp_purprd_prodcl' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m reco_cust_pv \u001b[38;5;241m=\u001b[39m \u001b[43mpp_purprd_prodcl\u001b[49m\u001b[38;5;241m.\u001b[39mpivot_table(index\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m,\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m중분류명\u001b[39m\u001b[38;5;124m'\u001b[39m],columns\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m분기\u001b[39m\u001b[38;5;124m'\u001b[39m,values\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m구매금액\u001b[39m\u001b[38;5;124m'\u001b[39m,aggfunc\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124msum\u001b[39m\u001b[38;5;124m'\u001b[39m)\u001b[38;5;241m.\u001b[39mreset_index()\n\u001b[1;32m 2\u001b[0m reco_cust_pv \u001b[38;5;241m=\u001b[39m reco_cust_pv[reco_cust_pv[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39misin(reco_cust_num)]\n\u001b[1;32m 3\u001b[0m reco_cust_pv \u001b[38;5;241m=\u001b[39m reco_cust_pv\u001b[38;5;241m.\u001b[39mfillna(\u001b[38;5;241m0\u001b[39m)\n", - "\u001b[0;31mNameError\u001b[0m: name 'pp_purprd_prodcl' is not defined" - ] - } - ], - "source": [ - "reco_cust_pv = pp_purprd_prodcl.pivot_table(index=['고객번호','중분류명'],columns='분기',values='구매금액',aggfunc='sum').reset_index()\n", - "reco_cust_pv = reco_cust_pv[reco_cust_pv['고객번호'].isin(reco_cust_num)]\n", - "reco_cust_pv = reco_cust_pv.fillna(0)\n", - "reco_cust_pv = reco_cust_pv.iloc[:,:-1]\n", - "reco_cust_pv" - ] - }, - { - "cell_type": "markdown", - "id": "ea0a12f0", - "metadata": {}, - "source": [ - " ## 이전 7분기의 고객별 대중분류별 총매출의 추세" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "c56b4a44", - "metadata": { - "ExecuteTime": { - "end_time": "2022-10-27T08:27:13.313553Z", - "start_time": "2022-10-27T08:27:13.285571Z" - }, - "scrolled": false - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'reco_cust_pv' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m grad_li \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;28mlen\u001b[39m(\u001b[43mreco_cust_pv\u001b[49m)):\n\u001b[1;32m 4\u001b[0m z\u001b[38;5;241m=\u001b[39mnp\u001b[38;5;241m.\u001b[39mpolyfit(np\u001b[38;5;241m.\u001b[39marray([\u001b[38;5;241m1.\u001b[39m,\u001b[38;5;241m2.\u001b[39m,\u001b[38;5;241m3.\u001b[39m,\u001b[38;5;241m4.\u001b[39m,\u001b[38;5;241m5.\u001b[39m,\u001b[38;5;241m6.\u001b[39m,\u001b[38;5;241m7.\u001b[39m])\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m100\u001b[39m,reco_cust_pv\u001b[38;5;241m.\u001b[39miloc[i,\u001b[38;5;241m2\u001b[39m:]\u001b[38;5;241m.\u001b[39mvalues\u001b[38;5;241m.\u001b[39mastype(\u001b[38;5;28mfloat\u001b[39m),\u001b[38;5;241m1\u001b[39m)[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 5\u001b[0m grad_li\u001b[38;5;241m.\u001b[39mappend(z)\n", - "\u001b[0;31mNameError\u001b[0m: name 'reco_cust_pv' is not defined" - ] - } - ], - "source": [ - "grad_li = []\n", - "for i in range(len(reco_cust_pv)):\n", - " \n", - " z=np.polyfit(np.array([1.,2.,3.,4.,5.,6.,7.])*100,reco_cust_pv.iloc[i,2:].values.astype(float),1)[0]\n", - " grad_li.append(z)\n", - "# len(grad_li)\n", - "reco_cust_pv['gradient'] = np.array([grad_li]).reshape(-1,1)\n", - "reco_cust_pv" - ] - }, - { - "cell_type": "markdown", - "id": "5230972d", - "metadata": {}, - "source": [ - "## 고객 맞춤 추천을 위한 고객 특성 생성 사용자 함수" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "fe417e0b", - "metadata": { - "ExecuteTime": { - "end_time": "2022-10-27T08:27:05.505923Z", - "start_time": "2022-10-27T08:27:05.127974Z" - } - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'reco_cust_pv' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 11\u001b[0m recom_dict[i] \u001b[38;5;241m=\u001b[39m li\n\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m recom_dict\n\u001b[0;32m---> 14\u001b[0m \u001b[43mcust_feat\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", - "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36mcust_feat\u001b[0;34m(i)\u001b[0m\n\u001b[1;32m 3\u001b[0m recom_dict \u001b[38;5;241m=\u001b[39m {}\n\u001b[1;32m 4\u001b[0m li \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m----> 5\u001b[0m reco_group\u001b[38;5;241m=\u001b[39m\u001b[43mreco_cust_pv\u001b[49m[[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m,\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m중분류명\u001b[39m\u001b[38;5;124m'\u001b[39m,\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mgradient\u001b[39m\u001b[38;5;124m'\u001b[39m]]\u001b[38;5;241m.\u001b[39mgroupby([\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m고객번호\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[1;32m 6\u001b[0m 중분류 \u001b[38;5;241m=\u001b[39m reco_group\u001b[38;5;241m.\u001b[39mget_group((i))\u001b[38;5;241m.\u001b[39msort_values(by\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mgradient\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m중분류명\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39miloc[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 7\u001b[0m li\u001b[38;5;241m.\u001b[39mappend(중분류)\n", - "\u001b[0;31mNameError\u001b[0m: name 'reco_cust_pv' is not defined" - ] - } - ], - "source": [ - "# 고객의 가장 매출이 감소한 중분류 와 고객 특성 반환 함수 \n", - "def cust_feat(i):\n", - " recom_dict = {}\n", - " li = []\n", - " reco_group=reco_cust_pv[['고객번호','중분류명','gradient']].groupby(['고객번호'])\n", - " 중분류 = reco_group.get_group((i)).sort_values(by='gradient')['중분류명'].iloc[0]\n", - " li.append(중분류)\n", - " cust = pp_demo[pp_demo['고객번호']==i].values\n", - " cust_info = (' ').join(cust[:,1:][0])\n", - " li.append(중분류+' '+cust_info)\n", - " recom_dict[i] = li\n", - " return recom_dict\n", - "\n", - "cust_feat(1)" - ] - }, - { - "cell_type": "markdown", - "id": "46a76a9a", + "id": "a09ca62b", "metadata": {}, "source": [ - "## 추천을 위한 소분류명별 종합 특성 컬럼 생성" + "## 추천을 위한 소분류명별 종합 특성 DataFrame 생성" ] }, { "cell_type": "code", - "execution_count": 15, - "id": "c72e0f3f", + "execution_count": 29, + "id": "fc51698a", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:04:30.733037Z", - "start_time": "2022-10-27T07:03:49.209165Z" + "end_time": "2022-10-27T10:43:46.914340Z", + "start_time": "2022-10-27T10:43:21.543655Z" }, "scrolled": false }, @@ -3470,7 +3318,7 @@ "[28437054 rows x 21 columns]" ] }, - "execution_count": 15, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -3482,14 +3330,14 @@ }, { "cell_type": "code", - "execution_count": 42, - "id": "63fe3ed0", + "execution_count": 76, + "id": "7940a745", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:45:17.232544Z", - "start_time": "2022-10-27T07:44:59.002954Z" + "end_time": "2022-10-27T11:44:49.886400Z", + "start_time": "2022-10-27T11:44:38.227980Z" }, - "scrolled": true + "scrolled": false }, "outputs": [ { @@ -3517,7 +3365,6 @@ " 성별\n", " 연령대\n", " 거주지역\n", - " 제휴사\n", " 중분류명\n", " \n", " \n", @@ -3526,45 +3373,40 @@ " 0\n", " 유제품\n", " M\n", - " 60세이상\n", + " 60\n", " 서울특별시 강남구\n", - " A\n", " 축산가공\n", " \n", " \n", " 1\n", " 유제품\n", " M\n", - " 60세이상\n", + " 60\n", " 경기도\n", - " A\n", " 축산가공\n", " \n", " \n", " 2\n", " 유제품\n", " F\n", - " 60세이상\n", + " 60\n", " 서울특별시 노원구\n", - " A\n", " 축산가공\n", " \n", " \n", " 3\n", " 유제품\n", " F\n", - " 60세이상\n", + " 60\n", " 강원도\n", - " A\n", " 축산가공\n", " \n", " \n", " 4\n", " 유제품\n", " F\n", - " 60세이상\n", + " 60\n", " 서울특별시 서대문구\n", - " A\n", " 축산가공\n", " \n", " \n", @@ -3574,99 +3416,150 @@ " ...\n", " ...\n", " ...\n", - " ...\n", " \n", " \n", - " 845166\n", + " 833889\n", " 기타탁구용품\n", " F\n", - " 40세~44세\n", + " 40\n", " 전라남도\n", - " C\n", " 레저취미\n", " \n", " \n", - " 845167\n", + " 833890\n", " 여성발가락\n", " F\n", - " 50세~54세\n", + " 50\n", " 경기도\n", - " B\n", " 여성양말\n", " \n", " \n", - " 845168\n", + " 833891\n", " 페스츄리류\n", " M\n", - " 50세~54세\n", + " 50\n", " 경기도\n", - " C\n", " 베이커리\n", " \n", " \n", - " 845169\n", + " 833892\n", " 기타한방약재\n", " F\n", - " 50세~54세\n", + " 50\n", " 전라북도\n", - " C\n", " 근채류\n", " \n", " \n", - " 845170\n", + " 833893\n", " 컵아이스크림\n", " F\n", - " 25세~29세\n", + " 25\n", " 인천광역시\n", - " D\n", " 과자\n", " \n", " \n", "\n", - "

845171 rows × 6 columns

\n", + "

833894 rows × 5 columns

\n", "" ], "text/plain": [ - " 소분류명 성별 연령대 거주지역 제휴사 중분류명\n", - "0 유제품 M 60세이상 서울특별시 강남구 A 축산가공\n", - "1 유제품 M 60세이상 경기도 A 축산가공\n", - "2 유제품 F 60세이상 서울특별시 노원구 A 축산가공\n", - "3 유제품 F 60세이상 강원도 A 축산가공\n", - "4 유제품 F 60세이상 서울특별시 서대문구 A 축산가공\n", - "... ... .. ... ... .. ...\n", - "845166 기타탁구용품 F 40세~44세 전라남도 C 레저취미\n", - "845167 여성발가락 F 50세~54세 경기도 B 여성양말\n", - "845168 페스츄리류 M 50세~54세 경기도 C 베이커리\n", - "845169 기타한방약재 F 50세~54세 전라북도 C 근채류\n", - "845170 컵아이스크림 F 25세~29세 인천광역시 D 과자\n", - "\n", - "[845171 rows x 6 columns]" + " 소분류명 성별 연령대 거주지역 중분류명\n", + "0 유제품 M 60 서울특별시 강남구 축산가공\n", + "1 유제품 M 60 경기도 축산가공\n", + "2 유제품 F 60 서울특별시 노원구 축산가공\n", + "3 유제품 F 60 강원도 축산가공\n", + "4 유제품 F 60 서울특별시 서대문구 축산가공\n", + "... ... .. .. ... ...\n", + "833889 기타탁구용품 F 40 전라남도 레저취미\n", + "833890 여성발가락 F 50 경기도 여성양말\n", + "833891 페스츄리류 M 50 경기도 베이커리\n", + "833892 기타한방약재 F 50 전라북도 근채류\n", + "833893 컵아이스크림 F 25 인천광역시 과자\n", + "\n", + "[833894 rows x 5 columns]" ] }, - "execution_count": 42, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pp_reco_df = pp_reco[pp_reco['분기']!='15_4']\n", - "pp_reco_df = pp_reco_df[['소분류명', '고객번호', '성별', '연령대', '거주지역','제휴사', '중분류명']] # 25006807\n", + "pp_reco_df = pp_reco_df[['소분류명', '고객번호', '성별', '연령대', '거주지역','중분류명']] # 25006807\n", "pp_reco_df = pp_reco_df.drop_duplicates(['소분류명', '고객번호'], keep='first').reset_index(drop=True)\n", "pp_reco_df = pp_reco_df.drop(columns=['고객번호'])\n", - "pp_reco_df = pp_reco_df.drop_duplicates(['소분류명', '성별', '연령대', '거주지역', '제휴사', '중분류명'], keep='first').reset_index(drop=True)\n", + "pp_reco_df = pp_reco_df.drop_duplicates(['소분류명', '성별', '연령대', '거주지역', '중분류명'], keep='first').reset_index(drop=True)\n", + "pp_reco_df['연령대'] = pp_reco_df['연령대'].apply(lambda x:x[:2])\n", "# pp_reco_df = pp_reco_df[(pp_reco_df['소분류명']=='유제품') & (pp_reco_df['성별']=='M')]\n", "pp_reco_df" ] }, { "cell_type": "code", - "execution_count": 43, - "id": "53979309", + "execution_count": 77, + "id": "c73d40d8", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:44:55.704008Z", + "start_time": "2022-10-27T11:44:55.693838Z" + } + }, + "outputs": [], + "source": [ + "# pp_reco_df = pp_reco_df.drop(columns='reco_feature')\n", + "# pp_reco_df['reco_feature'] = pp_reco_df.iloc[:,1:].apply(lambda row : (' ').join(row.values), axis=1)\n", + "# pp_reco_df" + ] + }, + { + "cell_type": "markdown", + "id": "6b8242b3", + "metadata": {}, + "source": [ + "## 고객별 소분류별 구매 횟수" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "de520817", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T07:45:21.061346Z", - "start_time": "2022-10-27T07:45:17.235142Z" + "end_time": "2022-10-27T09:54:01.717148Z", + "start_time": "2022-10-27T09:54:01.701756Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 3, ..., 19346, 19360, 19364])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } + ], + "source": [ + "# 추천 시스템용 데이터(recommend)\n", + "reco = pd.merge(pp_demo, pp_datasets, on='고객번호', how='left')\n", + "reco_cust = reco[pred_lr==1]\n", + "reco_cust_num = reco_cust['고객번호'].values\n", + "reco_cust_num" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "9c74c2b5", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T10:31:24.242593Z", + "start_time": "2022-10-27T10:31:19.606221Z" + }, + "scrolled": true }, "outputs": [ { @@ -3690,183 +3583,774 @@ " \n", " \n", " \n", + " 고객번호\n", " 소분류명\n", - " 성별\n", - " 연령대\n", - " 거주지역\n", - " 제휴사\n", - " 중분류명\n", - " reco_feature\n", + " count\n", " \n", " \n", " \n", " \n", " 0\n", - " 유제품\n", - " M\n", - " 60세이상\n", - " 서울특별시 강남구\n", - " A\n", - " 축산가공\n", - " M 60세이상 서울특별시 강남구 A 축산가공\n", + " 1\n", + " Bag&Bag\n", + " 1.0\n", " \n", " \n", " 1\n", - " 유제품\n", - " M\n", - " 60세이상\n", - " 경기도\n", - " A\n", - " 축산가공\n", - " M 60세이상 경기도 A 축산가공\n", + " 1\n", + " L.B\n", + " 3.0\n", " \n", " \n", " 2\n", - " 유제품\n", - " F\n", - " 60세이상\n", - " 서울특별시 노원구\n", - " A\n", - " 축산가공\n", - " F 60세이상 서울특별시 노원구 A 축산가공\n", + " 1\n", + " L/C 아웃도어\n", + " 1.0\n", " \n", " \n", " 3\n", - " 유제품\n", - " F\n", - " 60세이상\n", - " 강원도\n", - " A\n", - " 축산가공\n", - " F 60세이상 강원도 A 축산가공\n", + " 1\n", + " MP3 外\n", + " 2.0\n", " \n", " \n", " 4\n", - " 유제품\n", - " F\n", - " 60세이상\n", - " 서울특별시 서대문구\n", - " A\n", - " 축산가공\n", - " F 60세이상 서울특별시 서대문구 A 축산가공\n", + " 1\n", + " N.B\n", + " 3.0\n", " \n", " \n", " ...\n", " ...\n", " ...\n", " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", " \n", " \n", - " 845166\n", - " 기타탁구용품\n", - " F\n", - " 40세~44세\n", - " 전라남도\n", - " C\n", - " 레저취미\n", - " F 40세~44세 전라남도 C 레저취미\n", + " 6117702\n", + " 19383\n", + " 하드캔디\n", + " 10.0\n", " \n", " \n", - " 845167\n", - " 여성발가락\n", - " F\n", - " 50세~54세\n", - " 경기도\n", - " B\n", + " 6117703\n", + " 19383\n", + " 핸드로션/크림\n", + " 2.0\n", + " \n", + " \n", + " 6117704\n", + " 19383\n", + " 핸드워시/손세정제\n", + " 2.0\n", + " \n", + " \n", + " 6117705\n", + " 19383\n", + " 헤어에센스\n", + " 3.0\n", + " \n", + " \n", + " 6117706\n", + " 19383\n", + " 혼합탄산\n", + " 3.0\n", + " \n", + " \n", + "\n", + "

6117707 rows × 3 columns

\n", + "" + ], + "text/plain": [ + " 고객번호 소분류명 count\n", + "0 1 Bag&Bag 1.0\n", + "1 1 L.B 3.0\n", + "2 1 L/C 아웃도어 1.0\n", + "3 1 MP3 外 2.0\n", + "4 1 N.B 3.0\n", + "... ... ... ...\n", + "6117702 19383 하드캔디 10.0\n", + "6117703 19383 핸드로션/크림 2.0\n", + "6117704 19383 핸드워시/손세정제 2.0\n", + "6117705 19383 헤어에센스 3.0\n", + "6117706 19383 혼합탄산 3.0\n", + "\n", + "[6117707 rows x 3 columns]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reco_cust_pv = pp_purprd_prodcl.pivot_table(index=['소분류명'],columns='고객번호',values='구매금액',aggfunc='count')\n", + "reco_cust_pv = reco_cust_pv.unstack().dropna().reset_index()\n", + "reco_cust_pv = reco_cust_pv.rename(columns={0:'count'})\n", + "reco_cust_pv" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "99b518ac", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:31:21.545514Z", + "start_time": "2022-10-27T11:31:21.202680Z" + } + }, + "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", + "
고객번호count
count6.117707e+066.117707e+06
mean9.717193e+034.648319e+00
std5.464717e+031.018067e+01
min1.000000e+001.000000e+00
25%5.023000e+031.000000e+00
50%9.781000e+032.000000e+00
75%1.438400e+044.000000e+00
max1.938300e+047.060000e+02
\n", + "
" + ], + "text/plain": [ + " 고객번호 count\n", + "count 6.117707e+06 6.117707e+06\n", + "mean 9.717193e+03 4.648319e+00\n", + "std 5.464717e+03 1.018067e+01\n", + "min 1.000000e+00 1.000000e+00\n", + "25% 5.023000e+03 1.000000e+00\n", + "50% 9.781000e+03 2.000000e+00\n", + "75% 1.438400e+04 4.000000e+00\n", + "max 1.938300e+04 7.060000e+02" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reco_cust_pv.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "7e370cf9", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:31:44.149035Z", + "start_time": "2022-10-27T11:31:43.370043Z" + }, + "scrolled": true + }, + "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", + "
고객번호소분류명count
01Bag&Bag0.007082
11L.B0.021246
21L/C 아웃도어0.007082
31MP3 外0.014164
41N.B0.021246
............
611770219383하드캔디0.070822
611770319383핸드로션/크림0.014164
611770419383핸드워시/손세정제0.014164
611770519383헤어에센스0.021246
611770619383혼합탄산0.021246
\n", + "

6117707 rows × 3 columns

\n", + "
" + ], + "text/plain": [ + " 고객번호 소분류명 count\n", + "0 1 Bag&Bag 0.007082\n", + "1 1 L.B 0.021246\n", + "2 1 L/C 아웃도어 0.007082\n", + "3 1 MP3 外 0.014164\n", + "4 1 N.B 0.021246\n", + "... ... ... ...\n", + "6117702 19383 하드캔디 0.070822\n", + "6117703 19383 핸드로션/크림 0.014164\n", + "6117704 19383 핸드워시/손세정제 0.014164\n", + "6117705 19383 헤어에센스 0.021246\n", + "6117706 19383 혼합탄산 0.021246\n", + "\n", + "[6117707 rows x 3 columns]" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reco_cust_pv['count'] = reco_cust_pv['count'].apply(lambda x: 5*(x/706))\n", + "reco_cust_pv" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "9ec28428", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:31:56.781796Z", + "start_time": "2022-10-27T11:31:56.498635Z" + } + }, + "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", + "
고객번호count
count6.117707e+066.117707e+06
mean9.717193e+033.292011e-02
std5.464717e+037.210105e-02
min1.000000e+007.082153e-03
25%5.023000e+037.082153e-03
50%9.781000e+031.416431e-02
75%1.438400e+042.832861e-02
max1.938300e+045.000000e+00
\n", + "
" + ], + "text/plain": [ + " 고객번호 count\n", + "count 6.117707e+06 6.117707e+06\n", + "mean 9.717193e+03 3.292011e-02\n", + "std 5.464717e+03 7.210105e-02\n", + "min 1.000000e+00 7.082153e-03\n", + "25% 5.023000e+03 7.082153e-03\n", + "50% 9.781000e+03 1.416431e-02\n", + "75% 1.438400e+04 2.832861e-02\n", + "max 1.938300e+04 5.000000e+00" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reco_cust_pv.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "4dc04104", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:48:50.683544Z", + "start_time": "2022-10-27T11:48:48.873756Z" + } + }, + "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", "
소분류명성별연령대거주지역중분류명reco_feature
0유제품M60서울특별시 강남구축산가공M 60 서울특별시 강남구 축산가공
1유제품M60경기도축산가공M 60 경기도 축산가공
2유제품F60서울특별시 노원구축산가공F 60 서울특별시 노원구 축산가공
3유제품F60강원도축산가공F 60 강원도 축산가공
4유제품F60서울특별시 서대문구축산가공F 60 서울특별시 서대문구 축산가공
.....................
833889기타탁구용품F40전라남도레저취미F 40 전라남도 레저취미
833890여성발가락F50경기도여성양말F 50세~54세 경기도 B 여성양말F 50 경기도 여성양말
845168833891페스츄리류M50세~54세50경기도C베이커리M 50세~54세 경기도 C 베이커리M 50 경기도 베이커리
845169833892기타한방약재F50세~54세50전라북도C근채류F 50세~54세 전라북도 C 근채류F 50 전라북도 근채류
845170833893컵아이스크림F25세~29세25인천광역시D과자F 25세~29세 인천광역시 D 과자F 25 인천광역시 과자
\n", - "

845171 rows × 7 columns

\n", + "

833894 rows × 6 columns

\n", "
" ], "text/plain": [ - " 소분류명 성별 연령대 거주지역 제휴사 중분류명 reco_feature\n", - "0 유제품 M 60세이상 서울특별시 강남구 A 축산가공 M 60세이상 서울특별시 강남구 A 축산가공\n", - "1 유제품 M 60세이상 경기도 A 축산가공 M 60세이상 경기도 A 축산가공\n", - "2 유제품 F 60세이상 서울특별시 노원구 A 축산가공 F 60세이상 서울특별시 노원구 A 축산가공\n", - "3 유제품 F 60세이상 강원도 A 축산가공 F 60세이상 강원도 A 축산가공\n", - "4 유제품 F 60세이상 서울특별시 서대문구 A 축산가공 F 60세이상 서울특별시 서대문구 A 축산가공\n", - "... ... .. ... ... .. ... ...\n", - "845166 기타탁구용품 F 40세~44세 전라남도 C 레저취미 F 40세~44세 전라남도 C 레저취미\n", - "845167 여성발가락 F 50세~54세 경기도 B 여성양말 F 50세~54세 경기도 B 여성양말\n", - "845168 페스츄리류 M 50세~54세 경기도 C 베이커리 M 50세~54세 경기도 C 베이커리\n", - "845169 기타한방약재 F 50세~54세 전라북도 C 근채류 F 50세~54세 전라북도 C 근채류\n", - "845170 컵아이스크림 F 25세~29세 인천광역시 D 과자 F 25세~29세 인천광역시 D 과자\n", - "\n", - "[845171 rows x 7 columns]" + " 소분류명 성별 연령대 거주지역 중분류명 reco_feature\n", + "0 유제품 M 60 서울특별시 강남구 축산가공 M 60 서울특별시 강남구 축산가공\n", + "1 유제품 M 60 경기도 축산가공 M 60 경기도 축산가공\n", + "2 유제품 F 60 서울특별시 노원구 축산가공 F 60 서울특별시 노원구 축산가공\n", + "3 유제품 F 60 강원도 축산가공 F 60 강원도 축산가공\n", + "4 유제품 F 60 서울특별시 서대문구 축산가공 F 60 서울특별시 서대문구 축산가공\n", + "... ... .. .. ... ... ...\n", + "833889 기타탁구용품 F 40 전라남도 레저취미 F 40 전라남도 레저취미\n", + "833890 여성발가락 F 50 경기도 여성양말 F 50 경기도 여성양말\n", + "833891 페스츄리류 M 50 경기도 베이커리 M 50 경기도 베이커리\n", + "833892 기타한방약재 F 50 전라북도 근채류 F 50 전라북도 근채류\n", + "833893 컵아이스크림 F 25 인천광역시 과자 F 25 인천광역시 과자\n", + "\n", + "[833894 rows x 6 columns]" ] }, - "execution_count": 43, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# pp_reco_df = pp_reco_df.drop(columns='reco_feature')\n", - "pp_reco_df['reco_feature'] = pp_reco_df.iloc[:,1:].apply(lambda row : (' ').join(row.values), axis=1)\n", - "pp_reco_df" + "# pp_reco_df = pp_reco_df.drop(columns='reco_feature')\n", + "pp_reco_df['reco_feature'] = pp_reco_df.iloc[:,1:].apply(lambda row : (' ').join(row.values), axis=1)\n", + "pp_reco_df" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "92f160d8", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:39:24.972715Z", + "start_time": "2022-10-27T11:36:54.545128Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluating RMSE, MAE of algorithm SVD on 2 split(s).\n", + "\n", + " Fold 1 Fold 2 Mean Std \n", + "RMSE (testset) 0.0667 0.0666 0.0666 0.0001 \n", + "MAE (testset) 0.0283 0.0284 0.0283 0.0000 \n", + "Fit time 22.38 23.84 23.11 0.73 \n", + "Test time 40.04 45.88 42.96 2.92 \n" + ] + }, + { + "data": { + "text/plain": [ + "{'test_rmse': array([0.06670089, 0.06657903]),\n", + " 'test_mae': array([0.02831861, 0.02836099]),\n", + " 'fit_time': (22.381165981292725, 23.844670057296753),\n", + " 'test_time': (40.044742822647095, 45.880324840545654)}" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from surprise.model_selection import cross_validate\n", + "from surprise import Reader, Dataset\n", + "from surprise import SVD\n", + "from surprise import Dataset\n", + "from surprise import accuracy\n", + "from surprise.model_selection import train_test_split\n", + "\n", + "# 판다스 DataFrame에서 Surprise 데이터 세트로 데이터 로딩\n", + "reader = Reader(rating_scale=(0, 5.0))\n", + "data = Dataset.load_from_df(reco_cust_pv, reader)\n", + "\n", + "algo = SVD(random_state=0)\n", + "cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=2, verbose=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "44d768b1", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:41:32.974835Z", + "start_time": "2022-10-27T11:39:24.974366Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.06699629358085352\n", + "{'n_epochs': 10, 'n_factors': 50}\n" + ] + } + ], + "source": [ + "from surprise.model_selection import GridSearchCV\n", + "\n", + "# 최적화할 파라미터를 딕셔너리 형태로 지정\n", + "param_grid = {'n_epochs':[10], 'n_factors':[50]}# n_epochs : 점진적 하강 방식의 반복 횟수, n_factors : 잠재요인 크기 K mxn = mxp + nxp K 는 p의 크기 \n", + "\n", + "# CV를 3개 폴드 세트로 지정, 성능 평가는 rmse, mse로 수행하도록 GridSearchCV 구성\n", + "gs = GridSearchCV(SVD, param_grid, measures=['rmse', 'mae'], cv=2)\n", + "gs.fit(data)\n", + "\n", + "# 최고 RMSE Evaluation 점수와 그때의 하이퍼 파라미터\n", + "print(gs.best_score['rmse'])\n", + "print(gs.best_params['rmse'])" ] }, { - "cell_type": "markdown", - "id": "f3ef7e54", - "metadata": {}, + "cell_type": "code", + "execution_count": 72, + "id": "bd0d7ae1", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:41:48.736188Z", + "start_time": "2022-10-27T11:41:32.976115Z" + } + }, + "outputs": [], "source": [ - "## 추천 알고리즘" + "from surprise.dataset import DatasetAutoFolds\n", + "\n", + "reco_cust_pv.to_csv('./reco_cust_pv_noh.csv', index=False, header=False)\n", + "\n", + "reader = Reader(line_format='user item rating', sep=',', rating_scale=(0, 5.0))\n", + "# DatasetAutoFolds 클래스를 ratings_noh.csv 파일 기반으로 생성\n", + "data_folds = DatasetAutoFolds(ratings_file='./reco_cust_pv_noh.csv', reader=reader)\n", + "\n", + "# 전체 데이터를 학습 데이터로 생성함.\n", + "trainset = data_folds.build_full_trainset()" ] }, { - "cell_type": "markdown", - "id": "6304e18c", - "metadata": {}, + "cell_type": "code", + "execution_count": 73, + "id": "6fe5ccd2", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:42:04.776038Z", + "start_time": "2022-10-27T11:41:48.737714Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "### CountVectorizer : 추천 target 벡터화" + "algo = SVD(n_epochs=20, n_factors=50, random_state=0)\n", + "algo.fit(trainset)" ] }, { "cell_type": "code", - "execution_count": 89, - "id": "7983eeb0", + "execution_count": 95, + "id": "9dd55a11", "metadata": { "ExecuteTime": { - "end_time": "2022-10-27T08:17:31.241224Z", - "start_time": "2022-10-27T08:17:25.458062Z" + "end_time": "2022-10-26T02:48:29.085581Z", + "start_time": "2022-10-26T02:48:29.073980Z" } }, "outputs": [ @@ -3874,182 +4358,322 @@ "name": "stdout", "output_type": "stream", "text": [ - " (0, 162)\t1\n", - " (0, 13865)\t1\n", - " (0, 233)\t1\n", - " (0, 24114)\t1\n", - " (0, 171)\t1\n", - " (0, 13866)\t1\n", - " (0, 757)\t1\n", - " (1, 162)\t1\n", - " (1, 24114)\t1\n", - " (1, 3263)\t1\n", - " (1, 164)\t1\n", - " (1, 3839)\t1\n", - " (2, 162)\t1\n", - " (2, 13865)\t1\n", - " (2, 24114)\t1\n", - " (2, 171)\t1\n", - " (2, 8251)\t1\n", - " (2, 13874)\t1\n", - " (2, 8792)\t1\n", - "(845171, 25478)\n" + "사용자 아이디 9 는 영화 아이디 42의 평점 없음\n", + " movieId title genres\n", + "38 42 Dead Presidents (1995) Action|Crime|Drama\n" ] } ], "source": [ - "from sklearn.feature_extraction.text import CountVectorizer\n", + "# 삼품에 대한 상세 속성 정보 DataFrame 로딩\n", + "pp_reco_df\n", "\n", - "# CountVectorizer를 적용하기 위해 공백문자로 word 단위가 구분되는 문자열로 변환. \n", - "# movies_df['genres_literal'] = movies_df['genres'].apply(lambda x : (' ').join(x))\n", - "# print(movies_df['genres_literal'])\n", - "count_vect = CountVectorizer(min_df=0, ngram_range=(1,2))\n", - "item_mat = count_vect.fit_transform(pp_reco_df['reco_feature'])\n", - "print(item_mat[:3])\n", - "print(item_mat.shape)" + "# userId=9의 movieId 데이터를 추출해 movieId=42 데이터가 있는지 확인.\n", + "custIds = reco_cust_pv[reco_cust_pv['고객번호']==9]['movieId']\n", + "if custIds[custIds==42].count() == 0:\n", + " print('고객번호가9 이고 의 평점 없음')\n", + " \n", + "print(pp_reco_df[pp_reco_df['소분류명']==42])" ] }, { - "cell_type": "markdown", - "id": "f00c0180", - "metadata": {}, + "cell_type": "code", + "execution_count": 75, + "id": "5e984a1d", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:42:15.506940Z", + "start_time": "2022-10-27T11:42:15.501978Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "user: 9 item: 우유 r_ui = None est = 0.01 {'was_impossible': False}\n" + ] + } + ], "source": [ - "### 코사인 유사도 측정" + "uid = str(9)\n", + "iid = str('우유')\n", + "\n", + "pred = algo.predict(uid, iid, verbose=True)" ] }, { "cell_type": "code", - "execution_count": null, - "id": "b202f53c", + "execution_count": 79, + "id": "f3c9e198", "metadata": { "ExecuteTime": { - "start_time": "2022-10-27T08:17:27.584Z" + "end_time": "2022-10-27T11:45:32.152803Z", + "start_time": "2022-10-27T11:45:32.103128Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array(['유제품', '청과', '멸치류', ..., '기타탁구용품', '여성발가락', '기타한방약재'], dtype=object)" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "from sklearn.metrics.pairwise import cosine_similarity\n", - "\n", - "item_sim = cosine_similarity(item_mat, item_mat)\n", - "print(item_sim.shape)\n", - "print(item_sim[:10])\n", - "\n", - "\n", - "\n", - "item_sim_sorted_ind = item_sim.argsort()[:, ::-1]\n", - "print(item_sim_sorted_ind[:5])\n", - "\n", - "\n", - "\n", - "print(pp_reco_df[pp_reco_df['reco_feature']==1])\n", - "print(pp_reco_df[pp_reco_df['reco_feature']==262])" + "pp_reco_df['소분류명'].unique()" ] }, { "cell_type": "code", - "execution_count": null, - "id": "be8a7f70", - "metadata": {}, - "outputs": [], + "execution_count": 81, + "id": "45b1b79a", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:49:03.378424Z", + "start_time": "2022-10-27T11:49:03.357226Z" + } + }, + "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", + "
소분류명성별연령대거주지역중분류명reco_feature
0유제품M60서울특별시 강남구축산가공M 60 서울특별시 강남구 축산가공
1유제품M60경기도축산가공M 60 경기도 축산가공
2유제품F60서울특별시 노원구축산가공F 60 서울특별시 노원구 축산가공
3유제품F60강원도축산가공F 60 강원도 축산가공
4유제품F60서울특별시 서대문구축산가공F 60 서울특별시 서대문구 축산가공
.....................
833889기타탁구용품F40전라남도레저취미F 40 전라남도 레저취미
833890여성발가락F50경기도여성양말F 50 경기도 여성양말
833891페스츄리류M50경기도베이커리M 50 경기도 베이커리
833892기타한방약재F50전라북도근채류F 50 전라북도 근채류
833893컵아이스크림F25인천광역시과자F 25 인천광역시 과자
\n", + "

833894 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " 소분류명 성별 연령대 거주지역 중분류명 reco_feature\n", + "0 유제품 M 60 서울특별시 강남구 축산가공 M 60 서울특별시 강남구 축산가공\n", + "1 유제품 M 60 경기도 축산가공 M 60 경기도 축산가공\n", + "2 유제품 F 60 서울특별시 노원구 축산가공 F 60 서울특별시 노원구 축산가공\n", + "3 유제품 F 60 강원도 축산가공 F 60 강원도 축산가공\n", + "4 유제품 F 60 서울특별시 서대문구 축산가공 F 60 서울특별시 서대문구 축산가공\n", + "... ... .. .. ... ... ...\n", + "833889 기타탁구용품 F 40 전라남도 레저취미 F 40 전라남도 레저취미\n", + "833890 여성발가락 F 50 경기도 여성양말 F 50 경기도 여성양말\n", + "833891 페스츄리류 M 50 경기도 베이커리 M 50 경기도 베이커리\n", + "833892 기타한방약재 F 50 전라북도 근채류 F 50 전라북도 근채류\n", + "833893 컵아이스크림 F 25 인천광역시 과자 F 25 인천광역시 과자\n", + "\n", + "[833894 rows x 6 columns]" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "def find_sim_movie(df, sorted_ind, title_name, top_n=10):\n", - " \n", - " # 인자로 입력된 movies_df DataFrame에서 'title' 컬럼이 입력된 title_name 값인 DataFrame추출\n", - " title_movie = df[df['title'] == title_name]\n", - " \n", - " # title_named을 가진 DataFrame의 index 객체를 ndarray로 반환하고 \n", - " # sorted_ind 인자로 입력된 genre_sim_sorted_ind 객체에서 유사도 순으로 top_n 개의 index 추출\n", - " title_index = title_movie.index.values\n", - " similar_indexes = sorted_ind[title_index, :(top_n)]\n", - " \n", - " # 추출된 top_n index들 출력. top_n index는 2차원 데이터 임. \n", - " #dataframe에서 index로 사용하기 위해서 1차원 array로 변경\n", - " print(similar_indexes)\n", - " similar_indexes = similar_indexes.reshape(-1)\n", - " print(similar_indexes)\n", - " \n", - " return df.iloc[similar_indexes]\n", - "\n", - "\n", - "\n", - "\n", - "similar_movies = find_sim_movie(pp_reco_df, item_sim_sorted_ind, 1 ,10)\n", - "similar_movies[['title', 'vote_average']]" + "pp_reco_df" ] }, { "cell_type": "code", - "execution_count": null, - "id": "516874af", - "metadata": {}, - "outputs": [], + "execution_count": 95, + "id": "a45a9852", + "metadata": { + "ExecuteTime": { + "end_time": "2022-10-27T11:59:47.826597Z", + "start_time": "2022-10-27T11:59:47.744785Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Top 10 추천 아이템 리스트\n", + "종량제봉투 : 0.21711009996111366\n", + "두부류 : 0.20415478729479797\n", + "일반우유 : 0.2015140544313135\n", + "일반소주 : 0.2002306990452087\n", + "공병/공박스 : 0.19890586511587782\n", + "일반스낵 : 0.17264869170813266\n", + "일반흰우유 : 0.1701977942065114\n", + "주유소 : 0.16094399710554957\n", + "국산맥주 : 0.16034588071534042\n", + "수입담배 : 0.15426302258444086\n" + ] + } + ], "source": [ - "# 평가 횟수가 적은 영화로 인하여 의미 없는 목록이 나올 수 있다.\n", - "movies_df[['title','vote_average','vote_count']].sort_values('vote_average', ascending=False)[:10]\n", - "\n", - "\n", - "\n", - "# 모든 영화의 평점 평균 = C\n", - "C = movies_df['vote_average'].mean()\n", - "# 고객 평가 수가 상위 60%인 영화의 고객 평가 수 = m\n", - "m = movies_df['vote_count'].quantile(0.6) # 분위수\n", - "# DB에 있는 영화 총 편 수 : total_movie\n", - "total_movie = len(movies_df)\n", - "print('C:',round(C,3), 'm:',round(m,3), 'total_movie:',total_movie)\n", - "\n", - "\n", - "\n", - "percentile = 0.6\n", - "m = movies_df['vote_count'].quantile(percentile) # 고객 평가 수가 상위 60%인 영화의 고객 평가 수\n", - "C = movies_df['vote_average'].mean() # 모든 영화의 평점 평균\n", - "\n", - "def weighted_vote_average(record):\n", - " v = record['vote_count'] # 해당 영화의 고객 평가 수 \n", - " R = record['vote_average'] # 해당 영화의 평균 평점\n", + "def recomm_item_by_surprise(algo, userId, top_n=10):\n", " \n", - " # 해당 영화에 고객 평가 수가 많은만큼 해당 영화 평점에 가산점을 주고,\n", - " # 해당 영화의 고객 평가 수가 적으면 전체 영화 평점에 감점을 준다.\n", - " return ( (v/(v+m)) * R ) + ( (m/(m+v)) * C )\n", + " # 알고리즘 객체의 predict() 메서드를 평점이 없는 영화에 반복 수행한 후 결과를 list 객체로 저장\n", + " predictions = [algo.predict(str(userId), str(s_cat)) for s_cat in pp_reco_df['소분류명'].unique()]\n", " \n", - "# 가충치를 부여한 점수 컬럼 추가\n", - "movies_df['weighted_vote'] = movies_df.apply(weighted_vote_average, axis=1)\n", - "\n", - "\n", - "\n", - "\n", - "movies_df[['title','vote_average','weighted_vote','vote_count']].sort_values('weighted_vote',\n", - " ascending=False)[:10]\n", - "\n", - "\n", - "\n", - "def find_sim_movie(df, sorted_ind, title_name, top_n=10):\n", - " title_movie = df[df['title'] == title_name]\n", - " title_index = title_movie.index.values\n", - " print(title_movie.index)\n", - " print(title_index)\n", + " # predictions list 객체는 surprise의 Predictions 객체를 원소로 가지고 있음\n", + " # [Prediction(uid='9'm iid='1', est=3.69)....]\n", + " \n", + " # 이를 est 값으로 정렬하기 위해서 아래의 sortkey_est 함수를 정의함\n", + " # sortkey_set 함수는 list 객체의 sort() 함수의 키 값으로 사용되어 정렬 수행.\n", + " def sortkey_est(pred):\n", + " return pred.est\n", + " \n", + " # sortket_est() 반환값의 내림 차순으로 정렬 수행하고 top_n 개의 최상위 값 추출.\n", + " predictions.sort(key=sortkey_est, reverse=True)\n", + " top_predictions = predictions[:top_n]\n", + "# display(top_predictions)\n", + " \n", + " # top_n으로 추출된 영화의 정보 추출, 영화 아이디, 추천 예상 평점, 제목 추출\n", + " top_item_ids = [ str(pred.iid) for pred in top_predictions]\n", + " top_item_rating = [ pred.est for pred in top_predictions]\n", + "# top_item_titles = movies[movies.movieId.isin(top_movie_ids)]['title']\n", + " top_item_titles = pp_reco_df[pp_reco_df.소분류명.isin(top_item_ids)]['소분류명'].unique()\n", + "# print(top_item_titles)\n", " \n", - " # top_n의 2배에 해당하는 쟝르 유사성이 높은 index 추출 \n", - " similar_indexes = sorted_ind[title_index, :(top_n*2)]\n", - " print(similar_indexes)\n", - " similar_indexes = similar_indexes.reshape(-1)\n", - " print(similar_indexes)\n", - "# 기준 영화 index는 제외\n", - " similar_indexes = similar_indexes[similar_indexes != title_index]\n", + " top_item_preds = [ (id, title, rating) for id, title, rating in zip(top_item_ids, top_item_titles, top_item_rating)]\n", " \n", - " # top_n의 2배에 해당하는 후보군에서 weighted_vote 높은 순으로 top_n 만큼 추출 \n", - " return df.iloc[similar_indexes].sort_values('weighted_vote', ascending=False)[:top_n]\n", + " return top_item_preds\n", "\n", - "similar_movies = find_sim_movie(movies_df, genre_sim_sorted_ind, 'The Godfather',10)\n", - "similar_movies[['title', 'vote_average', 'weighted_vote']]" + "# unseen_movies = get_unseen_surprise(ratings, movies, 9)\n", + "top_item_preds = recomm_item_by_surprise(algo, 1200, top_n=10)\n", + "\n", + "print(' Top 10 추천 아이템 리스트')\n", + "for top_item in top_item_preds:\n", + " print(top_item[1], ':', top_item[2])" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "ae750ca0", + "cell_type": "markdown", + "id": "f3ef7e54", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "## 추천 알고리즘" + ] } ], "metadata": { @@ -4083,7 +4707,7 @@ "height": "772px", "left": "56px", "top": "91.328125px", - "width": "259px" + "width": "258.970581px" }, "toc_section_display": true, "toc_window_display": true