Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,985 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, List, Tuple, Any
|
2 |
+
from __future__ import annotations
|
3 |
+
from functools import reduce
|
4 |
+
import operator
|
5 |
+
import pandas as pd
|
6 |
+
|
7 |
+
import gradio as gr
|
8 |
+
|
9 |
+
from utility import load_json_obj
|
10 |
+
from pandas_utility import read_csv_df
|
11 |
+
from pipeline import NaturalLanguageProcessing
|
12 |
+
from my_gradio import GrBlocks, GrLayout, GrComponent, GrListener
|
13 |
+
|
14 |
+
class App(GrBlocks):
|
15 |
+
"""
|
16 |
+
アプリのクラス
|
17 |
+
"""
|
18 |
+
@staticmethod
|
19 |
+
def _create_children_and_listeners(
|
20 |
+
model_dir: str, cuisine_df_path: str, unify_dics_path: str
|
21 |
+
) -> Tuple[Dict[str, Any] | List[Any], List[Any]]:
|
22 |
+
"""
|
23 |
+
子要素とイベントリスナーの作成
|
24 |
+
|
25 |
+
Parameters
|
26 |
+
----------
|
27 |
+
model_dir : str
|
28 |
+
ファインチューニング済みモデルが保存されているディレクトリ
|
29 |
+
cuisine_df_path : str
|
30 |
+
料理のデータフレームが保存されているパス
|
31 |
+
unify_dics_path : str
|
32 |
+
表記ゆれ統一用辞書が保存されているパス
|
33 |
+
|
34 |
+
Returns
|
35 |
+
-------
|
36 |
+
Tuple[Dict[str, Any] | List[Any], List[Any]]
|
37 |
+
子要素とイベントリスナーのタプル
|
38 |
+
"""
|
39 |
+
cuisine_infos_num = 10
|
40 |
+
label_info_dics: Dict[str, str | List[str]] = {
|
41 |
+
'AREA': {
|
42 |
+
'jp': '都道府県/地方',
|
43 |
+
'color': 'red',
|
44 |
+
'df_cols': ['Prefecture', 'Areas']
|
45 |
+
},
|
46 |
+
'TYPE': {
|
47 |
+
'jp': '種類',
|
48 |
+
'color': 'green',
|
49 |
+
'df_cols': ['Types']
|
50 |
+
|
51 |
+
},
|
52 |
+
'SZN': {
|
53 |
+
'jp': '季節',
|
54 |
+
'color': 'blue',
|
55 |
+
'df_cols': ['Seasons']
|
56 |
+
},
|
57 |
+
'INGR': {
|
58 |
+
'jp': '食材',
|
59 |
+
'color': 'yellow',
|
60 |
+
'df_cols': ['Ingredients list']
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
input = InputTextbox(
|
65 |
+
model_dir, label_info_dics, cuisine_df_path, unify_dics_path,
|
66 |
+
cuisine_infos_num
|
67 |
+
)
|
68 |
+
input_samples = InputSamplesDataset()
|
69 |
+
extracted_words = ExtractedWordsHighlightedText(label_info_dics)
|
70 |
+
cuisine_infos = CuisineInfos(cuisine_infos_num)
|
71 |
+
|
72 |
+
input_submitted = GrListener(
|
73 |
+
trigger=input.comp.submit,
|
74 |
+
fn=input.submitted,
|
75 |
+
inputs=input,
|
76 |
+
outputs=[extracted_words, cuisine_infos],
|
77 |
+
scroll_to_output=True
|
78 |
+
)
|
79 |
+
|
80 |
+
input_samples_selected = GrListener(
|
81 |
+
trigger=input_samples.comp.select,
|
82 |
+
fn=InputSamplesDataset.selected,
|
83 |
+
outputs=input,
|
84 |
+
thens=input_submitted
|
85 |
+
)
|
86 |
+
|
87 |
+
children = [input, input_samples, extracted_words, cuisine_infos]
|
88 |
+
listeners = [input_submitted, input_samples_selected]
|
89 |
+
|
90 |
+
return children, listeners
|
91 |
+
|
92 |
+
|
93 |
+
class InputTextbox(GrComponent):
|
94 |
+
"""
|
95 |
+
入力欄のクラス
|
96 |
+
|
97 |
+
Attributes
|
98 |
+
----------
|
99 |
+
_nlp : NaturalLanguageProcessing
|
100 |
+
固有表現を抽出するオブジェクト
|
101 |
+
_jp_label_dic : Dict[str, str]
|
102 |
+
固有表現のラベルとその日本語訳の辞書
|
103 |
+
_cuisine_info_dics_maker : CuisineInfoDictionariesMaker
|
104 |
+
検索結果の料理の情報の辞書のリストを作成するオブジェクト
|
105 |
+
"""
|
106 |
+
def __init__(
|
107 |
+
self,
|
108 |
+
model_dir: str,
|
109 |
+
label_info_dics: Dict[str, str | List[str]],
|
110 |
+
cuisine_df_path: str,
|
111 |
+
unify_dics_path: str,
|
112 |
+
cuisine_infos_num: int
|
113 |
+
):
|
114 |
+
"""
|
115 |
+
コンストラクタ
|
116 |
+
|
117 |
+
Parameters
|
118 |
+
----------
|
119 |
+
model_dir : str
|
120 |
+
ファインチューニング済みモデルが保存されているディレクトリ
|
121 |
+
label_info_dics : Dict[str, str | List[str]]
|
122 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
123 |
+
cuisine_df_path : str
|
124 |
+
料理のデータフレームが保存されているパス
|
125 |
+
unify_dics_path : str
|
126 |
+
表記ゆれ統一用辞書が保存されているパス
|
127 |
+
cuisine_infos_num : int
|
128 |
+
表示する料理検索結果の最大数
|
129 |
+
"""
|
130 |
+
self._nlp = NaturalLanguageProcessing(model_dir)
|
131 |
+
self._jp_label_dic: Dict[str, str] = {
|
132 |
+
label: dic['jp'] for label, dic in label_info_dics.items()
|
133 |
+
}
|
134 |
+
|
135 |
+
self._cuisine_info_dics_maker = CuisineInfoDictionariesMaker(
|
136 |
+
cuisine_df_path, unify_dics_path, label_info_dics, cuisine_infos_num
|
137 |
+
)
|
138 |
+
|
139 |
+
super().__init__()
|
140 |
+
|
141 |
+
def _create(self) -> gr.Textbox:
|
142 |
+
"""
|
143 |
+
コンポーネントの作成
|
144 |
+
|
145 |
+
Returns
|
146 |
+
-------
|
147 |
+
gr.Textbox
|
148 |
+
入力欄のコンポーネント
|
149 |
+
"""
|
150 |
+
label = self._create_label()
|
151 |
+
placeholder = 'どんな料理をお探しでしょうか?'
|
152 |
+
comp = gr.Textbox(placeholder=placeholder, label=label)
|
153 |
+
|
154 |
+
return comp
|
155 |
+
|
156 |
+
def _create_label(self) -> str:
|
157 |
+
"""
|
158 |
+
ラベルの作成
|
159 |
+
|
160 |
+
Returns
|
161 |
+
-------
|
162 |
+
str
|
163 |
+
コンポーネントのラベル
|
164 |
+
"""
|
165 |
+
categories = [f'"{jp}"' for jp in self._jp_label_dic.values()]
|
166 |
+
label = f'入力文から、料理の{"、".join(categories)}を示す語彙を検出します'
|
167 |
+
|
168 |
+
return label
|
169 |
+
|
170 |
+
def submitted(
|
171 |
+
self, classifying_text: str
|
172 |
+
) -> List[List[Tuple[str, str]] | List[gr.Textbox | gr.Button]]:
|
173 |
+
"""
|
174 |
+
submitイベントリスナーの関数
|
175 |
+
|
176 |
+
Parameters
|
177 |
+
----------
|
178 |
+
classifying_text : str
|
179 |
+
固有表現抽出対象
|
180 |
+
|
181 |
+
Returns
|
182 |
+
-------
|
183 |
+
List[List[Tuple[str, str]] | List[gr.Textbox | gr.Button]]
|
184 |
+
抽出結果のリストと、料理検索結果に応じた
|
185 |
+
テキストボックスとボタンのリストのリスト
|
186 |
+
"""
|
187 |
+
classified_words: Dict[str, List[str]] = self._nlp.classify(classifying_text)
|
188 |
+
|
189 |
+
pos_tokens = [
|
190 |
+
(word, self._jp_label_dic[label])
|
191 |
+
for label, words in classified_words.items()
|
192 |
+
for word in words
|
193 |
+
]
|
194 |
+
|
195 |
+
cuisine_info_dics = self._cuisine_info_dics_maker.create(classified_words)
|
196 |
+
cuisine_infos = CuisineInfos.update(cuisine_info_dics)
|
197 |
+
|
198 |
+
return [pos_tokens] + cuisine_infos
|
199 |
+
|
200 |
+
|
201 |
+
class InputSamplesDataset(GrComponent):
|
202 |
+
"""
|
203 |
+
入力例のクラス
|
204 |
+
"""
|
205 |
+
def _create(self) -> gr.Dataset:
|
206 |
+
"""
|
207 |
+
コンポーネントの作成
|
208 |
+
|
209 |
+
Returns
|
210 |
+
-------
|
211 |
+
gr.Dataset
|
212 |
+
入力例のコンポーネント
|
213 |
+
"""
|
214 |
+
label = 'こんな風に聞いてみてください'
|
215 |
+
input_samples = [
|
216 |
+
'オオカミとムカデを使った肉料理を教えてください',
|
217 |
+
'野菜料理で仙豆を使用したものはありますか?',
|
218 |
+
'オールマイトの髪の毛を使った料理は?',
|
219 |
+
'仙台の、宿儺の指を使った、夏に食べられる肉料理',
|
220 |
+
'呪胎九相図が使われている料理を探しています'
|
221 |
+
]
|
222 |
+
|
223 |
+
comp = gr.Dataset(
|
224 |
+
label=label,
|
225 |
+
components=[gr.Textbox()],
|
226 |
+
samples=[[sample] for sample in input_samples]
|
227 |
+
)
|
228 |
+
|
229 |
+
return comp
|
230 |
+
|
231 |
+
@staticmethod
|
232 |
+
def selected(input: gr.SelectData) -> str:
|
233 |
+
"""
|
234 |
+
selectイベントリスナーの関数
|
235 |
+
|
236 |
+
Parameters
|
237 |
+
----------
|
238 |
+
input : gr.SelectData
|
239 |
+
_description_
|
240 |
+
|
241 |
+
Returns
|
242 |
+
-------
|
243 |
+
str
|
244 |
+
選択した入力例
|
245 |
+
"""
|
246 |
+
return input.value[0]
|
247 |
+
|
248 |
+
|
249 |
+
class ExtractedWordsHighlightedText(GrComponent):
|
250 |
+
"""
|
251 |
+
抽出結果のクラス
|
252 |
+
"""
|
253 |
+
def __init__(self, label_info_dics: Dict[str, str | List[str]]):
|
254 |
+
"""
|
255 |
+
コンストラクタ
|
256 |
+
|
257 |
+
Parameters
|
258 |
+
----------
|
259 |
+
label_info_dics : Dict[str, str | List[str]]
|
260 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
261 |
+
"""
|
262 |
+
super().__init__(label_info_dics)
|
263 |
+
|
264 |
+
def _create(
|
265 |
+
self, label_info_dics: Dict[str, str | List[str]]
|
266 |
+
) -> gr.HighlightedText:
|
267 |
+
"""
|
268 |
+
コンポーネントの作成
|
269 |
+
|
270 |
+
Parameters
|
271 |
+
----------
|
272 |
+
label_info_dics : Dict[str, str | List[str]]
|
273 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
274 |
+
|
275 |
+
Returns
|
276 |
+
-------
|
277 |
+
gr.HighlightedText
|
278 |
+
抽出結果のコンポーネント
|
279 |
+
"""
|
280 |
+
color_map: Dict[str, str] = {
|
281 |
+
dic['jp']: dic['color'] for dic in label_info_dics.values()
|
282 |
+
}
|
283 |
+
|
284 |
+
comp = gr.HighlightedText(
|
285 |
+
color_map=color_map,
|
286 |
+
combine_adjacent=True,
|
287 |
+
adjacent_separator='、',
|
288 |
+
label='検出語彙一覧'
|
289 |
+
)
|
290 |
+
|
291 |
+
return comp
|
292 |
+
|
293 |
+
|
294 |
+
class CuisineInfos(GrLayout):
|
295 |
+
"""
|
296 |
+
全検索結果のクラス
|
297 |
+
|
298 |
+
Attributes
|
299 |
+
----------
|
300 |
+
layout_type : gr.Column
|
301 |
+
GradioのColumn
|
302 |
+
"""
|
303 |
+
layout_type = gr.Column
|
304 |
+
|
305 |
+
def _create(self, cuisine_infos_num: int) -> List[CuisineInfo]:
|
306 |
+
"""
|
307 |
+
子要素の作成
|
308 |
+
|
309 |
+
Parameters
|
310 |
+
----------
|
311 |
+
cuisine_infos_num : int
|
312 |
+
表示する料理検索結果の最大数
|
313 |
+
|
314 |
+
Returns
|
315 |
+
-------
|
316 |
+
List[CuisineInfo]
|
317 |
+
全検索結果
|
318 |
+
"""
|
319 |
+
children = [CuisineInfo() for _ in range(cuisine_infos_num)]
|
320 |
+
|
321 |
+
return children
|
322 |
+
|
323 |
+
@staticmethod
|
324 |
+
def update(
|
325 |
+
cuisine_info_dics: List[Dict[str, str]]
|
326 |
+
) -> List[gr.Textbox | gr.Button]:
|
327 |
+
"""
|
328 |
+
全検索結果の更新
|
329 |
+
|
330 |
+
Parameters
|
331 |
+
----------
|
332 |
+
cuisine_info_dics : List[Dict[str, str]]
|
333 |
+
検索で見つかった料理の情報を持つ辞書のリスト
|
334 |
+
|
335 |
+
Returns
|
336 |
+
-------
|
337 |
+
List[gr.Textbox | gr.Button]
|
338 |
+
全料理の情���のテキストボックスと、詳細ページへのボタンのリスト
|
339 |
+
"""
|
340 |
+
cuisine_infos: List[gr.Textbox | gr.Button] = []
|
341 |
+
|
342 |
+
for cuisine_info_dic in cuisine_info_dics:
|
343 |
+
cuisine_info = CuisineInfo.update(cuisine_info_dic)
|
344 |
+
|
345 |
+
cuisine_infos.extend(cuisine_info)
|
346 |
+
|
347 |
+
return cuisine_infos
|
348 |
+
|
349 |
+
|
350 |
+
class CuisineInfo(GrLayout):
|
351 |
+
"""
|
352 |
+
料理の情報とURLボタンのクラス
|
353 |
+
|
354 |
+
Attributes
|
355 |
+
----------
|
356 |
+
layout_type : gr.Row
|
357 |
+
GradioのRow
|
358 |
+
"""
|
359 |
+
layout_type = gr.Row
|
360 |
+
|
361 |
+
def _create(self) -> List[InfoTextbox | UrlButton]:
|
362 |
+
"""
|
363 |
+
子要素の作成
|
364 |
+
|
365 |
+
Returns
|
366 |
+
-------
|
367 |
+
List[InfoTextbox | UrlButton]
|
368 |
+
料理の情報のテキストボックスと、詳細ページへのボタンのリスト
|
369 |
+
"""
|
370 |
+
info_textbox = InfoTextbox()
|
371 |
+
url_btn = UrlButton()
|
372 |
+
|
373 |
+
children = [info_textbox, url_btn]
|
374 |
+
|
375 |
+
return children
|
376 |
+
|
377 |
+
@staticmethod
|
378 |
+
def update(cuisine_info_dic: Dict[str, str]) -> List[gr.Textbox | gr.Button]:
|
379 |
+
"""
|
380 |
+
料理の情報とURLボタンの更新
|
381 |
+
|
382 |
+
Parameters
|
383 |
+
----------
|
384 |
+
cuisine_info_dic : Dict[str, str]
|
385 |
+
検索で見つかった料理の情報を持つ辞書
|
386 |
+
|
387 |
+
Returns
|
388 |
+
-------
|
389 |
+
List[gr.Textbox | gr.Button]
|
390 |
+
料理の情報のテキストボックスと、詳細ページへのボタンのリスト
|
391 |
+
"""
|
392 |
+
if cuisine_info_dic:
|
393 |
+
textbox, btn = CuisineInfo._reset(cuisine_info_dic)
|
394 |
+
|
395 |
+
else:
|
396 |
+
textbox, btn = CuisineInfo._hide()
|
397 |
+
|
398 |
+
return [textbox, btn]
|
399 |
+
|
400 |
+
@staticmethod
|
401 |
+
def _reset(cuisine_info_dic: Dict[str, str]) -> Tuple[gr.Textbox, gr.Button]:
|
402 |
+
"""
|
403 |
+
料理の変更
|
404 |
+
|
405 |
+
Parameters
|
406 |
+
----------
|
407 |
+
cuisine_info_dic : Dict[str, str]
|
408 |
+
検索で見つかった料理の情報を持つ辞書
|
409 |
+
|
410 |
+
Returns
|
411 |
+
-------
|
412 |
+
Tuple[gr.Textbox, gr.Button]
|
413 |
+
料理の情報のテキストボックスと、詳細ページへのボタンのタプル
|
414 |
+
"""
|
415 |
+
cuisine_name = cuisine_info_dic['name']
|
416 |
+
cuisine_info = cuisine_info_dic['info']
|
417 |
+
cuisine_url = cuisine_info_dic['url']
|
418 |
+
|
419 |
+
textbox = InfoTextbox.reset(cuisine_name, cuisine_info)
|
420 |
+
btn = UrlButton.reset(cuisine_name, cuisine_url)
|
421 |
+
|
422 |
+
return textbox, btn
|
423 |
+
|
424 |
+
@staticmethod
|
425 |
+
def _hide() -> Tuple[gr.Textbox, gr.Button]:
|
426 |
+
"""
|
427 |
+
料理の非表示
|
428 |
+
|
429 |
+
Returns
|
430 |
+
-------
|
431 |
+
Tuple[gr.Textbox, gr.Button]
|
432 |
+
料理の情報のテキストボックスと、詳細ページへのボタンのタプル
|
433 |
+
"""
|
434 |
+
textbox = InfoTextbox.hide()
|
435 |
+
btn = UrlButton.hide()
|
436 |
+
|
437 |
+
return textbox, btn
|
438 |
+
|
439 |
+
|
440 |
+
class InfoTextbox(GrComponent):
|
441 |
+
"""
|
442 |
+
料理の情報のクラス
|
443 |
+
"""
|
444 |
+
def _create(self) -> gr.Textbox:
|
445 |
+
"""
|
446 |
+
コンポーネントの作成
|
447 |
+
|
448 |
+
Returns
|
449 |
+
-------
|
450 |
+
gr.Textbox
|
451 |
+
料理の情報のコンポーネント
|
452 |
+
"""
|
453 |
+
comp = gr.Textbox(scale=9, visible=False)
|
454 |
+
|
455 |
+
return comp
|
456 |
+
|
457 |
+
@staticmethod
|
458 |
+
def reset(cuisine_name: str, cuisine_info: str) -> gr.Textbox:
|
459 |
+
"""
|
460 |
+
料理の情報の変更
|
461 |
+
|
462 |
+
Parameters
|
463 |
+
----------
|
464 |
+
cuisine_name : str
|
465 |
+
料理名
|
466 |
+
cuisine_info : str
|
467 |
+
料理の情報
|
468 |
+
|
469 |
+
Returns
|
470 |
+
-------
|
471 |
+
gr.Textbox
|
472 |
+
料理の情報のコンポーネント
|
473 |
+
"""
|
474 |
+
comp = gr.Textbox(value=cuisine_info, label=cuisine_name, visible=True)
|
475 |
+
|
476 |
+
return comp
|
477 |
+
|
478 |
+
@staticmethod
|
479 |
+
def hide() -> gr.Textbox:
|
480 |
+
"""
|
481 |
+
料理の情報の非表示
|
482 |
+
|
483 |
+
Returns
|
484 |
+
-------
|
485 |
+
gr.Textbox
|
486 |
+
非表示になった料理の情報のコンポーネント
|
487 |
+
"""
|
488 |
+
comp = gr.Textbox(visible=False)
|
489 |
+
|
490 |
+
return comp
|
491 |
+
|
492 |
+
|
493 |
+
class UrlButton(GrComponent):
|
494 |
+
"""
|
495 |
+
URLボタンのクラス
|
496 |
+
"""
|
497 |
+
def _create(self) -> gr.Button:
|
498 |
+
"""
|
499 |
+
コンポーネントの作成
|
500 |
+
|
501 |
+
Returns
|
502 |
+
-------
|
503 |
+
gr.Button
|
504 |
+
詳細ページへのボタンのコンポーネント
|
505 |
+
"""
|
506 |
+
comp = gr.Button(scale=1, visible=False)
|
507 |
+
|
508 |
+
return comp
|
509 |
+
|
510 |
+
@staticmethod
|
511 |
+
def reset(cuisine_name: str, cuisine_url: str) -> gr.Button:
|
512 |
+
"""
|
513 |
+
料理のボタンの更新
|
514 |
+
|
515 |
+
Parameters
|
516 |
+
----------
|
517 |
+
cuisine_name : str
|
518 |
+
料理名
|
519 |
+
cuisine_url : str
|
520 |
+
料理の詳細ページへのURL
|
521 |
+
|
522 |
+
Returns
|
523 |
+
-------
|
524 |
+
gr.Button
|
525 |
+
詳細ページへのボタンのコンポーネント
|
526 |
+
"""
|
527 |
+
value = cuisine_name + '\n詳細ページ'
|
528 |
+
comp = gr.Button(value=value, link=cuisine_url, visible=True)
|
529 |
+
|
530 |
+
return comp
|
531 |
+
|
532 |
+
@staticmethod
|
533 |
+
def hide() -> gr.Button:
|
534 |
+
"""
|
535 |
+
料理のボタンの非表示
|
536 |
+
|
537 |
+
Returns
|
538 |
+
-------
|
539 |
+
gr.Button
|
540 |
+
非表示になった詳細ページへのボタンのコンポーネント
|
541 |
+
"""
|
542 |
+
comp = gr.Button(visible=False)
|
543 |
+
|
544 |
+
return comp
|
545 |
+
|
546 |
+
|
547 |
+
class CuisineInfoDictionariesMaker:
|
548 |
+
"""
|
549 |
+
料理検索結果の辞書のリスト作成用クラス
|
550 |
+
|
551 |
+
Attributes
|
552 |
+
----------
|
553 |
+
_cuisine_searcher : CuisineSearcher
|
554 |
+
料理を検索するオブジェクト
|
555 |
+
_word_unifier : WordUnifier
|
556 |
+
抽出結果の表記ゆれを統一するオブジェクト
|
557 |
+
"""
|
558 |
+
def __init__(
|
559 |
+
self,
|
560 |
+
cuisine_df_path: str,
|
561 |
+
unify_dics_path: str,
|
562 |
+
label_info_dics: Dict[str, str | List[str]],
|
563 |
+
cuisine_infos_num: int
|
564 |
+
):
|
565 |
+
"""
|
566 |
+
コンストラクタ
|
567 |
+
|
568 |
+
Parameters
|
569 |
+
----------
|
570 |
+
cuisine_df_path : str
|
571 |
+
料理のデータフレームが保存されているパス
|
572 |
+
unify_dics_path : str
|
573 |
+
表記ゆれ統一用辞書が保存されているパス
|
574 |
+
label_info_dics : Dict[str, str | List[str]]
|
575 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
576 |
+
cuisine_infos_num : int
|
577 |
+
表示する料理検索結果の最大数
|
578 |
+
"""
|
579 |
+
self._cuisine_searcher = CuisineSearcher(
|
580 |
+
cuisine_df_path, label_info_dics, cuisine_infos_num
|
581 |
+
)
|
582 |
+
self._word_unifier = WordUnifier(unify_dics_path)
|
583 |
+
|
584 |
+
def create(
|
585 |
+
self, classified_words: Dict[str, List[str]]
|
586 |
+
) -> List[Dict[str, str]]:
|
587 |
+
"""
|
588 |
+
料理検索結果の辞書の作成
|
589 |
+
|
590 |
+
Parameters
|
591 |
+
----------
|
592 |
+
classified_words : Dict[str, List[str]]
|
593 |
+
ラベルと、そのラベルに分類された固有表現の辞書
|
594 |
+
|
595 |
+
Returns
|
596 |
+
-------
|
597 |
+
List[Dict[str, str]]
|
598 |
+
料理検索結果の辞書のリスト
|
599 |
+
"""
|
600 |
+
unified_words = self._word_unifier.unify(classified_words)
|
601 |
+
cuisine_info_dics = self._cuisine_searcher.search(unified_words)
|
602 |
+
|
603 |
+
return cuisine_info_dics
|
604 |
+
|
605 |
+
|
606 |
+
class CuisineSearcher:
|
607 |
+
"""
|
608 |
+
料理検索用のクラス
|
609 |
+
|
610 |
+
Attributes
|
611 |
+
----------
|
612 |
+
_search_infos : List[str]
|
613 |
+
料理のどの情報を取ってくるか示したリスト
|
614 |
+
_df : pd.DataFrame
|
615 |
+
料理のデータフレーム
|
616 |
+
_label_to_col : Dict[str, List[str]]
|
617 |
+
固有表現のラベルに対して、検索するデータフレームの列のリストの辞書
|
618 |
+
_words_dic : Dict[str, List[str]]
|
619 |
+
データフレームの列と、列に含まれる全ての要素の辞書
|
620 |
+
_cuisine_infos_num : int
|
621 |
+
表示する料理検索結果の最大数
|
622 |
+
"""
|
623 |
+
_search_infos = [
|
624 |
+
'Name', 'Prefecture', 'Types', 'Seasons', 'Ingredients', 'Detail URL'
|
625 |
+
]
|
626 |
+
|
627 |
+
def __init__(
|
628 |
+
self,
|
629 |
+
cuisine_df_path: str,
|
630 |
+
label_info_dics: Dict[str, str | List[str]],
|
631 |
+
cuisine_infos_num: int
|
632 |
+
):
|
633 |
+
"""
|
634 |
+
コンストラクタ
|
635 |
+
|
636 |
+
Parameters
|
637 |
+
----------
|
638 |
+
cuisine_df_path : str
|
639 |
+
料理のデータフレームが保存されているパス
|
640 |
+
label_info_dics : Dict[str, str | List[str]]
|
641 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
642 |
+
cuisine_infos_num : int
|
643 |
+
表示する料理検索結果の最大数
|
644 |
+
"""
|
645 |
+
self._df = read_csv_df(cuisine_df_path)
|
646 |
+
self._label_to_col = self._create_label_to_col(label_info_dics)
|
647 |
+
self._words_dic = {
|
648 |
+
col: self._find_words(col)
|
649 |
+
for cols in self._label_to_col.values() for col in cols
|
650 |
+
}
|
651 |
+
self._cuisine_infos_num = cuisine_infos_num
|
652 |
+
|
653 |
+
def _create_label_to_col(
|
654 |
+
self, label_info_dics: Dict[str, str | List[str]]
|
655 |
+
) -> Dict[str, List[str]]:
|
656 |
+
"""
|
657 |
+
label_to_colの作成
|
658 |
+
|
659 |
+
固有表現のラベルに対応したデータフレームの列を
|
660 |
+
特定するための辞書を作成する
|
661 |
+
|
662 |
+
Parameters
|
663 |
+
----------
|
664 |
+
label_info_dics : Dict[str, str | List[str]]
|
665 |
+
固有表現のラベルとラベルに対する各種設定情報の辞書
|
666 |
+
|
667 |
+
Returns
|
668 |
+
-------
|
669 |
+
Dict[str, List[str]]
|
670 |
+
固有表現のラベルに対して、検索するデータフレームの列のリストの辞書
|
671 |
+
|
672 |
+
Raises
|
673 |
+
------
|
674 |
+
ValueError
|
675 |
+
label_info_dicsに、データフレームに存在しない列名が含まれている場合
|
676 |
+
"""
|
677 |
+
label_to_col: Dict[str, List[str]] = {
|
678 |
+
label: dic['df_cols'] for label, dic in label_info_dics.items()
|
679 |
+
}
|
680 |
+
|
681 |
+
df_cols = self._df.columns.tolist()
|
682 |
+
for cols in label_to_col.values():
|
683 |
+
for col in cols:
|
684 |
+
if col not in df_cols:
|
685 |
+
raise ValueError(f'"{col}"という列名は存在しません')
|
686 |
+
|
687 |
+
return label_to_col
|
688 |
+
|
689 |
+
def _find_words(self, col: str) -> List[str]:
|
690 |
+
"""
|
691 |
+
列に含まれる全要素の取得
|
692 |
+
|
693 |
+
Parameters
|
694 |
+
----------
|
695 |
+
col : str
|
696 |
+
列名
|
697 |
+
|
698 |
+
Returns
|
699 |
+
-------
|
700 |
+
List[str]
|
701 |
+
列に含まれる全ての要素のリスト
|
702 |
+
"""
|
703 |
+
words: List[str, List[str]] = self._df[col].value_counts().index.tolist()
|
704 |
+
|
705 |
+
if isinstance(words[0], list):
|
706 |
+
words_lst = words
|
707 |
+
unique_words: List[str] = []
|
708 |
+
|
709 |
+
for words in words_lst:
|
710 |
+
for word in words:
|
711 |
+
if word not in unique_words:
|
712 |
+
unique_words.append(word)
|
713 |
+
|
714 |
+
return unique_words
|
715 |
+
|
716 |
+
return words
|
717 |
+
|
718 |
+
def search(self, unified_words: Dict[str, List[str]]) -> List[Dict[str, str]]:
|
719 |
+
"""
|
720 |
+
料理の検索
|
721 |
+
|
722 |
+
Parameters
|
723 |
+
----------
|
724 |
+
unified_words : Dict[str, List[str]]
|
725 |
+
表記ゆれが統一された固有表現の辞書
|
726 |
+
|
727 |
+
Returns
|
728 |
+
-------
|
729 |
+
List[Dict[str, str]]
|
730 |
+
検索結果の料理の情報を持つ辞書のリスト
|
731 |
+
"""
|
732 |
+
on_df_words_dic = self._create_on_df_words_dic(unified_words)
|
733 |
+
|
734 |
+
if not on_df_words_dic:
|
735 |
+
gr.Info('いずれの語彙もデータに存在しませんでした')
|
736 |
+
|
737 |
+
return self._create_empty_dics()
|
738 |
+
|
739 |
+
cuisine_info_dics = self._create_cuisine_info_dics(on_df_words_dic)
|
740 |
+
|
741 |
+
return cuisine_info_dics
|
742 |
+
|
743 |
+
def _create_on_df_words_dic(
|
744 |
+
self, unified_words: Dict[str, List[str]]
|
745 |
+
) -> Dict[str, List[str]]:
|
746 |
+
"""
|
747 |
+
データフレームに存在する固有表現だけの辞書の作成
|
748 |
+
|
749 |
+
Parameters
|
750 |
+
----------
|
751 |
+
unified_words : Dict[str, List[str]]
|
752 |
+
表記ゆれが統一された固有表現の辞書
|
753 |
+
|
754 |
+
Returns
|
755 |
+
-------
|
756 |
+
Dict[str, List[str]]
|
757 |
+
データフレームに存在する表記ゆれが統一された固有表現の辞書
|
758 |
+
"""
|
759 |
+
on_df_words_dic = {col: [] for col in self._words_dic}
|
760 |
+
not_on_df_words: List[str] = []
|
761 |
+
|
762 |
+
for label, words in unified_words.items():
|
763 |
+
search_cols = self._label_to_col[label]
|
764 |
+
|
765 |
+
for word in words:
|
766 |
+
not_on_df = True
|
767 |
+
|
768 |
+
for col in search_cols:
|
769 |
+
if word in self._words_dic[col]:
|
770 |
+
on_df_words_dic[col].append(word)
|
771 |
+
|
772 |
+
not_on_df = False
|
773 |
+
|
774 |
+
break
|
775 |
+
|
776 |
+
if not_on_df:
|
777 |
+
not_on_df_words.append(word)
|
778 |
+
|
779 |
+
if not_on_df_words:
|
780 |
+
CuisineSearcher._show_not_on_df_words(not_on_df_words)
|
781 |
+
|
782 |
+
on_df_words_dic = {
|
783 |
+
col: words for col, words in on_df_words_dic.items() if words
|
784 |
+
}
|
785 |
+
|
786 |
+
return on_df_words_dic
|
787 |
+
|
788 |
+
@staticmethod
|
789 |
+
def _show_not_on_df_words(not_on_df_words: List[str]) -> None:
|
790 |
+
"""
|
791 |
+
データフレームに存在しなかった固有表現の表示
|
792 |
+
|
793 |
+
Parameters
|
794 |
+
----------
|
795 |
+
not_on_df_words : List[str]
|
796 |
+
データフレームに存在しなかった固有表現のリスト
|
797 |
+
"""
|
798 |
+
words = '、'.join(not_on_df_words)
|
799 |
+
message = f'無効な語彙: {words}'
|
800 |
+
|
801 |
+
gr.Info(message)
|
802 |
+
|
803 |
+
def _create_empty_dics(self) -> List[Dict[Any, Any]]:
|
804 |
+
"""
|
805 |
+
空の辞書のリストの作成
|
806 |
+
|
807 |
+
検索結果に該当する料理がなかった場合は、CuisineInfosを非表示にする
|
808 |
+
CuisineInfo.update()に空の辞書を渡すと、
|
809 |
+
InfoTextboxとUrlButtonが非表示になる
|
810 |
+
|
811 |
+
Returns
|
812 |
+
-------
|
813 |
+
List[Dict[Any, Any]]
|
814 |
+
空の辞書のリスト
|
815 |
+
"""
|
816 |
+
return [{} for _ in range(self._cuisine_infos_num)]
|
817 |
+
|
818 |
+
def _create_cuisine_info_dics(
|
819 |
+
self, words_dic: Dict[str, List[str]]
|
820 |
+
) -> List[Dict[str, str]]:
|
821 |
+
"""
|
822 |
+
料理の情報を持つ辞書の作成
|
823 |
+
|
824 |
+
Parameters
|
825 |
+
----------
|
826 |
+
words_dic : Dict[str, List[str]]
|
827 |
+
検索ワードのリストを持つ辞書
|
828 |
+
|
829 |
+
Returns
|
830 |
+
-------
|
831 |
+
List[Dict[str, str]]
|
832 |
+
料理の情報を持つ辞書のリスト
|
833 |
+
"""
|
834 |
+
condition_lst: List[pd.Series] = []
|
835 |
+
|
836 |
+
for col, words in words_dic.items():
|
837 |
+
condition = self._create_condition(col, words)
|
838 |
+
condition_lst.append(condition)
|
839 |
+
|
840 |
+
conditions = reduce(operator.and_, condition_lst)
|
841 |
+
|
842 |
+
cuisine_infos_lst = self._df.loc[conditions, self._search_infos].values.tolist()
|
843 |
+
|
844 |
+
if len(cuisine_infos_lst) > self._cuisine_infos_num:
|
845 |
+
cuisine_infos_lst = cuisine_infos_lst[:self._cuisine_infos_num]
|
846 |
+
|
847 |
+
if not cuisine_infos_lst:
|
848 |
+
gr.Info('検索条件が厳しすぎて、該当料理が見つかりませんでした')
|
849 |
+
|
850 |
+
return self._create_empty_dics()
|
851 |
+
|
852 |
+
cuisine_info_dics = self._lst_to_dics(cuisine_infos_lst)
|
853 |
+
|
854 |
+
return cuisine_info_dics
|
855 |
+
|
856 |
+
def _create_condition(self, col: str, words: List[str]) -> pd.Series:
|
857 |
+
"""
|
858 |
+
検索条件の作成
|
859 |
+
|
860 |
+
Parameters
|
861 |
+
----------
|
862 |
+
col : str
|
863 |
+
絞り込み対象列
|
864 |
+
words : List[str]
|
865 |
+
検索ワード
|
866 |
+
|
867 |
+
Returns
|
868 |
+
-------
|
869 |
+
pd.Series
|
870 |
+
該当料理の行がTrueになったboolのシリーズ
|
871 |
+
"""
|
872 |
+
value_type = type(self._df.at[0, col])
|
873 |
+
|
874 |
+
if value_type is list:
|
875 |
+
condition = self._df[col].apply(
|
876 |
+
lambda values: any(word in values for word in words)
|
877 |
+
)
|
878 |
+
|
879 |
+
else:
|
880 |
+
conditions = [self._df[col] == word for word in words]
|
881 |
+
condition = reduce(operator.or_, conditions)
|
882 |
+
|
883 |
+
return condition
|
884 |
+
|
885 |
+
def _lst_to_dics(
|
886 |
+
self, infos_lst: List[List[str | List[str]]]
|
887 |
+
) -> List[Dict[str, str]]:
|
888 |
+
"""
|
889 |
+
リストから辞書への変換
|
890 |
+
|
891 |
+
料理の情報のリストのリストを、料理の情報の辞書のリストに変換する
|
892 |
+
|
893 |
+
Parameters
|
894 |
+
----------
|
895 |
+
infos_lst : List[List[str | List[str]]]
|
896 |
+
料理の情報のリストのリスト
|
897 |
+
|
898 |
+
Returns
|
899 |
+
-------
|
900 |
+
List[Dict[str, str]]
|
901 |
+
料理の情報の辞書のリスト
|
902 |
+
"""
|
903 |
+
dics: List[Dict[str, str]] = []
|
904 |
+
|
905 |
+
for infos in infos_lst:
|
906 |
+
infos = [
|
907 |
+
'、'.join(info) if isinstance(info, list) else info
|
908 |
+
for info in infos
|
909 |
+
]
|
910 |
+
|
911 |
+
name = infos[0]
|
912 |
+
info = ' | '.join(infos[1:-1])
|
913 |
+
url = infos[-1]
|
914 |
+
|
915 |
+
dic = {'name': name, 'info': info, 'url': url}
|
916 |
+
dics.append(dic)
|
917 |
+
|
918 |
+
dics_len = len(dics)
|
919 |
+
|
920 |
+
if dics_len < self._cuisine_infos_num:
|
921 |
+
dics = dics + [{} for _ in range(self._cuisine_infos_num - dics_len)]
|
922 |
+
|
923 |
+
return dics
|
924 |
+
|
925 |
+
|
926 |
+
class WordUnifier:
|
927 |
+
"""
|
928 |
+
表記ゆれ統一用のクラス
|
929 |
+
|
930 |
+
Attributes
|
931 |
+
----------
|
932 |
+
_not_unify_labels : List[str]
|
933 |
+
表記ゆれ統一対象ではない固有表現のラベルのリスト
|
934 |
+
_unify_dics : Dict[str, Dict[str, str]]
|
935 |
+
ラベルと、そのラベルの固有表現の表記ゆれ統一用の辞書の辞書
|
936 |
+
"""
|
937 |
+
_not_unify_labels = ['SZN']
|
938 |
+
|
939 |
+
def __init__(self, unify_dics_path: str):
|
940 |
+
"""
|
941 |
+
コンストラクタ
|
942 |
+
|
943 |
+
Parameters
|
944 |
+
----------
|
945 |
+
unify_dics_path : str
|
946 |
+
表記ゆれ統一用辞書が保存されているパス
|
947 |
+
"""
|
948 |
+
self._unify_dics: Dict[str, Dict[str, str]] = load_json_obj(unify_dics_path)
|
949 |
+
|
950 |
+
def unify(
|
951 |
+
self, classified_words: Dict[str, List[str]]
|
952 |
+
) -> Dict[str, List[str]]:
|
953 |
+
"""
|
954 |
+
表記ゆれの統一
|
955 |
+
|
956 |
+
Parameters
|
957 |
+
----------
|
958 |
+
classified_words : Dict[str, List[str]]
|
959 |
+
ラベルと、そのラベルに分類された固有表現の辞書
|
960 |
+
|
961 |
+
Returns
|
962 |
+
-------
|
963 |
+
Dict[str, List[str]]
|
964 |
+
表記ゆれが統一された固有表現の辞書
|
965 |
+
"""
|
966 |
+
for label, words in classified_words.items():
|
967 |
+
if label in self._not_unify_labels:
|
968 |
+
continue
|
969 |
+
|
970 |
+
unify_dic = self._unify_dics[label]
|
971 |
+
|
972 |
+
unified_words = [
|
973 |
+
unify_dic[word] if word in unify_dic else word for word in words
|
974 |
+
]
|
975 |
+
|
976 |
+
classified_words[label] = unified_words
|
977 |
+
|
978 |
+
return classified_words
|
979 |
+
|
980 |
+
|
981 |
+
model_name = 'wolf4032/bert-japanese-token-classification-search-local-cuisine'
|
982 |
+
cuisine_df_path = 'local_cuisine_dataframe.csv'
|
983 |
+
unify_dics_path = 'unifying_dictionaries.json'
|
984 |
+
|
985 |
+
app = App.create_and_launch(model_name, cuisine_df_path, unify_dics_path)
|