νŒŒμ΄μ¬μ„ ν™œμš©ν•œ 데이터 μ‚¬μ΄μ–ΈμŠ€ - μ •μΉ˜ μ„±ν–₯ λΆ„μ„ν•˜κΈ°
πŸ—žοΈ

νŒŒμ΄μ¬μ„ ν™œμš©ν•œ 데이터 μ‚¬μ΄μ–ΈμŠ€ - μ •μΉ˜ μ„±ν–₯ λΆ„μ„ν•˜κΈ°

Created
Aug 12, 2022
Editor
cleanUrl: 'articles/politicTend'
❓
β€œμ€‘λ¦½μ μΈ 제λͺ©μ˜ μ‹€μ‹œκ°„ 라이브 λ‰΄μŠ€μ˜μƒμ—μ„œλ„ μ±„λ„μ˜ μ •μΉ˜μ„±ν–₯에 따라 λŒ“κΈ€μ˜ μ°¬λ°˜μ—¬λ‘  차이가 λ‘λ“œλŸ¬μ§€κ²Œ λ‚˜νƒ€λ‚ κΉŒ?”
이λ₯Ό μ•Œμ•„λ³΄κΈ° μœ„ν•΄
  • 진보와 보수둜 각각 λŒ€ν‘œλ˜λŠ” μ„œλ‘œ λ‹€λ₯Έ μ •μΉ˜μ„±ν–₯의 두 μ±„λ„μ˜
  • μ •μΉ˜μ„±ν–₯ 상관없이 μ ˆλŒ€ λ‹€μˆ˜κ°€ μ‹œμ²­ν–ˆμ„ β€˜λŒ€ν†΅λ Ή μ·¨μž„μ‹ 라이브 μ˜μƒβ€™ λŒ“κΈ€μ„
λΉ„κ΅ν•΄λ³΄κΈ°λ‘œ ν–ˆμŠ΅λ‹ˆλ‹€.
Β 
λŒ“κΈ€ ν—ˆμš© μ—¬λΆ€, 유튜브 채널 κ΅¬λ…μž 수 및 ν•΄λ‹Ή μ˜μƒ 쑰회수λ₯Ό κΈ°μ€€μœΌλ‘œ μ˜μƒμ„ μ„ μ •ν•œ κ²°κ³Ό, 진보 μ–Έλ‘ μ‚¬λ‘œλŠ” JTBC, 보수 μ–Έλ‘ μ‚¬λ‘œλŠ” 채널 Aλ₯Ό μ„ μ •ν•˜μ—¬ μ˜μƒ λŒ“κΈ€μ„ ν¬λ‘€λ§ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
Β 
Β 

1. λŒ“κΈ€ 크둀링

Β 
Β 
from selenium import webdriver as wd from bs4 import BeautifulSoup import time from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager #μ €μž₯경둜 μ„€μ • s = Service("C:/Users/vip/Downloads/chromedriver_win32/chromedriver.exe") driver = wd.Chrome(service = s) #driver = wd.Chrome(executable_path = "C:/Users/vip/Downloads/chromedriver_win32/chromedriver.exe") time.sleep(2) #μœˆλ„μš° 크기 쑰절 driver.maximize_window() #JTBCλ‰΄μŠ€ λΌμ΄λΈŒμ˜μƒ 링크 접속 url = 'https://www.youtube.com/watch?v=ksO9xGYTf1s' driver.get(url) #μ ‘μ†ν•œ νŽ˜μ΄μ§€μ˜ κ°€μž₯ μ•„λž˜κΉŒμ§€ μŠ€ν¬λ‘€ν•˜κΈ° last_page_height = driver.execute_script("return document.documentElement.scrollHeight") while True : driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);") time.sleep(30.0) new_page_height = driver.execute_script("return document.documentElement.scrollHeight") if new_page_height == last_page_height: break last_page_height = new_page_height #νŒŒμ‹± 방법 μ„€μ • html_source = driver.page_source soup = BeautifulSoup(html_source, 'lxml') # μŠ€ν¬λž˜ν•‘ν•  μš”μ†Œ μ„€μ •(λ‹‰λ„€μž„, λŒ“κΈ€, 곡감 수) userID = soup.select('a#author-text') #λ‹€λ₯Έ 경둜..? userID = soup.select('div#header-author > a > span') comment = soup.select('yt-formatted-string#content-text') positive = soup.select('span#vote-count-middle') str_userID = [] str_comment = [] str_positive = [] #μ˜λ―Έμ—†λŠ” κ°’ 처리 for i in range(len(userID)): str_tmp = str(userID[i].text) str_tmp = str_tmp.replace('\n', '') str_tmp = str_tmp.replace('\t', '') str_tmp = str_tmp.replace(' ', '') str_userID.append(str_tmp) str_tmp = str(comment[i].text) str_tmp = str_tmp.replace('\n', '') str_tmp = str_tmp.replace('\t', '') str_tmp = str_tmp.replace(' ', '') str_comment.append(str_tmp) str_tmp = str(positive[i].text) str_tmp = str_tmp.replace('\n', '') str_tmp = str_tmp.replace('\t', '') str_tmp = str_tmp.replace(' ', '') str_positive.append(str_tmp) #처리된 데이터 좜λ ₯ for i in range(len(str_userID)): print(str_userID[i], str_comment[i], str_positive[i]) #λ°μ΄ν„°ν”„λ ˆμž„ λ§Œλ“€κΈ° import pandas as pd pd_data = {"ID":str_userID, "Coment":str_comment, "positive":str_positive} youtube_jtbcnews_pd = pd.DataFrame(pd_data) youtube_jtbcnews_pd youtube_jtbcnews_pd.to_excel("C:/Users/...경둜/youtube_jtbcnews_comments.xlsx")
Β 

