Spaces:
Sleeping
Sleeping
Kota Takahashi
commited on
Commit
·
a08962e
1
Parent(s):
c311dc0
ファーストコミット
Browse files- .gitignore +37 -0
- app.py +73 -0
- cosine_similarity_calculator.py +74 -0
- ja/ja.bin +3 -0
- ja/ja.bin.syn0.npy +3 -0
- ja/ja.bin.syn1neg.npy +3 -0
- news_scraper.py +131 -0
- summerizer.py +38 -0
- tidif_calclator.py +53 -0
.gitignore
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
|
6 |
+
# Caches and logs
|
7 |
+
*.log
|
8 |
+
logs/
|
9 |
+
*.cache/
|
10 |
+
|
11 |
+
# Environment variables
|
12 |
+
.env
|
13 |
+
|
14 |
+
# Static files (usually collected by Django's collectstatic)
|
15 |
+
/static/
|
16 |
+
|
17 |
+
# Media files
|
18 |
+
/media/
|
19 |
+
|
20 |
+
# Database
|
21 |
+
*.sqlite3
|
22 |
+
|
23 |
+
# IDE specific files
|
24 |
+
.idea/
|
25 |
+
.vscode/
|
26 |
+
|
27 |
+
# Dependency directories
|
28 |
+
venv/
|
29 |
+
env/
|
30 |
+
|
31 |
+
# Compiled Python files
|
32 |
+
*.pyc
|
33 |
+
*.pyo
|
34 |
+
*.pyd
|
35 |
+
|
36 |
+
# macOS
|
37 |
+
.DS_Store
|
app.py
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
from news_scraper import YahooNewsScraper
|
4 |
+
from tidif_calclator import JapaneseTextVectorizer
|
5 |
+
from cosine_similarity_calculator import CosineSimilarityCalculator
|
6 |
+
from summerizer import TextSummarizer
|
7 |
+
|
8 |
+
st.title("ニュース検索アプリ")
|
9 |
+
|
10 |
+
# 初期化
|
11 |
+
best_article_text = None
|
12 |
+
best_article_url = None
|
13 |
+
best_max_word = None
|
14 |
+
max_word = None
|
15 |
+
best_max_value = -1 # cos類似度は0以上なので、初期値を-1に設定
|
16 |
+
num_news = 5
|
17 |
+
|
18 |
+
# セッションステートの初期化
|
19 |
+
if 'news_fetched' not in st.session_state:
|
20 |
+
st.session_state['news_fetched'] = False
|
21 |
+
st.session_state['article_text_list'] = []
|
22 |
+
st.session_state['article_url_list'] = []
|
23 |
+
|
24 |
+
if st.button('最新ニュース取得'):
|
25 |
+
with st.spinner('ニュースを取得中...'):
|
26 |
+
# yahooニュースをスクレイピング
|
27 |
+
scraper = YahooNewsScraper()
|
28 |
+
article_text_list = []
|
29 |
+
article_url_list = []
|
30 |
+
for i in range(num_news):
|
31 |
+
article_text, detail_url = scraper.scrape_article(i)
|
32 |
+
article_text_list.append(article_text)
|
33 |
+
article_url_list.append(detail_url)
|
34 |
+
st.session_state['news_fetched'] = True # 処理完了フラグを設定
|
35 |
+
st.session_state['article_text_list'] = article_text_list # セッションステートに保存
|
36 |
+
st.session_state['article_url_list'] = article_url_list
|
37 |
+
st.write("取得完了しました")
|
38 |
+
|
39 |
+
if st.session_state['news_fetched']:
|
40 |
+
search_word = st.text_input('名詞', placeholder='名詞を入力してください', max_chars=10, help='10文字以内の名詞')
|
41 |
+
if st.button('要約作成'):
|
42 |
+
article_text_list = st.session_state['article_text_list']
|
43 |
+
article_url_list = st.session_state['article_url_list']
|
44 |
+
for temp_article_text, temp_article_url in zip(article_text_list, article_url_list):
|
45 |
+
# TD-IDF値を計算
|
46 |
+
vectorizer = JapaneseTextVectorizer()
|
47 |
+
tfidf_dict = vectorizer.fit_transform(temp_article_text)
|
48 |
+
|
49 |
+
# cos類似度を計算
|
50 |
+
word_similarity = CosineSimilarityCalculator()
|
51 |
+
article_keyword_list = list(tfidf_dict.keys())
|
52 |
+
result_word_similarity = word_similarity.calculate_similarity(search_word, article_keyword_list)
|
53 |
+
|
54 |
+
# None でない値のみを抽出
|
55 |
+
filtered_data = {k: v for k, v in result_word_similarity.items() if v is not None}
|
56 |
+
|
57 |
+
# 最大値を持つキーとその値を取得
|
58 |
+
if filtered_data: # filtered_dataが空でないことを確認
|
59 |
+
max_word = max(filtered_data, key=filtered_data.get)
|
60 |
+
max_value = filtered_data[max_word]
|
61 |
+
# 最大値がこれまでの最大値より大きければ更新
|
62 |
+
if max_value > best_max_value:
|
63 |
+
best_max_value = max_value
|
64 |
+
best_max_word = max_word
|
65 |
+
best_article_text = temp_article_text
|
66 |
+
best_article_url = temp_article_url
|
67 |
+
|
68 |
+
# テキストを要約
|
69 |
+
summarizer = TextSummarizer()
|
70 |
+
summary_text = summarizer.summarize(best_article_text, max_length=30, min_length=20)
|
71 |
+
st.write(f'最も類似度が高いワードは「{best_max_word}」でした')
|
72 |
+
st.write(f'url:{best_article_url}')
|
73 |
+
st.text_area("要約:", summary_text, height=20)
|
cosine_similarity_calculator.py
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gensim
|
2 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
3 |
+
|
4 |
+
|
5 |
+
class CosineSimilarityCalculator:
|
6 |
+
model_path = 'ja/ja.bin'
|
7 |
+
|
8 |
+
def __init__(self):
|
9 |
+
"""
|
10 |
+
CosineSimilarityCalculatorクラスを初期化し、
|
11 |
+
事前トレーニング済みのWord2Vecモデルをロード
|
12 |
+
|
13 |
+
Parameters:
|
14 |
+
- なし。
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
- なし。
|
18 |
+
"""
|
19 |
+
self.model = gensim.models.Word2Vec.load(CosineSimilarityCalculator.model_path)
|
20 |
+
|
21 |
+
def _convert_to_2d_array(self, vector):
|
22 |
+
"""
|
23 |
+
埋め込みベクトルを2次元配列に変換
|
24 |
+
|
25 |
+
Parameters:
|
26 |
+
- vector (numpy.ndarray): 変換する1次元配列のベクトル
|
27 |
+
|
28 |
+
Returns:
|
29 |
+
- vector_2d (numpy.ndarray): 変換後の2次元配列のベクトル
|
30 |
+
"""
|
31 |
+
return vector.reshape(1, -1)
|
32 |
+
|
33 |
+
def _calculate_cosine_similarity(self, embedding1, embedding2):
|
34 |
+
"""
|
35 |
+
コサイン類似度を計算
|
36 |
+
|
37 |
+
Parameters:
|
38 |
+
- embedding1 (numpy.ndarray): 1つ目の埋め込みベクトル(2次元配列)
|
39 |
+
- embedding2 (numpy.ndarray): 2つ目の埋め込みベクトル(2次元配列)
|
40 |
+
|
41 |
+
Returns:
|
42 |
+
- similarity (numpy.ndarray): コサイン類似度
|
43 |
+
"""
|
44 |
+
return cosine_similarity(embedding1, embedding2)
|
45 |
+
|
46 |
+
def calculate_similarity(self, search_word, article_keyword_list):
|
47 |
+
"""
|
48 |
+
指定された検索ワードと記事のキーワードリストの間のコサイン類似度を計算
|
49 |
+
|
50 |
+
Parameters:
|
51 |
+
- search_word (str): 検索ワード
|
52 |
+
- article_keyword_list (list): 記事のキーワードリスト
|
53 |
+
Returns:
|
54 |
+
- similarities (dict): 記事キーワードとそれぞれの検索ワードのコサイン類似度を含む辞書を作成し、モデルにない単語の場合はNoneを返す
|
55 |
+
"""
|
56 |
+
# 検索ワードの埋め込みベクトルを取得
|
57 |
+
if search_word in self.model.wv:
|
58 |
+
search_embedding = self.model.wv[search_word]
|
59 |
+
else:
|
60 |
+
print(f"{search_word} は本モデルの語彙にありません。")
|
61 |
+
return None
|
62 |
+
|
63 |
+
similarities = {}
|
64 |
+
# 記事キーワードの埋め込みベクトルを取得し、コサイン類似度を計算
|
65 |
+
for keyword in article_keyword_list:
|
66 |
+
if keyword in self.model.wv:
|
67 |
+
keyword_embedding = self.model.wv[keyword]
|
68 |
+
search_embedding_2d = self._convert_to_2d_array(search_embedding)
|
69 |
+
keyword_embedding_2d = self._convert_to_2d_array(keyword_embedding)
|
70 |
+
similarity = self._calculate_cosine_similarity(search_embedding_2d, keyword_embedding_2d)
|
71 |
+
similarities[keyword] = similarity[0][0]
|
72 |
+
else:
|
73 |
+
similarities[keyword] = None
|
74 |
+
return similarities
|
ja/ja.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0b7f970b78b76dd1785c5e665af83c63e0c0c6129d27fcbbb39025eaf3d48a64
|
3 |
+
size 4187227
|
ja/ja.bin.syn0.npy
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:6a336b13ed39aba8ea4846d14b2140b2db5444d2a8c96c91387f077c2786be1d
|
3 |
+
size 60129680
|
ja/ja.bin.syn1neg.npy
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d13900fbeee5e3dd84ac6cc64de3e18c27c61f8c2f2eba7fb3c364213f53799e
|
3 |
+
size 60129680
|
news_scraper.py
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
from bs4 import BeautifulSoup
|
3 |
+
import re
|
4 |
+
from time import sleep
|
5 |
+
|
6 |
+
|
7 |
+
class Scraper:
|
8 |
+
def __init__(self):
|
9 |
+
"""
|
10 |
+
Scraperクラスを初期化し、requestsセッションを作成する。
|
11 |
+
"""
|
12 |
+
self.session = requests.Session()
|
13 |
+
|
14 |
+
def _fetch_content(self, url):
|
15 |
+
"""
|
16 |
+
指定されたURLのコンテンツを取得する。
|
17 |
+
|
18 |
+
Parameters:
|
19 |
+
- url (str): 取得するウェブページのURL。
|
20 |
+
|
21 |
+
Returns:
|
22 |
+
- content (bytes): 取得したコンテンツのバイトデータ。
|
23 |
+
"""
|
24 |
+
response = self.session.get(url)
|
25 |
+
response.raise_for_status() # HTTPエラーが発生した場合は例外を投げる
|
26 |
+
return response.content
|
27 |
+
|
28 |
+
def _parse_html(self, html):
|
29 |
+
"""
|
30 |
+
HTMLコンテンツをBeautifulSoupでパースする。
|
31 |
+
|
32 |
+
Parameters:
|
33 |
+
- html (bytes): パースするHTMLコンテンツ。
|
34 |
+
|
35 |
+
Returns:
|
36 |
+
- soup (BeautifulSoup): パースされたBeautifulSoupオブジェクト。
|
37 |
+
"""
|
38 |
+
soup = BeautifulSoup(html, 'html.parser')
|
39 |
+
return soup
|
40 |
+
|
41 |
+
|
42 |
+
class YahooNewsScraper(Scraper):
|
43 |
+
base_url = "https://news.yahoo.co.jp/"
|
44 |
+
|
45 |
+
def get_news_urls(self):
|
46 |
+
"""
|
47 |
+
Yahooニュースのトップページから最新ニュース記事のURLを取得する
|
48 |
+
|
49 |
+
Parameters:
|
50 |
+
- なし
|
51 |
+
|
52 |
+
Returns:
|
53 |
+
- article_url_list (list): ニュース記事のURLリスト(最大5件)
|
54 |
+
"""
|
55 |
+
content = self._fetch_content(self.base_url)
|
56 |
+
soup = self._parse_html(content)
|
57 |
+
news_list = soup.select('section.topics a') # 'topics'セクション内のすべての<a>タグを選択
|
58 |
+
article_url_list = [tag.get('href') for tag in news_list if tag.get('href')] # href属性を抽出
|
59 |
+
return article_url_list[:5] # 最初の5つのURLを返す
|
60 |
+
|
61 |
+
def get_article_url(self, index=0):
|
62 |
+
"""
|
63 |
+
指定したインデックスのニュース記事のURLを取得する
|
64 |
+
|
65 |
+
Parameters:
|
66 |
+
- index (int): 取得したい記事のインデックス (デフォルトは0)
|
67 |
+
|
68 |
+
Returns:
|
69 |
+
- article_url (str): 指定されたインデックスの記事のURL
|
70 |
+
|
71 |
+
Raises:
|
72 |
+
- IndexError: インデックスが範囲外の場合に発生
|
73 |
+
"""
|
74 |
+
article_urls = self.get_news_urls()
|
75 |
+
if index >= len(article_urls):
|
76 |
+
raise IndexError("URLが取得できませんでした") # インデックスが範囲外の場合は例外を投げる
|
77 |
+
return article_urls[index]
|
78 |
+
|
79 |
+
def get_article_detail_url(self, article_url):
|
80 |
+
"""
|
81 |
+
記事ページから詳細記事のURLを取得する
|
82 |
+
|
83 |
+
Parameters:
|
84 |
+
- article_url (str): ニュース記事のURL
|
85 |
+
|
86 |
+
Returns:
|
87 |
+
- detail_url (str): 記事の詳細ページのURL
|
88 |
+
|
89 |
+
Raises:
|
90 |
+
- ValueError: 詳細ページのURLが見つからない場合に発生
|
91 |
+
"""
|
92 |
+
content = self._fetch_content(article_url)
|
93 |
+
soup = self._parse_html(content)
|
94 |
+
detail_url_tag = soup.select_one('a:-soup-contains("記事全文を読む")') # "記事全文を読む"を含むリンクを選択
|
95 |
+
if detail_url_tag:
|
96 |
+
return detail_url_tag.get('href') # タグのhref属性を返す
|
97 |
+
else:
|
98 |
+
raise ValueError("ニュース記事が見つかりませんでした") # タグが見つからない場合はエラーを出力
|
99 |
+
|
100 |
+
def get_full_article_text(self, detail_url):
|
101 |
+
"""
|
102 |
+
詳細記事の全文を取得し、不要な文字を削除する
|
103 |
+
|
104 |
+
Parameters:
|
105 |
+
- detail_url (str): 記事の詳細ページのURL
|
106 |
+
|
107 |
+
Returns:
|
108 |
+
- full_text (str): 記事の全文テキスト
|
109 |
+
"""
|
110 |
+
content = self._fetch_content(detail_url)
|
111 |
+
soup = BeautifulSoup(content, 'html.parser')
|
112 |
+
paragraphs = soup.select('article div.article_body p') # 記事本文内のすべての<p>タグを選択
|
113 |
+
full_text = ''.join([p.text for p in paragraphs]) # すべての段落のテキストを結合
|
114 |
+
return re.sub(r"[\u3000\n\r]", "", full_text) # 不要な文字を削除
|
115 |
+
|
116 |
+
def scrape_article(self, index=0):
|
117 |
+
"""
|
118 |
+
指定されたインデックスの記事をスクレイプし、全文を取得する
|
119 |
+
|
120 |
+
Parameters:
|
121 |
+
- index (int): スクレイプする記事のインデックス (デフォルトは0)
|
122 |
+
|
123 |
+
Returns:
|
124 |
+
- full_text (str): スクレイプされた記事の全文テキスト
|
125 |
+
"""
|
126 |
+
article_url = self.get_article_url(index)
|
127 |
+
sleep(1) # サーバー負荷を避けるために1秒待機
|
128 |
+
detail_url = self.get_article_detail_url(article_url)
|
129 |
+
sleep(1) # サーバー負荷を避けるためにさらに1秒待機
|
130 |
+
article_text = self.get_full_article_text(detail_url)
|
131 |
+
return article_text, detail_url
|
summerizer.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import pipeline, T5Tokenizer, T5ForConditionalGeneration
|
2 |
+
|
3 |
+
|
4 |
+
class TextSummarizer:
|
5 |
+
model_name = "sonoisa/t5-base-japanese"
|
6 |
+
tokenizer_name = "sonoisa/t5-base-japanese"
|
7 |
+
|
8 |
+
def __init__(self):
|
9 |
+
"""
|
10 |
+
TextSummarizerクラスを初期化し、トークナイザ、モデル、パイプラインを設定
|
11 |
+
|
12 |
+
Parameters:
|
13 |
+
- なし
|
14 |
+
|
15 |
+
Returns:
|
16 |
+
- な。
|
17 |
+
"""
|
18 |
+
# トークナイザを個別に初期化し、legacy=Falseを指定
|
19 |
+
self.tokenizer = T5Tokenizer.from_pretrained(self.tokenizer_name, legacy=False)
|
20 |
+
# モデルを個別に初期化
|
21 |
+
self.model = T5ForConditionalGeneration.from_pretrained(self.model_name)
|
22 |
+
# パイプラインを初期化
|
23 |
+
self.summarizer = pipeline("summarization", model=self.model, tokenizer=self.tokenizer)
|
24 |
+
|
25 |
+
def summarize(self, text, max_length=20, min_length=10):
|
26 |
+
"""
|
27 |
+
テキストを要約
|
28 |
+
|
29 |
+
Parameters:
|
30 |
+
- text (str): 要約する対象のテキスト。
|
31 |
+
- max_length (int): 要約の最大長 (デフォルトは20)
|
32 |
+
- min_length (int): 要約の最小長 (デフォルトは10)
|
33 |
+
|
34 |
+
Returns:
|
35 |
+
- summary_text (str): 要約されたテキスト
|
36 |
+
"""
|
37 |
+
summary = self.summarizer(text, max_length=max_length, min_length=min_length, do_sample=False)
|
38 |
+
return summary[0]['summary_text']
|
tidif_calclator.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import MeCab
|
2 |
+
import re
|
3 |
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
4 |
+
|
5 |
+
|
6 |
+
class JapaneseTextVectorizer:
|
7 |
+
def __init__(self):
|
8 |
+
"""。
|
9 |
+
MeCabのTaggerとTF-IDFベクトライザーを初期化
|
10 |
+
"""
|
11 |
+
self.mecab_tagger = MeCab.Tagger()
|
12 |
+
self.tfidf_model = TfidfVectorizer(token_pattern='(?u)\\b\\w+\\b', norm=None)
|
13 |
+
self.vocab_list = []
|
14 |
+
|
15 |
+
def _extract_nouns(self, text):
|
16 |
+
"""
|
17 |
+
テキストから名詞を抽出
|
18 |
+
|
19 |
+
Parameters:
|
20 |
+
- text (str): 名詞を抽出する対象のテキスト
|
21 |
+
|
22 |
+
Returns:
|
23 |
+
- nouns (list): 抽出された名詞リスト
|
24 |
+
"""
|
25 |
+
node = self.mecab_tagger.parseToNode(text)
|
26 |
+
nouns = []
|
27 |
+
while node:
|
28 |
+
word = node.surface
|
29 |
+
hinshi = node.feature.split(",")[0]
|
30 |
+
if hinshi == "名詞":
|
31 |
+
if (not word.isnumeric()) and (not re.match(r'^[\u3040-\u309F]+$', word)):
|
32 |
+
# 名詞が数値と平仮名のみの場合は除き、それ以外の名詞を保存
|
33 |
+
nouns.append(word)
|
34 |
+
node = node.next
|
35 |
+
return nouns
|
36 |
+
|
37 |
+
def fit_transform(self, text):
|
38 |
+
"""
|
39 |
+
テキストをTF-IDF表現に変換
|
40 |
+
Parameters:
|
41 |
+
- text (str): TF-IDF表現に変換する対象のテキスト
|
42 |
+
Returns:
|
43 |
+
- tfidf_dict (dict): 単語とそのTF-IDF値を格納した辞書
|
44 |
+
"""
|
45 |
+
nouns = self._extract_nouns(text)
|
46 |
+
self.tfidf_model.fit(nouns)
|
47 |
+
vocab_text = " ".join(nouns)
|
48 |
+
tfidf_vec = self.tfidf_model.transform([vocab_text]).toarray()[0]
|
49 |
+
tfidf_dict = dict(zip(self.tfidf_model.get_feature_names_out(), tfidf_vec))
|
50 |
+
tfidf_dict = {word: num_val for word, num_val in tfidf_dict.items() if num_val > 0}
|
51 |
+
# TF-IDF値で辞書をソートし、上位5つの要素を取得
|
52 |
+
top_tfidf = dict(sorted(tfidf_dict.items(), key=lambda x: x[1], reverse=True)[:5])
|
53 |
+
return top_tfidf
|