{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# **MDX 데이터 분석하기**\n", "https://pypi.org/project/mdict-utils/\n", "1. **MDX 사전파일을** 활용하여 객체에 구분하기\n", "1. 각 사전의 첫번째 의미를 통해서 **NER** 사전 만들기\n", "1. 추후에 보완을 해서 wordnet 으로 강화하기\n", "\n", "## **1 데이터 불러오기**\n", "작업한 내용을 대상으로 중분류 내용 추가하기\n", "- 법률, 식품, 자동차 등 중분류 내용이 당장은 필요가 없어 보인다.\n", "- 작업을 진행하면서 필요하면 추가하기" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(388091, 2)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pickle\n", "with open('data/nerDict.pk', 'rb') as handle:\n", " nerDict = pickle.load(handle)\n", "nerDict.shape" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('명사',\n", " ['명사',\n", " '얄팍하게 저며 갖은 양념을 하여 구운 쇠고기.',\n", " '¶ 석쇠에 너비아니를 구우니 연기가 올라왔다./송 씨는 새로 구워 온 너비아니 접시를 상 위에 올려놓으며, 신호의 눈치를 살폈다.≪최일남, 거룩한 응달≫\\r\\n'])]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = \"너비아니\" # 원문 내용의 확인\n", "q_data = nerDict[nerDict.Text == query].Data.values.tolist()\n", "q_data[0]" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'식빵'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "food_token = [\"식용\",\"양념\",\"음식\",\"물고기\",\"고기\",\"동물\",\"야채\",\"열매\",\"잎\",\"줄기\",\"생선\"]\n", "\n", "from muyong.nlp import skdict_filter\n", "from konlpy.tag import Okt\n", "skdict_filter(Okt(), nerDict, food_token, \"식빵\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **2 N-Gram 데이터**\n", "**itemIndexTemp** 를 활용하며 고유단어 찾기" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "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", "
년도주차요일구분메인1메인2메인3메인4메인5메인6메인7메인8메인9메인10Unnamed: 14
1201752중식황태미역국매콤제육볶음해물볶음우동팽이맛살계란전양상추샐러드흑임자D치커리사과겉절이양념깻잎지포기김치
2201752중식육개장사천식칠리탕수육카레라이스멸치꽈리볶음사과단감샐러드훈제오리야채겨자무침콩나물매콤무침포기김치
\n", "
" ], "text/plain": [ " 년도 주차 요일 구분 메인1 메인2 메인3 메인4 메인5 메인6 \\\n", "1 2017 52 화 중식 황태미역국 매콤제육볶음 해물볶음우동 팽이맛살계란전 양상추샐러드 흑임자D \n", "2 2017 52 수 중식 육개장 사천식칠리탕수육 카레라이스 멸치꽈리볶음 사과단감샐러드 훈제오리야채겨자무침 \n", "\n", " 메인7 메인8 메인9 메인10 Unnamed: 14 \n", "1 치커리사과겉절이 양념깻잎지 포기김치 \n", "2 콩나물매콤무침 포기김치 " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "menus = pd.read_csv('data/menu_muyong.csv', encoding='ms949')\n", "menus = menus.dropna(subset=['메인1', '메인2'])\n", "menus = menus.fillna('') # NaN 값을 지운다\n", "menus.head(2)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2903\n" ] }, { "data": { "text/plain": [ "(None,\n", " '소고기당면국 누룽지닭백숙 상하이치킨스파게티 양배추쌈 미트볼조림 쫄면콩나물야채무침 파인애플 애호박새우젓볶음 지마구튀김 콘치즈구이 가래떡강정소스꼬치 양상추옥수수샐러드 치즈웨지감자튀김 오징어청파래까스 셀프산채비빔밥 돈숯불고기 소불고기 돈육메추리알장조림 건취나물볶음 후랑크봉어묵볶음 옥수수스프 미역줄기맛살볶음 날치알참치캔생야채비빔밥 땅콩콩견과류무침 고추장양념비엔나볶음')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# DataFrame 에서 고유 Token List 추출\n", "tokens, result = [], []\n", "for _ in menus.columns[4:]:\n", " tokens += menus[_].values.tolist()\n", "\n", "# &, %, ., / 등의 제거 후, 고유 Token 문서 만들기 \n", "import re\n", "for _ in tokens:\n", " _temp = re.findall(r\"\\w+\", _)\n", " if _temp: result += _temp\n", "tokens = list(set(result))\n", "print(len(tokens)), \" \".join(tokens[:25])" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2903, 8965)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Okt 모듈로 명사만 찾기 (대중적 선별기준)\n", "from konlpy.tag import Okt\n", "token_Okt = Okt().morphs(\" \".join(tokens))\n", "len(tokens), len(token_Okt)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# N-Gram 유용한 값 찾기\n", "n_gram, tokeNgram = 4, []\n", "def ngram(token, n):\n", " return [token[_: _+n] for _ in range(0, (len(token)-n+1))]\n", "\n", "for _ in tokens:\n", " tokeNgram += ngram(_, n_gram)\n", "\n", "from nltk import Text\n", "ngramObj = Text(tokeNgram) # [list] 객체로 Text 객체의 생성" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/png": "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bMuwgduydr0zft77pc6OiIiIiIhsYdSYK6Lj9qwBYOwMNeZERERERKR5qTFXRMftWUP7qgpeW7iGmYtWlTo7IiIiIiKyBVFjroi6tK/i2D3C1bk76+eUODciIiIiIrIlUWOuyE7eb0cgNObWrfcS50ZERERERLYUaswV2QEDutO7YyXvLHufp99aWOrsiIiIiIjIFkKNuSIzMw4d0AGAOyaoq6WIiIiIiDQPNeZawMH9OlBh8Mjr81m8ak2psyMiIiIiIlsANeZawLbVlRy0a08+XOfc8/LcUmdHRERERES2AGrMtZDP1YWBUG6fMBt3DYQiIiIiIiKbR425FnLY4F5079iWyfNXMHHuslJnR0REREREWjk15lpI2zYVfGrvHYBwdU5ERERERGRzqDHXgj4XPXPuv6/M4/0P15U4NyIiIiIi0poVrTFnZlea2T/M7BYzuySadriZ3W9mt5vZ74u17HI1qHdn9urTlRXvr+Wh1+aXOjsiIiIiItKKFa0x5+7nuPtX3P1UYICZDQIuBE5095OBBjM7oljLL1cnxQZCERERERERaSor9siKZtYNuBm4FDjN3b8ZTa8jNOwuyoofBYwCqKmpqR09enRR89cUDQ0NVFdXNyl+1YfrOeveBaxZD389pge9O7VpctqKVaxiFavYLSu2XPKhWMUqVrGKLR91dXX17l6X80N3L8oL2JnQiJsOfAoYDlwa+3wgcHWhNGpra70cTZgwYbPiv/Pvl73fBff57x6evFlpK1axilWsYres2HLJh2IVq1jFKrZ8ABM8T3upmN0sp7r7F4BdgC8AVUC3WEh3YFGxll/OTo66Wt5ZP4d16/XMORERERERSa/oo1m6+1qgEpgB7GFm7aKPTgDGFnv55eiAAd3p272aecve55mp75U6OyIiIiIi0goVpTFnZvua2U1mdpWZ3QL8x91nApcBN5vZdUB74JFiLL/cVVQYJ9X2ATQQioiIiIiINE2bxkPSc/eXgNNyTB8DjCnGMlubz9b14fePTeHRSe+yZNUaunVsW+osiYiIiIhIK6KHhpdITdcOHLRLT9asW889r8wtdXZERERERKSVUWOuhDIDodz24uzMCJ8iIiIiIiKJqDFXQocP2Y5u1VVMnr+C1+YuL3V2RERERESkFVFjroTatankU/vsAGggFBERERERSUeNuRL73H6hq+U9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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "from matplotlib import rc, rcParams\n", "import matplotlib.pyplot as plt\n", "rc('font', family=['NanumGothic','Malgun Gothic']) # 한글의 표시\n", "rcParams['axes.unicode_minus'] = False # '-' 표시의 처리\n", "plt.figure(figsize=(15, 3)) # 파레트 설정\n", "ngramObj.plot(70)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **3 N-Gram 의 Filtering**\n", "**itemIndexTemp** 를 활용하며 고유단어 찾기\n", "1. **Naver API 검색결과** 를 활용하는 등의 응용 방법도 가능하다" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(320585, 388091)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# N-Gram 단어들 중 사전에 있는 단어로 필터링\n", "itemIndex = nerDict.Text.values.tolist()\n", "itemIndexTemp = list(set(itemIndex))\n", "len(itemIndexTemp), len(itemIndex)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 5554/5554 [01:37<00:00, 57.08it/s]\n" ] }, { "data": { "text/plain": [ "(96, 5458)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# itemIndexTemp : NER 객체어 사전으로 필터링\n", "valid_token = []\n", "unvalid_token = []\n", "voca_tokens = ngramObj.vocab().most_common()\n", "\n", "from tqdm import tqdm\n", "for _ in tqdm(voca_tokens):\n", " if _[0] in itemIndexTemp: \n", " valid_token.append(_[0])\n", " else: \n", " unvalid_token.append(_[0])\n", "\n", "# 유효하게 추출된 단어는 96개\n", "len(valid_token), len(unvalid_token)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **4 N-Gram 데이터**\n", "**itemIndexTemp** 를 활용하며 고유단어 찾기" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28764" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 원본 메뉴데이터 새로 불러오기\n", "for _ in menus.columns[4:]:\n", " tokens += menus[_].values.tolist()\n", "len(tokens) # 메뉴 Tokens" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 1 n_gram valid Tokens: 71 unvalid: 402\n", " 2 n_gram valid Tokens: 266 unvalid: 3,191\n", " 3 n_gram valid Tokens: 359 unvalid: 7,963\n", " 4 n_gram valid Tokens: 403 unvalid: 13,473\n", " 5 n_gram valid Tokens: 422 unvalid: 18,625\n", " 6 n_gram valid Tokens: 423 unvalid: 22,683\n" ] } ], "source": [ "# K-Big 수집결과 30만개 중 11만개의 식품 연관단어 추출\n", "with open('backup/nouns01_std.txt', 'r') as f:\n", " token_nouns = f.read()\n", "food_unique_tokens = sorted(set(token_nouns.split(',')))\n", "valid_token, unvalid_token = [], []\n", "\n", "# unigram ~ 7gram 까지 유효성 사전검사 반복실행\n", "from muyong.nlp import ngram_to_Text\n", "for _n in range(1,7): \n", " ngramObj = ngram_to_Text(tokens, n_gram=_n)\n", " voca_tokens = ngramObj.vocab().most_common()\n", " for _ in voca_tokens:\n", " # food_unique_tokens : NER 객체어 사전\n", " if _[0] in food_unique_tokens: \n", " valid_token.append(_[0])\n", " else: \n", " unvalid_token.append(_[0])\n", " print(\"{:2} n_gram valid Tokens: {:4,} unvalid: {:6,}\".format(\n", " _n, len(valid_token), len(unvalid_token)))" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "521\n" ] }, { "data": { "text/plain": [ "',\\n,가래,가쓰오국,가오리,가자,가자미,가자미조림,가지,가쯔오,간장,갈릭,갈비,갈비구이,갈비볶음,갈비찜,갈비탕,갈치,갈치조림,감,감귤,감자,감자전,감자탕,갓,갓김치,갓나물,강정,강회,겉절이,게,게맛살,겨자,겨자채,견과류,경단,경채,계란,계란구이,계란말이,계란찜,계장,고구마,고구마순,고기,고기볶음,고기소,고기전골,고등어,고등어구이,고등어조림,고로게,고로케,고사리,고사리나물,고추,고추알,고추장,고춧잎,곤드레(풀),곤약,골뱅이,곰,곰피,과일,교자,구이,국,국물,국밥,국수,군만두,굴,굴소스,귤,그릴,근대,근대나물,기장,김,김구이,김말이,김'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 작업결과를 바탕으로 Huristic 내용의 수정/ 보완하기\n", "plus_word = \"\"\"등갈비/가자미/구이/조림/탕/튀김/채/깻잎/쭈꾸미/소갈비/소세지/자반/오징어/햄/호박/범벅/호박범벅/\n", "/홍어/후랑크/고추장/열무/나물/매운/등뼈/찜/꽁치/스프/후르츠/매콤/새송이/콘/닭/곤약/굴소스/버터/치킨/스파게티/피망/\n", "/브로컬리/브로컬리/계란/맛살/치즈/꼬치/데리야끼/장국/견과류/장조림/얼큰/콩나물/팽이/팽이버섯/칠리/칠리소스/우동/\n", "/미트볼/강정/고로게/고로케/메추리알/날치알/탕수강정/마카로니/사골/황태/까르보나라/돈육/깐풍육/깐풍기/해물/짜장/\n", "/해파리/냉채/실치/순살/파/파채/참치/비엔나/조개/해장국/야채/게맛살/들깨소스/감귤/무말랭이/소보로/새우젓//조갯살/노각/\n", "/생채/산채나물/파스타/치커리/숙주/돈/우거지/숙주나물/에그/연근/파인/얼갈이/장어/단무지/주먹밥/된장/동태/문어/깨/돈나물/\n", "/진미채/오징어젓/된장국/미소/미소국/도토리묵/가쓰오국/누룽지/소스/깐풍/바베큐소스/카레/북어/코다리/묵은지/알배기/산채/\n", "/케찹/케첩/느타리버섯/김치/볶음밥/감자/튀김/겉절이/해초/까스/찜닭/지리/새싹/겉절이/함박/뼈다귀/탕수/소면/베이컨/고구마순/\n", "/말이/카레/라이스/청포묵/까스/돈까스/타코야끼/너바이니/반계탕/후르츠/칵테일/야채/샐러드/애호박/쥐어/가오리/천사채/새콤/\n", "/우렁/베이컨/닭꼬치/유린기/돈까스/돈가스/홍합/오뎅/소면/들깨/소스/영양탕/찐빵/동그랑땡/김말이/크래미/춘장/대구/구이/사천/\n", "/군만두/유린기/애호박/후라이드/뼈다귀/명엽채/청포북/양장피/묵/무침/짬봉/소면/경단/마요네즈/뼈다귀/깻순/꺳잎/깻잎/해초/고추/\n", "/데미소스/순두부/찌개/찜닭/곤드레(풀)/모밀/메밀/빈대떡/떡볶이/어묵무침/흑임자/굴/쌀/동그랑땡/가쯔오/마요/곰피/푸실리/초무침/\n", "/동그랑땡/스팸/천사채/마요/고등어/후루츠/와사비/찹쌀/아몬드/명태/메추리/계장/민찌/새싹/석박지/무석박지/쭈꾸미/포테이토/\"\"\" \n", "# 추가할 내용을 Huristic 선별 후 추가\n", "except_word = \"\"\"고사/기/꾸미/다대/다리/대/대국/동/두/등/띠/미파/물파/물새우/물배추/미무/바라기/박/박새/발/부들/\n", "/부어/부육/부초/산/살/살파/상/선/소갈/수/어미/어부/어비/어자반/어조/어채/어초/어해/오어/우마/우무/운감/조/종/주식/\n", "/줄/지간/지감/지어/진/집/쪽/키/파리/파초/팽/포/풍/피/합/햄에그/호박범벅/홍어회/각/갓무/견과/과류/물국/약/육/순살/순살/\n", "/개/색소/각부/육장/인/이초/모/진미/고추장찌개/양초/육보/젓/살치/겨자무/김치볶음밥/소/카레라이스/싹/순/야채샐러드/들깨소스/\n", "/귀/그릇/대구구이/맥/이드/귀/묵무침/경/고추전/순두부찌개/거/감자튀김/장지/\"\"\" # 국내산, 중국산 내용 제거\n", "except_word = except_word.split('/')\n", "valid_token = [_ for _ in valid_token if _ not in except_word]\n", "valid_token += plus_word.split('/')\n", "valid_token = sorted(list(set(valid_token))); print(len(valid_token))\n", "\n", "# 현재 작업된 결과가 있는만큼 추가하지 않기\n", "# with open(\"data/nouns03_food.txt\", \"w\") as f:\n", "# f.write(\",\".join(valid_token[2:]))\n", "\",\".join(valid_token)[:300] # \",\".join(valid_token[2:100])" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2880, '소고기당면국 누룽지닭백숙 상하이치킨스파게티 양배추쌈 미트볼조림 쫄면콩나물야채무침 파인애플 딷기 애호박새우젓볶음 지마구튀김')" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import re\n", "from nltk import Text\n", "token_list = re.findall(r\"[가-힣]+\", \" \".join(tokens))\n", "token_list = list(set(token_list))\n", "len(token_list), \" \".join(token_list[:10])" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "from collections import defaultdict\n", "def food_unique_tokens(token_temp, valid_token):\n", " token_temp_list = defaultdict(int)\n", " for _ in valid_token: # 검증용 단어와 일치여부 확인\n", " if token_temp.find(_) != -1: # 포함된 Token 추출\n", " token_temp_list[_] = len(_)\n", "\n", " # 검증용 단어 중, 중복token 필터링 (길이가 긴 단어순 정렬 후 중복단어들 제거하기)\n", " token_temp_list = sorted(token_temp_list.items(), key = lambda i: i[1], reverse=True)\n", " token_temp_list = [_[0] for _ in token_temp_list]\n", " for i, _ in enumerate(token_temp_list):\n", " for chk in token_temp_list[i+1:]:\n", " if _.find(chk) != -1:\n", " token_temp_list.remove(chk)\n", " \n", " # 개별 단어의 출현순서(find)로 재정렬 {token:index}\n", " token_rerange = {_:token_temp.find(_) for _ in token_temp_list}\n", " token_rerange = list(sorted(token_rerange.items(), key = lambda i: i[1], reverse=False))\n", " token_temp_list = [_[0] for _ in token_rerange]\n", " return token_temp_list" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "원본내용: 비엔나브로콜리칠리볶음\n" ] }, { "data": { "text/plain": [ "['비엔나', '브로콜리', '칠리', '볶음']" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Valid Unique Token\n", "token_temp = token_list[1428]\n", "print(\"원본내용:\", token_temp)\n", "token_filter = food_unique_tokens(token_temp, valid_token)\n", "token_filter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **5 내용의 분류 수정**\n", "음식의 Type 에 따라 사전을 다르게 적용하기\n", "1. **Category 를 구분** 하는 사전\n", "1. 구분한 결과로 **식별용 개별 단어사전** 작업하기" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "token01 = \"\"\",랍스타,로스트,수제쿠키,쉐이크,쉬림프,스낵,스크램블,멜론,버거,버팔로윙,쏘세지,아이스크림,애플,더치커피,\n", ",도넛,또띠아,또띠야,라떼,롤케이크,리조또,마들렌,마멀레이드,마카롱,스트로베리,시나몬,요거트,요구르트,머랭쿠키,머핀,모카,\n", ",모카머핀,바닐라,바질,발사믹,생크림,브리또,비스킷,빙수,올리브,올리브오일,와인,와플,우유,자몽,젤리,주스,쥬스,진저,쨈,\n", ",시리얼,체리,초코머핀,초코볼,초코칩,초코칩머핀,초코쿠키,초콜릿,초콜릿칩,츄러스,카라멜,카스테라,카페모카,캐러멜,캬라멜,\n", ",커피,컵케이크,컵케익,케이크,케익,케일,코코넛,코코아,쿠키,크래커,크랜베리,크레페,크로와상,크리미,크림,크림머핀,타르타르,\n", ",타르트,토스트,티라미수,티라미슈,파운드케이크,푀유나베,푸딩,프레첼,프렌치,프렌치토스트,핫케이크,핫케익,허브,타코,낫토,\n", ",모카,발사믹,브리또,스트로베리,요거트,초코칩,컵케이크,케일,타코,파운드케이크,핫케이크,\n", "\n", ",가슴살,계란,고구마줄기,고등어,고명,고추,고춧가루,과메기,광어,구이,구절판,군밤,깍뚜기,꼬막,꼴뚜기,나물,나박,해삼,냉국,냉라면,\n", ",노가리,다슬기,달걀,달래,닭도리탕,대추,동치미,두부,두유,들기름,라떼,라면,라볶이,막걸리,식혜,탕수,참외,말랭이,목살,묵사발,\n", ",미숫가루,민어,병어,볶음,부추,부침,부침개,말랭이,목살,묵사발,미숫가루,민어,병어,볶음,부추,부침,부침개,막걸리,맥주,메기,멘보샤,\n", ",명란,명란젓,모과,밤,뱅어포,복분자,붕어,삼겹,새우,샐러리,생강,샤브샤브,석류,소바,소박이,송어,수프,식혜,아구,에이드,오골계,\n", ",오믈렛,오향장육,옥수동,우럭,우육면,육우,잼쿠키,전골,젓갈,제육,조림,족발,주꾸미,쥐포,참기름,참외,탕수,토란대,튀각,튀김,한라봉,\n", ",한치,호두,홍삼,홍시,황도,후라이,흑임자,\"\"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 명사 추출사전 데이터 호출\n", "# with open(\"data/nouns_food_03-2.txt\", \"r\") as f:\n", "# valid_token = f.read()\n", "# valid_token = valid_token.split(\",\"); len(valid_token)\n", "# except_word = \"\"\"/\"\"\" # 국내산, 중국산 내용 제거\n", "# valid_token += plus_word.split('/')\n", "# # valid_token = [_ for _ in valid_token if _ not in except_word.split('/')]\n", "# valid_token = sorted(list(set(valid_token)))\n", "# valid_token += valid_token[0].split(\",\")\n", "# valid_token = sorted(set(valid_token[1:]))[3:]\n", "# with open(\"data/nouns_food_03-2.txt\", \"w\") as f:\n", "# f.write(\",\".join(valid_token))\n", "# len(valid_token)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1# 1# korea = [] # 1.한식분류\n", "west = [] # 2.서양식\n", "chijap = [] # 3.중식일식\n", "fruitSource = [] # 4.과일소스\n", "desert = [] # 5.디저트\n", "error = []\n", "\n", "for no, _ in enumerate(token01.split(\",\")):\n", " x = input(_ + \"\\n(1)한,(2)서양,(3)중일,(4)과일소스,(5)디저트\")\n", " if int(x) == 1: \n", " korea.append(_)\n", " print(\"{:.2f}% 한식 분류완료 : \".format( no/len(token01.split(\",\")) * 100))\n", " elif int(x) == 2:\n", " west.append(_)\n", " print(\">> 양식 분류완료\")\n", " elif int(x) == 3:\n", " west.append(_)\n", " print(\">> 중일식 분류 완료\")\n", " elif int(x) == 4:\n", " west.append(_)\n", " print(\">> 과일소스 분류 완료\")\n", " elif int(x) == 5:\n", " west.append(_)\n", " print(\">> 디저트 분류 완료\")\n", " else:\n", " print(\">>>> ERROR >>>> 입력 내용이 없습니다 >>>>\")\n", " error.append()\n", "error" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# **NLP 기본 함수들**\n", "레빈스타인 편집함수등 기본적인 내용 정리하기\n", "\n", "## **1 Python 기본함수로 만들기**\n", "from Tutorials" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n" ] } ], "source": [ "def levenshtein(s, t, display=False):\n", " \"\"\" iterative_levenshtein(s, t) -> ldist\n", " ldist is the Levenshtein distance between the strings s and t.\n", " For all i and j, dist[i,j] will contain the Levenshtein distance \n", " between the first i characters of s and the first j characters of t\"\"\"\n", " rows, cols = len(s)+1, len(t)+1\n", " dist = [[0 for x in range(cols)] for x in range(rows)]\n", " # source prefixes can be transformed into empty strings \n", " # by deletions:\n", " for i in range(1, rows):\n", " dist[i][0] = i\n", " # target prefixes can be created from an empty source string\n", " # by inserting the characters\n", " for i in range(1, cols):\n", " dist[0][i] = i \n", " for col in range(1, cols):\n", " for row in range(1, rows):\n", " if s[row-1] == t[col-1]: cost = 0\n", " else: cost = 1\n", " dist[row][col] = min(dist[row-1][col] + 1, # deletion\n", " dist[row][col-1] + 1, # insertion\n", " dist[row-1][col-1] + cost) # substitution\n", " for r in range(rows):\n", " if display: print(dist[r]) # 중간과정의 출력 \n", " return dist[row][col]\n", "\n", "print(levenshtein(\"아이오아이\", \"아이오아이\"))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "def levenshtein_ratio(s, t, ratio_calc = False):\n", " \"\"\" levenshtein_ratio_and_distance:\n", " Calculates levenshtein distance between two strings.\n", " If ratio_calc = True, the function computes the\n", " levenshtein distance ratio of similarity between two strings\n", " For all i and j, distance[i,j] will contain the Levenshtein\n", " distance between the first i characters of s and the\n", " first j characters of t\n", " \"\"\"\n", " # Initialize matrix of zeros\n", " rows = len(s)+1\n", " cols = len(t)+1\n", " distance = np.zeros((rows,cols),dtype = int)\n", "\n", " # Populate matrix of zeros with the indeces of each character of both strings\n", " for i in range(1, rows):\n", " for k in range(1,cols):\n", " distance[i][0] = i\n", " distance[0][k] = k\n", "\n", " # Iterate over the matrix to compute the cost of deletions,insertions and/or substitutions \n", " for col in range(1, cols):\n", " for row in range(1, rows):\n", " if s[row-1] == t[col-1]:\n", " cost = 0 # If the characters are the same in the two strings in a given position [i,j] then the cost is 0\n", " else:\n", " # In order to align the results with those of the Python Levenshtein package, if we choose to calculate the ratio\n", " # the cost of a substitution is 2. If we calculate just distance, then the cost of a substitution is 1.\n", " if ratio_calc == True:\n", " cost = 2\n", " else:\n", " cost = 1\n", " distance[row][col] = min(distance[row-1][col] + 1, # Cost of deletions\n", " distance[row][col-1] + 1, # Cost of insertions\n", " distance[row-1][col-1] + cost) # Cost of substitutions\n", " if ratio_calc == True:\n", " # Computation of the Levenshtein Distance Ratio\n", " Ratio = ((len(s)+len(t)) - distance[row][col]) / (len(s)+len(t))\n", " return Ratio\n", " else:\n", " # print(distance) # Uncomment if you want to see the matrix showing how the algorithm computes the cost of deletions,\n", " # insertions and/or substitutions\n", " # This is the minimum number of edits needed to convert string a to string b\n", " return \"The strings are {} edits away\".format(distance[row][col])" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.5" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1 = '아이디어'\n", "s2 = '어디아이'\n", "levenshtein_ratio(s1, s2, ratio_calc=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **2 Soynlp 모듈의 응용**\n", "기본원리와 함께, 한글용 응용모듈로써 가장 자료가 많다" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "0.3333333333333333\n" ] } ], "source": [ "from soynlp.hangle import levenshtein\n", "from soynlp.hangle import jamo_levenshtein\n", "\n", "s1 = '아이쿠야'\n", "s2 = '아이쿵야'\n", "\n", "print(levenshtein(s1, s2)) # 1\n", "print(jamo_levenshtein(s1, s2)) # 0.3333333333333333" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('ㄲ', 'ㅗ', 'ㄱ')" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from soynlp.hangle import compose\n", "from soynlp.hangle import decompose\n", "\n", "decompose('꼭') # ('ㄲ', 'ㅗ', 'ㄱ')" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "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.8" } }, "nbformat": 4, "nbformat_minor": 4 }