2. ν˜•νƒœμ†Œ 뢄석

Β 

λΆˆμš©μ–΄ μ„€μ •

뢄석할 λŒ“κΈ€μ„ 직접 λͺ¨μ•„λ³΄μ•˜μœΌλ‹ˆ μ΄μ œλŠ” 데이터λ₯Ό 가곡해볼 μ°¨λ‘€μž…λ‹ˆλ‹€. λͺ¨μ€ 데이터λ₯Ό λ¬΄μž‘μ • μ‹œκ°ν™”ν–ˆλ‹€κ°„ 띄어쓰기도 μ œλŒ€λ‘œ λ˜μ–΄ μžˆμ§€ μ•Šμ€ λŒ“κΈ€μ΄ κ·ΈλŒ€λ‘œ λ“€μ–΄κ°ˆ 수 μžˆμœΌλ‹ˆκΉŒμš”!
λ¨Όμ €, 우리의 λΆ„μ„μ—μ„œ μ“°μ§€ μ•Šμ„ 단어λ₯Ό μ§€μ •ν•΄ λ°μ΄ν„°μ—μ„œ μ‚­μ œν•΄ 쀄 ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 단어듀을 λ°”λ‘œ λΆˆμš©μ–΄λΌκ³  μ§€μΉ­ν•˜λŠ”λ°, μ—°κ΅¬μž λ§ˆμŒλŒ€λ‘œ μ§€μ •ν•  수 μžˆλ‹΅λ‹ˆλ‹€! 보톡은 β€˜μ€/λŠ”/이/가’와 같은 μ‘°μ‚¬λ‚˜, β€˜~ν•©λ‹ˆλ‹€β€™ 같은 μ–΄λ―Έ, β€˜μ‚¬λžŒβ€™, β€˜μ‚¬μ‹€β€™, β€˜μ–΄μ΄μΏ β€™κ°™μ΄ 큰 뜻이 μ—†λŠ” 단어듀을 λΆˆμš©μ–΄λ‘œ μ§€μ •ν•˜κ³ λŠ” ν•©λ‹ˆλ‹€. 보편적으둜 μ“°μ΄λŠ” ν•œκ΅­μ–΄ λΆˆμš©μ–΄ μ˜ˆμ‹œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.
Β 
이번 μ‹€μŠ΅μ—μ„œλŠ” β€˜μœ€μ„μ—΄ λŒ€ν†΅λ Ή μ·¨μž„μ‹β€™μ΄ λŒ€μ£Όμ œμ˜€λ˜ 만큼, μœ„ 파일 속 단어듀과 ν•¨κ»˜ 두 μ±„λ„μ˜ λŒ“κΈ€μ— κ³΅ν†΅μœΌλ‘œ 많이 λ‚˜μ˜¬ λ§Œν•œ β€˜μœ€μ„μ—΄β€™, β€˜λŒ€ν†΅λ Ήβ€™, β€˜λŒ€ν†΅λ Ήλ‹˜β€™ λ“±μ˜ 단어도 λΆˆμš©μ–΄λ‘œ μ„€μ •ν–ˆλ‹΅λ‹ˆλ‹€! λ˜ν•œ, μœ„ νŒŒμΌμ— λ¬Έμž₯ λΆ€ν˜ΈλŠ” ν¬ν•¨λ˜μ§€ μ•Šμ•˜μœΌλ‹ˆ, 데이터 μ‹œκ°ν™”μ—μ„œ β€˜!’와 같은 κΈ°ν˜Έκ°€ κ°€μž₯ 크게 λ‚˜μ˜€κΈΈ μ›ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ λ¬Έμž₯ λΆ€ν˜Έλ„ λ³„λ„λ‘œ μΆ”κ°€ν•΄μ€˜μ•Όκ² μ£ ?
Β 

그러면 이것을 μ–΄λ–»κ²Œ μ½”λ“œλ‘œ μ •μ˜ν•˜λ‚˜μš”?

