์ง๋๋ฒ์ ๋ฐฐ์ด BoW, DTM, TF-IDF, ์ ํด๋ฆฌ๋์ ์ ์ฌ๋, ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ํ์ฉํ์ฌ ์ง์ ํฌ๋กค๋งํ '์คํ๋ฒ ์ค' ๊ด๋ จ ๊ธฐ์ฌ ๋ฐ์ดํฐ์ ์ผ๋ก ๋ฌธ์ ์ ์ฌ๋๋ฅผ ๊ตฌํ๋ ์ค์ต์ ์งํํด ๋ณด์๋ค.
์์ธํ ์ฝ๋๋ค์ ๊นํ๋ธ๋ฅผ ์ฐธ๊ณ ํ๊ธธ ๋ฐ๋๋ค.
1. ๋ฐ์ดํฐ ํ์ธ ๋ฐ ์ ์ฒ๋ฆฌ
๋ฐ์ดํฐ๋ฅผ df๋ผ๋ ๋ณ์์ ์ ์ฅํ๊ณ ํ์ธํด ๋ณด์๋ค.
df.head()
์ ๋ชฉ | ์ธ๋ก ์ฌ | ๋ ์ง | URL | ๋ค์ด๋ฒ๋ด์ค_URL |
์ค๋(1/1) ์ฝ์คํธ์ฝ ์ ์์์ , ์ง์ ๋ณ 1์ ํด๋ฌด์ผ·์์ ์๊ฐ 'ํ์ธํ์ธ์' | ํํฌ์ธํธ๋ด์ค | 2023.01.01. | http://www.pinpointnews.co.kr/news/articleView... | NaN |
ํธ๋์ด ๊ฐ๊ณ ๊ฒ์ ํ ๋ผ ์จ๋ค…์ ํต๊ฐ ‘ํ ๋ผ ๋ง์ผํ ’ ํ๋ฐ | ์ธ๋๋ด์ค | 2023.01.01. | https://www.inthenews.co.kr/news/article.html?... | NaN |
[์๋2022 โก] ๋จธ์ง๋ถํฐ FTX ํ์ฐ๊น์ง...์ฌํด์ ์ฃผ์ ์ด์ TOP 10 | ํ ํฐํฌ์คํธ | 2023.01.01. | https://www.tokenpost.kr/article-117836 | NaN |
๊ฐ๋จ ์ง ํ์ ์ฒญ์๋ ์ผํฐ ์ธ์ ๋ค…๋ฐ๋ฆฌ์คํ ํค์ฐ๋ ํ์ฅ๋ | ์ค์์ผ๋ณด | 2023.01.01. | https://www.joongang.co.kr/article/25130324 | https://n.news.naver.com/mnews/article/025/000... |
๋ณํํ๋ ์ ํต์ ๊ณ, ์์ญ๊ณผ ๊ฒฝ๊ณ ํ๋ฌธ 'ํ๊ดด์ ์ปค๋จธ์ค' ์๋ ์ด๋ ธ๋ค | ๋ด์ค1 | 2023.01.01. | https://www.news1.kr/articles/4865044 | https://n.news.naver.com/mnews/article/421/000... |
ํฌ๋กค๋งํ๋ ๊ณผ์ ์์ URL์ด ์ค๋ณต๋์ ์๋ ์๊ฒ ๋ค๊ณ ์๊ฐํ์ฌ ์ค๋ณต๊ฐ์ ํ์ธ ํ, ์ ๊ฑฐ, index ์ฌ์ค์ ๊น์ง ํด์ฃผ์๋ค.
# URL ๊ธฐ์ค ์ค๋ณต๊ฐ ํ์ธ
print(df.duplicated().sum())
print(df.duplicated(subset='URL').sum())
df[df.duplicated(subset='URL')]
# URL ๊ธฐ์ค ์ค๋ณต๊ฐ ์ ๊ฑฐ
df.drop_duplicates(subset='URL', inplace=True)
# ์ธ๋ฑ์ค ์ฌ์ค์
df.reset_index(drop=True, inplace=True)
์ดํ, Okt ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ์ ๋ชฉ์ ์๋ ๋จ์ด๋ค์ ๋ช ์ฌ๋ฅผ ์ค์ฌ์ผ๋ก ํ ํฐํ์ํค๊ณ , ๋ณํฉํ์ฌ ํ๋์ ๋ฆฌ์คํธ๋ก ๋ง๋ค์ด ๋ณด์๋ค.
์ดํ, CounterVecorizer ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฅ ๋ง์ด ๋ฑ์ฅํ ๋จ์ด๋ฅผ ์ถ๋ ฅํด ๋ณด์๋ค.
# Okt ๋ถ๋ฌ์ค๊ธฐ
from konlpy.tag import Okt
okt = Okt()
# ๋ช
์ฌ ์ค์ฌ์ ํ ํฐํ
df['๋จ์ด'] = df['์ ๋ชฉ'].map(okt.nouns)
# ๋จ์ด ๋ฆฌ์คํธ ๋ง๋ค๊ธฐ
word_list = sum(df['๋จ์ด'],[])
# Counter ๋ถ๋ฌ์ค๊ธฐ
from collections import Counter
c = Counter(word_list)
# ๊ฐ์ฅ ๋ง์ด ๋ฑ์ฅํ ๋จ์ด
c.most_common(100)
์ถ๋ ฅ๊ฐ์ ํ์ธํด ๋ณด๋, ์๊ฐ๋ณด๋ค '์คํ๋ฒ ์ค'์ ๊ด๋ จ๋ ๋จ์ด๊ฐ ๊ฑฐ์ ์์๋ค. ์ ์ ์ฆ๊ถ์ฌ ํฌ๋กค๋ง ๋ฐ์ดํฐ๋ฅผ ์๋ชป ๊ฐ์ ธ์๋ ์ถ๊ธฐ๋ ํ๋ค. ํ์ง๋ง ์ค์ ๋ก ๋ค์ด๋ฒ ๊ฒ์์์ ๊ธฐ์ฌ๋ค์ ํ์ธํด ๋ณด๋ ์ ๋จ์ด๋ค์ด ๋ค์ด๊ฐ ๊ธฐ์ฌ๋ค์ด ์ ๋ง ๋ง์๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด '์คํ๋ฒ ์ค' ๋๋ '์ปคํผ'๊ฐ ๋ค์ด๊ฐ ๋จ์ด๋ค๋ก๋ง ํํฐ๋ง์ ์งํํ๋ค.
df = df[df['๋จ์ด'].map(lambda x: '์คํ๋ฒ
์ค' in x or '์ปคํผ' in x)].copy(); df
์กฐ๊ธ์ ์ปคํผ์ ๊ด๋ จ๋ ๊ธฐ์ฌ๋ค์ด ๋ง์์ง ๊ฒ ๊ฐ์ ๋ณด์๋ค.
์ดํ, ์ ๋ชฉ์ ๋ค์ด๊ฐ ๋จ์ด๋ค์ ์ ์ฒ๋ฆฌํ์ฌ '์ ๋ชฉ_์ ์ฒ๋ฆฌ'๋ผ๋ ์ ๋ชฉ์ ํ์ผ๋ก ๋ค์ ์ ์ฅํ๋ค.
# ํ
์คํธ ํด๋ฆฌ๋
import re
df['์ ๋ชฉ_์ ์ฒ๋ฆฌ'] = df['์ ๋ชฉ'].map(lambda x: re.sub('[^\w\s]', ' ', x))
# ์ธ๋ฑ์ค ๋ฆฌ์
df.reset_index(drop=True, inplace=True)
2. TF-IDF ์ ์ฉ
๋ค์์ผ๋ก๋ TF-IDF ๋ฐฉ์์ผ๋ก ๋จ์ด๋ค์ ๋ฒกํฐํ์์ผ DTM์ ์์ฑํด ๋ณด์๋ค. (์ฌ์ดํท๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์๋ค.)
# TF-IDF ๋ฒกํฐํ
vectorizer = TfidfVectorizer(min_df = 10, ngram_range=(1,2), tokenizer=okt.morphs)
features = vectorizer.fit_transform(df['์ ๋ชฉ_์ ์ฒ๋ฆฌ'])
# feature ์ด๋ฆ ๋ถ๋ฌ ์ค๊ธฐ
feature_names = vectorizer.get_feature_names_out()
# DTM ์์ฑ
dtm_np = np.array(features.todense())
# DataFrame ํ์ธ
pd.DataFrame(data = dtm_np, columns = feature_names)
์ถ๋ ฅ๊ฐ์ ๋ค์๊ณผ ๊ฐ์๋ค.
๋น์ทํ ๋ด์ฉ์ ๊ธฐ์ฌ๋ค๋ก ๊ตฐ์งํ์ํค๋ ๊ฒ์ด ์ต์ข ๋ชฉํ์ด๊ธฐ ๋๋ฌธ์, ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ํตํด ์ ์ฌ๋๋ฅผ ๊ตฌํด๋ณด์๋ค.
3. ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ตฌํ๊ธฐ
์ฌ์ดํท๋ฐ์ cosine_similarity๋ฅผ ์ฌ์ฉํ๋ค.
์ดํ ํ๋์ ๊ธฐ์ฌ๋ฅผ ์ ์ ํด์ ํด๋น ๊ธฐ์ฌ์ ์ฝ์ฌ์ธ ์ ์ฌ๋๊ฐ ๋์ top 10 ๊ธฐ์ฌ๋ค์ ์ถ๋ ฅํด ๋ณด์๋ค.
# ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ตฌํ๊ธฐ
from sklearn.metrics.pairwise import cosine_similarity
cosine_sim = cosine_similarity(dtm_np, dtm_np)
# ์ธ๋ฑ์ค ์ค์
indices = pd.Series(df.index, index=df['์ ๋ชฉ'])
# ๋ด์ค ๊ธฐ์ฌ ์ธ๋ฑ์ค ์ถ์ถ
idx = indices["์คํ๋ฒ
์ค, ์ฌํด ์ปค๋ฎค๋ํฐ ์คํ ์ด ์ฒญ๋
์ธ์ฌ ๋ฐฐ์ถ…'์ญ๋ ์ต๋ค ์ธ์'"]
print(idx) # 1753
# ์ ํํ ๋ด์ค ๊ธฐ์ฌ์ ๋ค๋ฅธ ๋ด์ค ๊ธฐ์ฌ๊ฐ ์ ์ฌ๋ ํ์ธ
sim_scores = list(enumerate(cosine_sim[idx]))
# ์ ์ฌ๋๊ฐ ๋์ ์์ผ๋ก ๋ด์ค ์ ๋ ฌ
sim_scores = sorted(sim_scores, key = lambda x: x[1], reverse=True)
# ์ ์ฌ๋ ๋์ 10๊ฐ์ ๋ด์ค
sim_scores = sim_scores[0:11]; sim_scores # 0์ ์๊ธฐ ์์ ์ผ ๊ฒ
๋น์ฐํ ๊ฐ์ ๊ธฐ์ฌ๋ผ๋ฆฌ๋ 1์ ์ ์ฌ๋๋ฅผ ๋ณด์๋ค.
์ฒซ ๊ธฐ์ฌ๋ฅผ ์ ์ธํ๊ณ , ๋๋จธ์ง ๊ธฐ์ฌ๋ค๋ก ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ธฐ๋ฐ์ ๋ฐ์ดํฐํ๋ ์์ ๋ง๋ค์ด ๋ณด์๋ค.
sim_scores = sim_scores[1:11]
# ๊ฐ์ฅ ์ ์ฌํ 10๊ฐ์ ๋ด์ค์ ์ธ๋ฑ์ค
news_indices = [i[0] for i in sim_scores]
# ์ ์ฌํ ๋ด์ค ๊ธฐ์ฌ ์ ๋ชฉ ์ถ๋ ฅ
df['์ ๋ชฉ'].iloc[news_indices]
# ์ฝ์ฌ์ธ ์ ์ฌ๋ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ํ๋ ์
sim_df = pd.DataFrame(cosine_sim); sim_df
์ดํ dataframe์ ์ ์ฌ๋๊ฐ 0.5๋ฅผ ๋๋์ง์ ๋ํ ์ ๋ฌด๋ก boolean index๋ก ๋ง๋ค์ด ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๋ง๋ค์๋ค.
# Boolean Index ๋ง๋ค๊ธฐ
sim_boolean = sim_df > 0.5 ; sim_boolean
# ์ ์ฌ๋ 0.5๊ฐ ๋๋ ๊ฒ์๊ธ์ด 10๊ฐ ์ด์ ์๋ ๊ฒฝ์ฐ
sim_boolean.sum() >= 10
# 10๊ฐ ์ด์ ์๋ ๊ฒ์๊ธ์ด ๋ช ๊ฐ๋ ์์๊น?
sum(sim_boolean.sum() >= 10) #๊ฒฐ๊ณผ: 2465
4. ๊ตฐ์งํ
์ต์ข ์ ์ผ๋ก, ์ ์ฌ๋๊ฐ 25% ์ด์์ธ ๊ฒ์๋ฌผ๋ค๋ก ๊ตฐ์งํ๋ฅผ ์งํํด ๋ณด์๋ค.
# ์ ์ฌ๋ 25% ์ด์์ ๋ด์ค ํ ํฝ ๋ฌถ๊ธฐ
idx_list = list(sim_df[sim_boolean.sum() >= 10].index)
cluster = []
pass_list = []
threshold = 0.25
id_idx = []
for i in idx_list:
if i not in pass_list:
idx = sim_df[i][sim_df[i] > threshold].index
cluster.append(idx)
pass_list.extend(list(idx))
id_idx.append(i)
else:
pass
print(len(cluster)) #๊ฒฐ๊ณผ: 106
# ์คํ๋ฒ
์ค ๋ด์ค ํ ํฝ
starbucks_df = df.iloc[id_idx,:].copy(); starbucks_df
์ ๋ณด๋ค ํจ์ฌ '์คํ๋ฒ ์ค'์ '์ปคํผ' ๊ด๋ จ๋ ๊ธฐ์ฌ๋ค๋ก ๋ฌถ์ธ ๊ฒ ๊ฐ์ ์ฑ๊ณต์ ์ผ๋ก ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๋ค๊ณ ์๊ฐํ๋ค.
์ด๋ ๊ฒ, ์คํ๋ฒ ์ค ๊ธฐ์ฌ ๋ฐ์ดํฐ์ ์ผ๋ก TF-IDF๋ฅผ ์ ์ฉํ๊ณ , ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ๊ตฌํ์ฌ ์ ์ฌํ ๊ธฐ์ฌ๋ค๋ผ๋ฆฌ์ ๊ตฐ์งํ๋ฅผ ์งํํด ๋ณด์๋ค.
'๐ ์คํฐ๋ > NLP' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Word2Vec์ ๊ดํ์ฌ] - CBOW, Skip-gram (0) | 2023.07.25 |
---|---|
[NLP-์คํฐ๋] RNN์ ๊ดํ์ฌ (1) | 2023.06.29 |
[ํ ์คํธ๋ง์ด๋] 2-1. ํ ์คํธ ํํ๊ณผ ๋ฌธ์ ์ ์ฌ๋ (0) | 2023.06.19 |
[ํ ์คํธ๋ง์ด๋] 1. ํ ์คํธ ๋ถ์ (0) | 2023.06.17 |