ν˜„μž¬ λΆˆμš©μ–΄κ°€ txt 파일둜 μ •μ˜λ˜μ–΄ μžˆμœΌλ‹ˆ λ‚˜μ€‘μ— 이것을 μ‚¬μš©ν•˜λ €λ©΄ ν•œ 라이브러리 μ•ˆμ— 미리 넣어둬야 ν•©λ‹ˆλ‹€. 그러기 μœ„ν•΄μ„  μ½”λ“œλ‘œ 미리 λΆˆμš©μ–΄λ₯Ό μ •μ˜ν•΄μ•Ό ν•˜λŠ”λ°, μ΄λ•Œ νŒŒμΌμ„ 잘 μ‚΄νŽ΄λ³΄λ©΄ λ‹¨μ–΄λ§ˆλ‹€ μ€„λ°”κΏˆμ΄ λ˜μ–΄ μžˆλ‹€λŠ” κ±Έ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” λΆˆμš©μ–΄κ°€ μ•„λž˜λ‘œ 좜λ ₯λ˜λŠ” ν˜•νƒœκ°€ μ•„λ‹Œ, 리슀트처럼 μ˜†μœΌλ‘œ λ°°μ—΄λ˜μ–΄ μžˆλŠ” ν˜•νƒœλ₯Ό μ›ν•˜λ―€λ‘œ 이 λͺ©μ μ— λ§žμΆ”μ–΄ μ½”λ“œλ₯Ό κ΅¬ν˜„ν•΄μ€λ‹ˆλ‹€.
Β 
with open('stop_words.txt', 'r') as file: data = file.read().replace('\n', ' ') with open('stop_word2.txt', 'r') as file: content = file.read().replace('\n', ' ') stop_words = " ".join((data, content 'μœ€μ„μ—΄', 'λŒ€ν†΅λ Ή', 'λŒ€ν†΅λ Ήλ‹˜', 'λŒ€ν•œλ―Όκ΅­', 'μ·¨μž„μ‹', 'μ·¨μž„', 'λŒ€ν†΅λ Ήμ΄', '~', 'λ‚˜λΌ', 'κ΅­λ―Ό', '?!', '.', '!', ':', '...', '?', ',', 'β€₯', '?)..', ')'))
Β 
이 단계λ₯Ό 톡해 stop_words.txt, stop_word2.txt, 그리고 μš°λ¦¬κ°€ μΆ”κ°€λ‘œ μ§€μ •ν•œ 단어듀이 stop_wordsλΌλŠ” 이름 μ•„λž˜ λ“€μ–΄κ°€ 있게 λ©λ‹ˆλ‹€.
Β 

ν˜•νƒœμ†Œ 뢄석

본격적으둜 데이터λ₯Ό μ •μ˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” κΌ­ μ•Œμ•„μ•Ό ν•  κ°œλ…μ΄ μžˆμŠ΅λ‹ˆλ‹€: λ°”λ‘œ ν˜•νƒœμ†Œμž…λ‹ˆλ‹€.
πŸ’‘
ν˜•νƒœμ†Œ(morpheme): 의미λ₯Ό κ°€μ§€λŠ” κ°€μž₯ μž‘μ€ 말의 λ‹¨μœ„
예λ₯Ό λ“€μ–΄, β€˜λ‚˜λŠ” λ”₯λ‹€μ΄λΈŒ 아티클을 읽고 μžˆλ‹€.β€™λΌλŠ” λ¬Έμž₯이 μžˆλ‹€κ³  ν•©μ‹œλ‹€. μ—¬κΈ°μ„œ λ‹¨μ–΄λŠ” λ¬΄μ—‡μΌκΉŒμš”? β€˜λ‚˜β€™, β€˜λŠ”β€™, β€˜λ”₯λ‹€μ΄λΈŒβ€™, β€˜μ•„ν‹°ν΄β€™, β€˜μ„β€™, β€˜μ½κ³ β€™, β€˜μžˆλ‹€β€™λ‘œ λ‚˜λˆŒ 수 있겠죠. 보톡 λ‹¨μ–΄λŠ” λ„μ–΄μ“°κΈ°λ‘œ λ‚˜λˆ μ£Όκ³ , μ‘°μ‚¬λŠ” λ‹€λ₯Έ 단어와 λΆ™μ–΄ μžˆλ”λΌλ„ μ˜ˆμ™Έμ μœΌλ‘œ 뢄리성을 μΈμ •ν•˜μ—¬ 단어 취급을 ν•΄μ£Όλ‹ˆκΉŒμš”.
κ·Έλ ‡λ‹€λ©΄ ν˜•νƒœμ†ŒλŠ” λ¬΄μ—‡μΌκΉŒμš”? β€˜μ˜λ―Έβ€™λ₯Ό κ°€μ§„λ‹€λŠ” 것이 ꡬ체적으둜 μ–΄λ–€ λœ»μΌκΉŒμš”?
κ°„λ‹¨ν•©λ‹ˆλ‹€. β€˜μ½κ³  μžˆλ‹€β€™λŠ” λ™μ‚¬κ΅¬λ§Œ μ‚΄νŽ΄λ³Όκ²Œμš”. 그러면 μš°λ¦¬λŠ” μžμ—°μŠ€λŸ½κ²Œ 이 ꡬλ₯Ό β€˜μ½-’, β€˜-고’, β€˜μžˆ-’, β€˜-λ‹€β€™λ‘œ μ’€ 더 μž‘κ²Œ μͺΌκ°€ 수 μžˆλ‹€λŠ” κ±Έ μ•Œκ²Œ λ©λ‹ˆλ‹€. β€˜μ½κ³ β€™λΌλŠ” λ‹¨μ–΄λŠ” β€˜μ½μœΌλ©°β€™, β€˜μ“°κ³ β€™ λ“± λ‹€μ–‘ν•œ λ‹¨μ–΄λ‘œ ν™œμš©λ  수 μžˆμœΌλ‹ˆ, β€˜μ½-’과 β€˜-κ³ β€™λΌλŠ” κΈ€μžλŠ” κΌ­ λΆ™μ–΄ μžˆμ„ ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€. 두 κΈ€μžκ°€ κ°€μ§€κ³  μžˆλŠ” μ˜λ―Έλ„ λ‹€λ₯΄κ³ μš”. μš°λ¦¬λŠ” 이런 λ‹¨μœ„λ₯Ό ν˜•νƒœμ†ŒλΌκ³  λΆ€λ¦…λ‹ˆλ‹€.
μ„ΈλΆ€μ μœΌλ‘œλŠ”, ν˜•νƒœμ†ŒλŠ” μ–΄λ–€ 의미λ₯Ό κ°€μ§€λŠ”μ§€μ— 따라 μ‹€μ§ˆ ν˜•νƒœμ†Œμ™€ ν˜•μ‹ ν˜•νƒœμ†Œλ‘œ λ‚˜λ‰©λ‹ˆλ‹€. 이 μ‚¬λ‘€μ—μ„œλŠ” 행동을 μ§€μΉ­ν•˜λŠ” β€˜μ½-’과 β€˜μžˆ-’이 μ‹€μ§ˆ ν˜•νƒœμ†Œ, 어미에 ν•΄λ‹Ήν•˜λŠ” β€˜-고’, β€˜-닀’가 β€˜ν˜•μ‹ ν˜•νƒœμ†Œβ€™μ— μ†ν•œλ‹΅λ‹ˆλ‹€. μžλ¦½μ„±μ— 따라 자립 ν˜•νƒœμ†Œμ™€ 의쑴 ν˜•νƒœμ†Œλ‘œ λ‚˜λ‰˜κΈ°λ„ ν•˜λŠ”λ°, μš°λ¦¬λŠ” λ°μ΄ν„°μ˜ λ‚΄μš©μ„ μ‚΄νŽ΄λ³΄κ³  μ‹ΆμœΌλ‹ˆ ν˜•νƒœμ†Œκ°€ κ°€μ§€λŠ” μ˜λ―Έμ— μ§‘μ€‘ν•˜λ„λ‘ ν•©μ‹œλ‹€!
Β 

κ·Έλž˜μ„œ,

μš°λ¦¬κ°€ μ§€κΈˆ ν•˜κ³ μž ν•˜λŠ” 것은 λͺ¨μ€ λŒ“κΈ€μ„ β€˜μ‹€μ§ˆ ν˜•νƒœμ†Œβ€™λ‘œ λΆ„μ„ν•˜λŠ” μΌμž…λ‹ˆλ‹€.
μ‹€μ§ˆ ν˜•νƒœμ†Œμ™€ ν˜•μ‹ ν˜•νƒœμ†Œ 쀑 μš°λ¦¬κ°€ 더 관심이 μžˆλŠ” 건 λ¬΄μ—‡μΌκΉŒμš”? λ‹Ήμ—°νžˆ, μ‹€μ§ˆ ν˜•νƒœμ†Œμž…λ‹ˆλ‹€. 만일 μ–΄λ–€ ꡬ가 β€˜-κ³  -λ‹€β€™λŠ” μ‹μœΌλ‘œ ν˜•μ‹ ν˜•νƒœμ†Œλ§Œ λͺ¨μ•„ μ ν˜€ μžˆλ‹€λ©΄, β€˜μ½- 있-β€™μ΄λΌλŠ” μ‹€μ§ˆ ν˜•νƒœμ†Œ κ΅¬μ ˆλ³΄λ‹€ λ‚΄μš©μ„ μ•ŒκΈ° μ–΄λ €μš°λ‹ˆκΉŒμš”. κ·Έλ ‡μ§€λ§Œ μš°λ¦¬κ°€ λŒ“κΈ€μ„ ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ ν•˜λ‚˜ν•˜λ‚˜ 뢄석할 수 μ—†μœΌλ‹ˆ, 파이썬 νŒ¨ν‚€μ§€μ˜ νž˜μ„ λΉŒλ¦¬λ„λ‘ ν•©μ‹œλ‹€.
ν˜•νƒœμ†Œ 뢄석을 ν•΄μ£ΌλŠ” λŒ€ν‘œμ μΈ νŒ¨ν‚€μ§€λŠ” nltk둜, Natural Language Toolkit의 μ•½μžμž…λ‹ˆλ‹€. 이 νŒ¨ν‚€μ§€μ—μ„œλ„ word_tokenize ν•¨μˆ˜κ°€ λ°”λ‘œ ν˜•νƒœμ†Œ 뢄석을 λŒ€μ‹  해쀄 μΉœκ΅¬μž…λ‹ˆλ‹€. 이 과정을 μ½”λ“œλ‘œ κ΅¬ν˜„ν•˜λŠ” 과정은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.
Β 
import nltk nltk.download('punkt') from nltk import word_tokenize JTBCnltk = word_tokenize(JTBC_str) ChAnltk = word_tokenize(ChA_str)
Β 
이 κ³Όμ •μ—μ„œ μš°λ¦¬κ°€ μ•žμ— μ •μ˜ν–ˆλ˜ λΆˆμš©μ–΄λ₯Ό 써야 ν•©λ‹ˆλ‹€. stop_words에 λ“€μ–΄κ°€ μžˆλŠ” 단어λ₯Ό μ œμ™Έν•œ λ°μ΄ν„°μ—λ§Œ word_tokenizer ν•¨μˆ˜λ₯Ό μ“°λΌλŠ” μ˜λ―Έλ‘œμš”!
Β 
JTBC_nltkr = [word for word in JTBCnltk if not word in stop_words] ChA_nltkr = [word for word in ChAnltk if not word in stop_words]
Β 
μ΄λ ‡κ²Œ ν˜•νƒœμ†Œ λΆ„μ„λ§Œ ν•˜κ³  λ„˜μ–΄κ°€κΈ°λŠ” μ•„μ‰¬μš°λ‹ˆ, νŒ¨ν‚€μ§€κ°€ λ‚˜λˆˆ ν˜•νƒœμ†Œ 개수λ₯Ό μ„Έ μ£ΌλŠ” Counter νŒ¨ν‚€μ§€λ„ μ„€μΉ˜ν•΄ κ²°κ³Όλ₯Ό μ‚΄νŽ΄λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
Β 
from collections import Counter JTBCnltkc = Counter(JTBC_nltkr) ChAnltkc = Counter(ChA_nltkr) print(JTBCnltkc) print(ChAnltkc)
Β 
그런데, κ²°κ³Όλ₯Ό 보면 μ΄μƒν•œ 점이 ν•˜λ‚˜ μžˆμŠ΅λ‹ˆλ‹€. 채널 A의 λ°μ΄ν„°μ—μ„œ κ°€μž₯ 많이 λ“±μž₯ν•œ ν˜•νƒœμ†Œκ°€ β€˜λ‚˜λΌλ₯Όβ€™μ΄λΌκ³  좜λ ₯λ©λ‹ˆλ‹€. 무엇이 μ΄μƒν•œμ§€ 눈치 μ±„μ…¨λ‚˜μš”?
λ§žμ•„μš”, β€˜λ‚˜λΌλ₯Όβ€™μ€ ν•˜λ‚˜μ˜ ν˜•νƒœμ†Œκ°€ μ•„λ‹™λ‹ˆλ‹€! β€˜λ‚˜λΌβ€™μ™€ β€˜λ₯Όβ€™μ΄λΌλŠ” 두 개의 ν˜•νƒœμ†Œλ‘œ 이루어져 있죠. 그러면 νŒ¨ν‚€μ§€κ°€ 단어λ₯Ό 잘λͺ» λΆ„μ„ν•œ μ…ˆμΈλ°, μ™œ 이런 κ²°κ³Όκ°€ (그것도 κ½€ 빈번히!) λ‚˜μ˜¬κΉŒμš”?
그것은 λ°”λ‘œ nltkκ°€ μ˜μ–΄λ₯Ό ν† λŒ€λ‘œ 개발된 νŒ¨ν‚€μ§€κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. μ‰½κ²Œ λ§ν•˜μžλ©΄, μš°λ¦¬κ°€ λΆ„μ„ν•˜λ €κ³  ν•˜λŠ” 건 μ˜μ–΄κ°€ μ•„λ‹Œ ν•œκ΅­μ–΄κΈ° λ•Œλ¬Έμ— nltk의 μ„±λŠ₯이 쑰금 λ–¨μ–΄μ§„λ‹€λŠ” 것이죠.
이 점을 κ°μ•ˆν•΄ 개발된 νŒ¨ν‚€μ§€κ°€ λ°”λ‘œ KoNLPyμž…λ‹ˆλ‹€. ꡉμž₯히 직관적인 이름이죠? 그럼 KoNLPyλ₯Ό 직접 μ‚¬μš©ν•΄λ³΄λ„λ‘ ν•©μ‹œλ‹€. KoNLPyλŠ” 총 λ‹€μ„― κ°€μ§€ νŒ¨ν‚€μ§€λ₯Ό μ œκ³΅ν•˜λŠ”λ°, 이번 μ‹€μŠ΅μ—μ„œ μ‚¬μš©ν•  것은 Mecabμž…λ‹ˆλ‹€.
Β 
pip install konlpy !curl -s https://raw.githubusercontent.com/teddylee777/machine-learning/master/99-Misc/01-Colab/mecab-colab.sh | bash from konlpy.tag import Mecab mecab = Mecab()
Β 
Mecab μ•ˆμ—λŠ” 기본적으둜 ν˜•νƒœμ†Œλ₯Ό λΆ„μ„ν•΄μ£ΌλŠ” ν•¨μˆ˜ mecab.morphs와, λͺ…μ‚¬λ§Œ λΆ„μ„ν•΄μ£ΌλŠ” ν•¨μˆ˜ mecab.nouns 등이 μžˆμŠ΅λ‹ˆλ‹€. 자, 그럼 μ—¬κΈ°μ„œ ν•˜λ‚˜ 더 μƒκ°ν•΄λ΄…μ‹œλ‹€. 데이터λ₯Ό ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ μͺΌκ°œλŠ” 것과 λͺ…사 λ‹¨μœ„λ‘œ μͺΌκ°œλŠ” 것, λ‘˜ 쀑 무엇이 λ°μ΄ν„°μ˜ λ‚΄μš©μ„ 더 잘 ν‘œν˜„ν•΄μ€„κΉŒμš”?
단연 λͺ…μ‚¬μž…λ‹ˆλ‹€. ν˜•νƒœμ†Œ μ•ˆμ—λŠ” μ•žμ„œ μ–˜κΈ°ν•œ ν˜•μ‹ ν˜•νƒœμ†Œλ„ 포함돼 μžˆμœΌλ‹ˆ λͺ…μ‚¬μ˜ μ˜λ―Έκ°€ μ’€ 더 직접적일 κ²ƒμž…λ‹ˆλ‹€. κ·Έλ ‡μ§€λ§Œ, μ—­μ‹œ 눈으둜 두 차이λ₯Ό ν™•μΈν•΄λ³΄λŠ” 게 κ°€μž₯ νŽΈν•˜κ² μ£ ? μ£Όμ–΄μ§„ 데이터에 ν˜•νƒœμ†Œ 뢄석과 λͺ…사 뢄석을 λͺ¨λ‘ ν•΄μ„œ λΉ„κ΅ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
Β 
ν˜•νƒœμ†Œ λ‹¨μœ„ 뢄석
JTBCM = mecab.morphs(JTBC_str) ChAM = mecab.morphs(ChA_str) JTBC_r = [word for word in JTBCM if not word in stop_words] ChA_r = [word for word in ChAM if not word in stop_words]
Β 
λͺ…사 λ‹¨μœ„ 뢄석
JTBC_nouns = mecab.nouns(JTBC_str) ChA_nouns = mecab.nouns(ChA_str) JTBC_nresult = [word for word in JTBC_nouns if not word in stop_words] ChA_nresult = [word for word in ChA_nouns if not word in stop_words]
Β 
기본적인 데이터 μ „μ²˜λ¦¬λŠ” 끝났고, 이제 μš°λ¦¬κ°€ κ°€κ³΅ν•œ 데이터λ₯Ό 눈으둜 확인해볼 μ°¨λ‘€μž…λ‹ˆλ‹€!

3. 데이터 μ‹œκ°ν™” (μ›Œλ“œν΄λΌμš°λ“œ)

Β 
ν˜•νƒœμ†Œ 뢄석을 마친 ν›„μ—λŠ” μ‹œκ°ν™” μž‘μ—…μ„ μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 뢄석 κ²°κ³Όλ₯Ό λΆ„λͺ…νžˆ λ‚˜νƒ€λ‚΄κΈ°μ— μ›Œλ“œ ν΄λΌμš°λ“œκ°€ 적합할 것이라 νŒλ‹¨ν•˜μ—¬ μ›Œλ“œ ν΄λΌμš°λ“œλ‘œ 데이터λ₯Ό μ‹œκ°ν™”ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
πŸ’­
μ›Œλ“œ ν΄λΌμš°λ“œ(Wordcloud) : ν…μŠ€νŠΈ λ‚΄ 단어가 μ–ΈκΈ‰λœ 순으둜 크기λ₯Ό λ‹€λ₯΄κ²Œ ν•˜μ—¬ λ‹¨μ–΄μ˜ μ€‘μš”λ„λ₯Ό μ‹œκ°ν™”ν•˜λŠ” 방법
Β 

1) ν•„μš”ν•œ 라이브러리 μ„€μΉ˜

Β 
μ›Œλ“œν΄λΌμš°λ“œλ₯Ό μƒμ„±ν•˜κΈ°μ— μ•žμ„œμ„œ ν•„μš”ν•œ 라이브러리λ₯Ό μ„€μΉ˜ν•΄μ€„ κ²ƒμž…λ‹ˆλ‹€.
μš°λ¦¬κ°€ 였늘 μ‚¬μš©ν•  λΌμ΄λΈŒλŸ¬λ¦¬λŠ” λ°”λ‘œ Matplotlibμž…λ‹ˆλ‹€. matplotlib.pyplot λͺ¨λ“ˆμ€ MATLABκ³Ό λΉ„μŠ·ν•˜κ²Œ λͺ…λ Ήμ–΄ μŠ€νƒ€μΌλ‘œ λ™μž‘ν•˜λŠ” ν•¨μˆ˜μ˜ λͺ¨μŒμž…λ‹ˆλ‹€. ν•΄λ‹Ή λͺ¨λ“ˆμ˜ 각각의 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ κ°„νŽΈν•˜κ²Œ κ·Έλž˜ν”„λ₯Ό λ§Œλ“€κ³  λ³€ν™”λ₯Ό 쀄 수 μžˆμŠ΅λ‹ˆλ‹€!
Pillow λͺ¨λ“ˆμ€ 파이썬 이미지 처리λ₯Ό λ‹΄λ‹Ήν•˜λŠ” λͺ¨λ“ˆμž…λ‹ˆλ‹€. λ‹€μ–‘ν•œ 이미지 파일 ν˜•μ‹μ„ μ§€μ›ν•˜λ©°, κ°•λ ₯ν•œ 이미지 μ²˜λ¦¬μ™€ κ·Έλž˜ν”½ κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” 이미지 ν”„λ‘œμ„Έμ‹± λΌμ΄λΈŒλŸ¬λ¦¬μž…λ‹ˆλ‹€.
Β 
from wordcloud import WordCloud # matplotlib.pyplot λͺ¨λ“ˆμ˜ 각각 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ κ°„νŽΈν•˜κ²Œ κ·Έλž˜ν”„λ₯Ό λ§Œλ“€ 수 μžˆλ‹€. import matplotlib.pyplot as plt # collections λͺ¨λ“ˆμ˜ counter ν΄λž˜μŠ€λŠ” μ£Όμ–΄μ§„ 단어에 ν¬ν•¨λœ 각 κΈ€μžμ˜ 수λ₯Ό μ„Έμ–΄μ€€λ‹€. from collections import Counter from konlpy.tag import Okt from PIL import Image import numpy as np
Β 

2) mecab.nounsλ₯Ό ν™œμš©ν•œ JTBC λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ°

Β 
Mecab λ‚΄μ—μ„œ λͺ…μ‚¬λ§Œ λΆ„μ„ν•΄μ£ΌλŠ” mecab.nouns ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ JTBC λŒ“κΈ€ 데이터λ₯Ό λͺ…사 λ‹¨μœ„λ‘œ μͺΌκ°œκ³ , 이λ₯Ό μ›Œλ“œν΄λΌμš°λ“œλ‘œ μ‹œκ°ν™” ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€.
scale, max_font_size μ˜΅μ…˜μœΌλ‘œ λ‹¨μ–΄μ˜ 폰트 크기λ₯Ό μ„€μ •ν•΄μ£Όμ—ˆκ³ , background_color μ˜΅μ…˜μ„ β€œwhiteβ€λ‘œ 지정함에 따라 λ°°κ²½ 색상은 ν•˜μ–€μƒ‰μœΌλ‘œ μ„€μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€. width, height μ˜΅μ…˜μ„ 톡해 μ›Œλ“œν΄λΌμš°λ“œ 크기λ₯Ό μ„€μ •ν–ˆμœΌλ©°, random_state 인자λ₯Ό μ§€μ •ν•΄μ£Όμ–΄ μ›Œλ“œν΄λΌμš°λ“œμ˜ μŠ€νƒ€μΌμ„ κ³ μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
Β 
wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(JTBC_nouncount) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show()
notion image
Β 

3) mecab.nounsλ₯Ό ν™œμš©ν•œ Channel.A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ°

Β 
같은 λ°©λ²•μœΌλ‘œ Channel.A λŒ“κΈ€ 데이터λ₯Ό μ›Œλ“œν΄λΌμš°λ“œλ‘œ μ‹œκ°ν™” ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. μ˜΅μ…˜μ˜ 기본적인 섀정은 μ•žμ„œ JTBC λŒ“κΈ€μ„ μ‹œκ°ν™”ν•œ 것과 λ™μΌν•˜κ²Œ μ§€μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
Β 
wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(ChA_nouncount) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show()
notion image
Β 

4) Mecab.morphsλ₯Ό ν™œμš©ν•œ JTBC λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ°

Β 
μ΄μ œλŠ” Mecab λ‚΄μ—μ„œ 데이터λ₯Ό ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ μͺΌκ°œλŠ” mecab.morphs ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ JTBC λŒ“κΈ€ 데이터λ₯Ό μ›Œλ“œν΄λΌμš°λ“œλ‘œ μ‹œκ°ν™” ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
Β 
wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(JTBCMc) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show()
notion image
Β 

5) Mecab.morphsλ₯Ό ν™œμš©ν•œ Channel.A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ°

Β 
같은 λ°©λ²•μœΌλ‘œ Channel.A λŒ“κΈ€ 데이터 μ—­μ‹œ ν˜•νƒœμ†Œ λ‹¨μœ„λ‘œ μͺΌκ°œμ–΄ μ›Œλ“œν΄λΌμš°λ“œλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€!
Β 
wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(ChAc) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show()
notion image
Β 

6.) nltkλ₯Ό ν™œμš©ν•œ JTBC, Channel.A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ°

Β 
μ΄λ²ˆμ—λŠ” nltk νŒ¨ν‚€μ§€λ₯Ό μ΄μš©ν•˜μ—¬ JTBC와 Channel.A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œλ₯Ό μƒμ„±ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.
Β 
##JTBC λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ° wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(JTBCnltkc) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show() ##Channel A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ μƒμ„±ν•˜κΈ° wordcloud = WordCloud(font_path='/content/NanumGothic.ttf', scale=2.0, max_font_size=250, background_color ='white', colormap='autumn', width = 700, height = 700, random_state = 43).generate_from_frequencies(ChAnltkc) plt.figure(figsize = (6, 6)) # μ΅œμ’… μ›Œλ“œ ν΄λΌμš°λ“œ μ‚¬μ΄μ¦ˆ μ§€μ • plt.imshow(wordcloud) plt.title("Word Frequency", size = 13) plt.axis('off') # κ·Έλž˜ν”„ μΆ• 제거 plt.show()
nltk둜 λΆ„μ„ν•œ JTBC λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ
nltk둜 λΆ„μ„ν•œ JTBC λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ
nltk둜 λΆ„μ„ν•œ Channel A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ
nltk둜 λΆ„μ„ν•œ Channel A λŒ“κΈ€ μ›Œλ“œν΄λΌμš°λ“œ
Β 
두 사진 λͺ¨λ‘ κ³΅ν†΅μ μœΌλ‘œ β€œ5λ…„ λ™μ•ˆβ€μ—μ„œ β€œ5년”과 β€œλ™μ•ˆβ€μ΄λ‚˜ β€œλ‚˜λΌλ₯Όβ€μ—μ„œ β€œλ‚˜λΌβ€λ‚˜ β€œλ₯Όβ€μ˜ ν˜•νƒœμ†Œλ₯Ό μ œλŒ€λ‘œ λΆ„μ„ν•˜μ§€ λͺ»ν•œ λͺ¨μŠ΅μ„ ν™•μΈν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ μ‹œκ°μ μœΌλ‘œ ν™•μ—°ν•œ μ°¨μ΄λŠ” λ‘λ“œλŸ¬μ§€μ§€ μ•ŠμœΌλ©°, 인λͺ…을 κΈ°μ€€μœΌλ‘œ ν–ˆμ„ λ•ŒλŠ” μ–‘μͺ½ λͺ¨λ‘ β€œκΉ€κ±΄ν¬"κ°€ κ³΅ν†΅μœΌλ‘œ λ“±μž₯ν–ˆλ‹€λŠ” 점을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.
Β 

4. κ²°κ³Ό 해석

Β 
λ‹€μŒκ³Ό 같이 해석할 수 μžˆμŠ΅λ‹ˆλ‹€.
  1. 유튜브 μ‹€μ‹œκ°„ 라이브 μ˜μƒμ€ 생각보닀 μ§„λ³΄λ‚˜ 보수 μ§„μ˜μ˜ 좩성도λ₯Ό λ°˜μ˜ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€
  1. λŒ€λΆ€λΆ„ λŒ“κΈ€ λ°μ΄ν„°λ“€μ˜ μ „λ°˜μ μΈ λ§₯락은 λΉ„μŠ·ν•˜λ‚˜ νŠΉμ • ν‚€μ›Œλ“œμ˜ μ–ΈκΈ‰λŸ‰μ—μ„œ 세뢀적인 차이가 μ‘΄μž¬ν•œλ‹€
Β