tmplot 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tmplot-0.1.2/src/tmplot.egg-info → tmplot-0.1.3}/PKG-INFO +7 -1
- {tmplot-0.1.2 → tmplot-0.1.3}/README.md +6 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/__init__.py +1 -1
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/_helpers.py +89 -81
- tmplot-0.1.3/src/tmplot/_report.py +334 -0
- {tmplot-0.1.2 → tmplot-0.1.3/src/tmplot.egg-info}/PKG-INFO +7 -1
- tmplot-0.1.2/src/tmplot/_report.py +0 -309
- {tmplot-0.1.2 → tmplot-0.1.3}/LICENSE +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/MANIFEST.in +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/pyproject.toml +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/setup.cfg +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/_distance.py +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/_metrics.py +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/_stability.py +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot/_vis.py +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot.egg-info/SOURCES.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot.egg-info/dependency_links.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot.egg-info/requires.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.1.3}/src/tmplot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tmplot
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Visualization of Topic Modeling Results
|
|
5
5
|
Author-email: Maksim Terpilovskii <maximtrp@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -96,6 +96,12 @@ Requires-Dist: bitermplus; extra == "models"
|
|
|
96
96
|
- LocallyLinearEmbedding
|
|
97
97
|
- Isomap
|
|
98
98
|
|
|
99
|
+
## Donate
|
|
100
|
+
|
|
101
|
+
If you find this package useful, please consider donating any amount of money. This will help me spend more time on supporting open-source software.
|
|
102
|
+
|
|
103
|
+
<a href="https://www.buymeacoffee.com/maximtrp" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
104
|
+
|
|
99
105
|
## Installation
|
|
100
106
|
|
|
101
107
|
The package can be installed from PyPi:
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
- LocallyLinearEmbedding
|
|
39
39
|
- Isomap
|
|
40
40
|
|
|
41
|
+
## Donate
|
|
42
|
+
|
|
43
|
+
If you find this package useful, please consider donating any amount of money. This will help me spend more time on supporting open-source software.
|
|
44
|
+
|
|
45
|
+
<a href="https://www.buymeacoffee.com/maximtrp" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
46
|
+
|
|
41
47
|
## Installation
|
|
42
48
|
|
|
43
49
|
The package can be installed from PyPi:
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
__all__ = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"get_phi",
|
|
3
|
+
"get_theta",
|
|
4
|
+
"get_relevant_terms",
|
|
5
|
+
"get_salient_terms",
|
|
6
|
+
"get_docs",
|
|
7
|
+
"get_top_docs",
|
|
8
|
+
"calc_terms_marg_probs",
|
|
9
|
+
"calc_topics_marg_probs",
|
|
10
|
+
"calc_terms_probs_ratio",
|
|
11
|
+
]
|
|
7
12
|
from warnings import warn
|
|
8
13
|
from importlib.util import find_spec
|
|
9
14
|
from typing import Union, Optional, Sequence, List
|
|
@@ -13,7 +18,7 @@ from numpy import ndarray, zeros, argsort, array, arange, vstack
|
|
|
13
18
|
from numpy import log as nplog
|
|
14
19
|
from pandas import concat, Series, DataFrame
|
|
15
20
|
|
|
16
|
-
tomotopy_installed = find_spec(
|
|
21
|
+
tomotopy_installed = find_spec("tomotopy")
|
|
17
22
|
if tomotopy_installed:
|
|
18
23
|
from tomotopy import (
|
|
19
24
|
LDAModel as tomotopyLDA,
|
|
@@ -23,14 +28,15 @@ if tomotopy_installed:
|
|
|
23
28
|
HDPModel as tomotopyHDP,
|
|
24
29
|
PTModel as tomotopyPT,
|
|
25
30
|
SLDAModel as tomotopySLDA,
|
|
26
|
-
GDMRModel as tomotopyGDMR
|
|
31
|
+
GDMRModel as tomotopyGDMR,
|
|
32
|
+
)
|
|
27
33
|
|
|
28
|
-
gensim_installed = find_spec(
|
|
34
|
+
gensim_installed = find_spec("gensim")
|
|
29
35
|
if gensim_installed:
|
|
30
36
|
from gensim.models.ldamodel import LdaModel as gensimLDA
|
|
31
37
|
from gensim.models.ldamulticore import LdaMulticore as gensimLDAMC
|
|
32
38
|
|
|
33
|
-
bitermplus_installed = find_spec(
|
|
39
|
+
bitermplus_installed = find_spec("bitermplus")
|
|
34
40
|
if bitermplus_installed:
|
|
35
41
|
from bitermplus._btm import BTM
|
|
36
42
|
|
|
@@ -38,12 +44,11 @@ if bitermplus_installed:
|
|
|
38
44
|
def __warn_package_installation(package_name: str):
|
|
39
45
|
warn(
|
|
40
46
|
f'Please install "{package_name}" package to analyze its models.\n'
|
|
41
|
-
f
|
|
47
|
+
f"Run `pip install {package_name}` in the console."
|
|
48
|
+
)
|
|
42
49
|
|
|
43
50
|
|
|
44
|
-
def get_phi(
|
|
45
|
-
model: object,
|
|
46
|
-
vocabulary: Optional[Sequence] = None) -> DataFrame:
|
|
51
|
+
def get_phi(model: object, vocabulary: Optional[Sequence] = None) -> DataFrame:
|
|
47
52
|
"""Get words vs topics matrix (phi).
|
|
48
53
|
|
|
49
54
|
Returns ``phi`` matrix of shape W x T, where W is the number of words,
|
|
@@ -65,7 +70,6 @@ def get_phi(
|
|
|
65
70
|
phi = None
|
|
66
71
|
|
|
67
72
|
if _is_tomotopy(model):
|
|
68
|
-
|
|
69
73
|
# Topics vs words distributions
|
|
70
74
|
twd = list(map(model.get_topic_word_dist, range(model.k)))
|
|
71
75
|
|
|
@@ -76,7 +80,6 @@ def get_phi(
|
|
|
76
80
|
phi.index = list(model.used_vocabs)
|
|
77
81
|
|
|
78
82
|
elif _is_gensim(model):
|
|
79
|
-
|
|
80
83
|
phi = DataFrame(model.get_topics().T)
|
|
81
84
|
if vocabulary:
|
|
82
85
|
phi.index = vocabulary
|
|
@@ -85,8 +88,8 @@ def get_phi(
|
|
|
85
88
|
phi = model.df_words_topics_
|
|
86
89
|
|
|
87
90
|
if isinstance(phi, DataFrame):
|
|
88
|
-
phi.index.name =
|
|
89
|
-
phi.columns.name =
|
|
91
|
+
phi.index.name = "words"
|
|
92
|
+
phi.columns.name = "topics"
|
|
90
93
|
|
|
91
94
|
return phi
|
|
92
95
|
|
|
@@ -94,8 +97,15 @@ def get_phi(
|
|
|
94
97
|
def _is_tomotopy(model: object) -> bool:
|
|
95
98
|
if tomotopy_installed:
|
|
96
99
|
tomotopy_models = [
|
|
97
|
-
tomotopyLDA,
|
|
98
|
-
|
|
100
|
+
tomotopyLDA,
|
|
101
|
+
tomotopyLLDA,
|
|
102
|
+
tomotopyCT,
|
|
103
|
+
tomotopyDMR,
|
|
104
|
+
tomotopyHDP,
|
|
105
|
+
tomotopyPT,
|
|
106
|
+
tomotopySLDA,
|
|
107
|
+
tomotopyGDMR,
|
|
108
|
+
]
|
|
99
109
|
return any(map(partial(isinstance, model), tomotopy_models))
|
|
100
110
|
|
|
101
111
|
__warn_package_installation("tomotopy")
|
|
@@ -119,9 +129,7 @@ def _is_btmplus(model: object) -> bool:
|
|
|
119
129
|
return False
|
|
120
130
|
|
|
121
131
|
|
|
122
|
-
def get_theta(
|
|
123
|
-
model: object,
|
|
124
|
-
corpus: Optional[List] = None) -> DataFrame:
|
|
132
|
+
def get_theta(model: object, corpus: Optional[List] = None) -> Optional[DataFrame]:
|
|
125
133
|
"""Get topics vs documents (theta) matrix.
|
|
126
134
|
|
|
127
135
|
Returns theta matrix of shape T x D, where T is the number of topics,
|
|
@@ -132,7 +140,7 @@ def get_theta(
|
|
|
132
140
|
model : object
|
|
133
141
|
Topic model instance.
|
|
134
142
|
corpus : Optional[List], optional
|
|
135
|
-
Corpus.
|
|
143
|
+
Corpus (must be specified for a `gensim` model).
|
|
136
144
|
|
|
137
145
|
Returns
|
|
138
146
|
-------
|
|
@@ -147,8 +155,7 @@ def get_theta(
|
|
|
147
155
|
|
|
148
156
|
elif _is_gensim(model):
|
|
149
157
|
if corpus is None:
|
|
150
|
-
raise ValueError(
|
|
151
|
-
'`corpus` must be supplied for a gensim model')
|
|
158
|
+
raise ValueError("`corpus` must be supplied for a gensim model")
|
|
152
159
|
tdd = list(map(model.get_document_topics, corpus))
|
|
153
160
|
theta = DataFrame(zeros((len(tdd), model.num_topics)))
|
|
154
161
|
for doc_id, doc_topic in enumerate(tdd):
|
|
@@ -160,14 +167,13 @@ def get_theta(
|
|
|
160
167
|
theta = DataFrame(model.matrix_topics_docs_)
|
|
161
168
|
|
|
162
169
|
if isinstance(theta, DataFrame):
|
|
163
|
-
theta.index.name =
|
|
164
|
-
theta.columns.name =
|
|
170
|
+
theta.index.name = "topics"
|
|
171
|
+
theta.columns.name = "docs"
|
|
165
172
|
|
|
166
173
|
return theta
|
|
167
174
|
|
|
168
175
|
|
|
169
|
-
def get_docs(
|
|
170
|
-
model: object) -> List[str]:
|
|
176
|
+
def get_docs(model: object) -> Optional[List[str]]:
|
|
171
177
|
"""Retrieve documents from topic model object.
|
|
172
178
|
|
|
173
179
|
Parameters
|
|
@@ -183,19 +189,19 @@ def get_docs(
|
|
|
183
189
|
if _is_tomotopy(model):
|
|
184
190
|
docs_raw = map(lambda x: x.words, model.docs)
|
|
185
191
|
return list(
|
|
186
|
-
map(
|
|
187
|
-
|
|
188
|
-
docs_raw))
|
|
192
|
+
map(lambda doc: " ".join(map(lambda x: model.vocabs[x], doc)), docs_raw)
|
|
193
|
+
)
|
|
189
194
|
return None
|
|
190
195
|
|
|
191
196
|
|
|
192
197
|
def get_top_docs(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
docs: Sequence[str],
|
|
199
|
+
model: object = None,
|
|
200
|
+
theta: Optional[ndarray] = None,
|
|
201
|
+
corpus: Optional[List] = None,
|
|
202
|
+
docs_num: int = 5,
|
|
203
|
+
topics: Optional[Sequence[int]] = None,
|
|
204
|
+
) -> DataFrame:
|
|
199
205
|
"""Get top documents for all (or a selected) topic.
|
|
200
206
|
|
|
201
207
|
Parameters
|
|
@@ -231,20 +237,19 @@ def get_top_docs(
|
|
|
231
237
|
|
|
232
238
|
def _select_docs(docs, theta, topic_id: int):
|
|
233
239
|
probs = theta[topic_id, :]
|
|
234
|
-
idx = argsort(probs)[
|
|
240
|
+
idx = argsort(probs)[: -docs_num - 1 : -1]
|
|
235
241
|
result = Series(list(map(lambda x: docs[x], idx)))
|
|
236
|
-
result.name = f
|
|
242
|
+
result.name = f"topic{topic_id}"
|
|
237
243
|
return result
|
|
238
244
|
|
|
239
245
|
topics_num = theta.shape[0]
|
|
240
246
|
topics_idx = arange(topics_num) if topics is None else topics
|
|
241
|
-
return concat(
|
|
242
|
-
map(lambda x: _select_docs(docs, theta, x), topics_idx), axis=1)
|
|
247
|
+
return concat(map(lambda x: _select_docs(docs, theta, x), topics_idx), axis=1)
|
|
243
248
|
|
|
244
249
|
|
|
245
250
|
def calc_topics_marg_probs(
|
|
246
|
-
|
|
247
|
-
|
|
251
|
+
theta: Union[DataFrame, ndarray], topic_id: Optional[int] = None
|
|
252
|
+
) -> Union[DataFrame, ndarray]:
|
|
248
253
|
"""Calculate marginal topics probabilities.
|
|
249
254
|
|
|
250
255
|
Parameters
|
|
@@ -269,8 +274,8 @@ def calc_topics_marg_probs(
|
|
|
269
274
|
|
|
270
275
|
|
|
271
276
|
def calc_terms_marg_probs(
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
phi: Union[ndarray, DataFrame], word_id: Optional[int] = None
|
|
278
|
+
) -> Union[ndarray, Series]:
|
|
274
279
|
"""Calculate marginal terms probabilities.
|
|
275
280
|
|
|
276
281
|
Parameters
|
|
@@ -294,10 +299,7 @@ def calc_terms_marg_probs(
|
|
|
294
299
|
return phi.sum(axis=1)
|
|
295
300
|
|
|
296
301
|
|
|
297
|
-
def get_salient_terms(
|
|
298
|
-
terms_freqs: ndarray,
|
|
299
|
-
phi: ndarray,
|
|
300
|
-
theta: ndarray) -> ndarray:
|
|
302
|
+
def get_salient_terms(terms_freqs: ndarray, phi: ndarray, theta: ndarray) -> ndarray:
|
|
301
303
|
"""Get salient terms.
|
|
302
304
|
|
|
303
305
|
Calculated as:
|
|
@@ -324,12 +326,18 @@ def get_salient_terms(
|
|
|
324
326
|
def _p_tw(phi, w, t):
|
|
325
327
|
return phi[w, t] * p_t[t] / p_w[w]
|
|
326
328
|
|
|
327
|
-
saliency = array(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
saliency = array(
|
|
330
|
+
(
|
|
331
|
+
terms_freqs[w]
|
|
332
|
+
* sum(
|
|
333
|
+
(
|
|
334
|
+
_p_tw(phi, w, t) * log(_p_tw(phi, w, t) / p_t[t])
|
|
335
|
+
for t in range(phi.shape[1])
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
for w in range(phi.shape[0])
|
|
339
|
+
)
|
|
340
|
+
)
|
|
333
341
|
# saliency(term w) = frequency(w)
|
|
334
342
|
# * [sum_t p(t | w) * log(p(t | w)/p(t))] for topics t
|
|
335
343
|
# p(t | w) = p(w | t) * p(t) / p(w)
|
|
@@ -337,10 +345,8 @@ def get_salient_terms(
|
|
|
337
345
|
|
|
338
346
|
|
|
339
347
|
def calc_terms_probs_ratio(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
terms_num: int = 30,
|
|
343
|
-
lambda_: float = 0.6) -> DataFrame:
|
|
348
|
+
phi: DataFrame, topic: int, terms_num: int = 30, lambda_: float = 0.6
|
|
349
|
+
) -> DataFrame:
|
|
344
350
|
"""Get terms conditional and marginal probabilities.
|
|
345
351
|
|
|
346
352
|
Parameters
|
|
@@ -368,35 +374,38 @@ def calc_terms_probs_ratio(
|
|
|
368
374
|
pandas.DataFrame
|
|
369
375
|
Words conditional and marginal probabilities.
|
|
370
376
|
"""
|
|
371
|
-
p_cond_name =
|
|
372
|
-
p_cond =
|
|
373
|
-
.rename(p_cond_name)
|
|
374
|
-
if isinstance(phi, DataFrame)
|
|
377
|
+
p_cond_name = "Conditional term probability, p(w | t)"
|
|
378
|
+
p_cond = (
|
|
379
|
+
phi.iloc[:, topic].rename(p_cond_name)
|
|
380
|
+
if isinstance(phi, DataFrame)
|
|
375
381
|
else Series(phi[:, topic], name=p_cond_name)
|
|
382
|
+
)
|
|
376
383
|
|
|
377
|
-
p_marg_name =
|
|
378
|
-
p_marg =
|
|
379
|
-
.rename(p_marg_name)
|
|
380
|
-
if isinstance(phi, DataFrame)
|
|
384
|
+
p_marg_name = "Marginal term probability, p(w)"
|
|
385
|
+
p_marg = (
|
|
386
|
+
phi.sum(axis=1).rename(p_marg_name)
|
|
387
|
+
if isinstance(phi, DataFrame)
|
|
381
388
|
else Series(phi[:, topic], name=p_marg_name)
|
|
389
|
+
)
|
|
382
390
|
|
|
383
391
|
terms_probs = concat((p_marg, p_cond), axis=1)
|
|
384
392
|
relevant_idx = get_relevant_terms(phi, topic, lambda_).index
|
|
385
393
|
terms_probs_slice = terms_probs.loc[relevant_idx].head(terms_num)
|
|
386
394
|
|
|
387
|
-
return
|
|
388
|
-
.reset_index(drop=False)
|
|
395
|
+
return (
|
|
396
|
+
terms_probs_slice.reset_index(drop=False)
|
|
389
397
|
.melt(
|
|
390
398
|
id_vars=[terms_probs_slice.index.name],
|
|
391
|
-
var_name=
|
|
392
|
-
value_name=
|
|
393
|
-
|
|
399
|
+
var_name="Type",
|
|
400
|
+
value_name="Probability",
|
|
401
|
+
)
|
|
402
|
+
.rename(columns={terms_probs_slice.index.name: "Terms"})
|
|
403
|
+
)
|
|
394
404
|
|
|
395
405
|
|
|
396
406
|
def get_relevant_terms(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
lambda_: float = 0.6) -> Series:
|
|
407
|
+
phi: Union[ndarray, DataFrame], topic: int, lambda_: float = 0.6
|
|
408
|
+
) -> Series:
|
|
400
409
|
"""Select relevant terms.
|
|
401
410
|
|
|
402
411
|
Parameters
|
|
@@ -422,11 +431,10 @@ def get_relevant_terms(
|
|
|
422
431
|
pandas.Series
|
|
423
432
|
Terms sorted by relevance (descendingly).
|
|
424
433
|
"""
|
|
425
|
-
phi_topic = phi.iloc[:, topic]
|
|
426
|
-
if isinstance(phi, DataFrame)\
|
|
427
|
-
else phi[:, topic]
|
|
434
|
+
phi_topic = phi.iloc[:, topic] if isinstance(phi, DataFrame) else phi[:, topic]
|
|
428
435
|
|
|
429
436
|
# relevance = lambda * log(p(w | t)) + (1 - lambda) * log(p(w | t) / p(w))
|
|
430
|
-
relevance = lambda_ * nplog(phi_topic)
|
|
431
|
-
|
|
437
|
+
relevance = lambda_ * nplog(phi_topic) + (1 - lambda_) * nplog(
|
|
438
|
+
phi_topic / phi.sum(axis=1)
|
|
439
|
+
)
|
|
432
440
|
return relevance.sort_values(ascending=False)
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
__all__ = ["prepare_coords", "report"]
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Dict, Optional, Sequence, List
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from IPython.display import display
|
|
6
|
+
from ipywidgets import widgets as wdg
|
|
7
|
+
from pandas import DataFrame
|
|
8
|
+
from ._distance import get_topics_dist, get_topics_scatter
|
|
9
|
+
from ._vis import plot_scatter_topics, plot_terms, plot_docs
|
|
10
|
+
from ._helpers import calc_terms_probs_ratio, get_phi, get_theta, get_top_docs
|
|
11
|
+
|
|
12
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
13
|
+
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def prepare_coords(
|
|
17
|
+
model: object,
|
|
18
|
+
labels: Optional[Sequence] = None,
|
|
19
|
+
corpus: Optional[List] = None,
|
|
20
|
+
dist_kws: Optional[Dict] = None,
|
|
21
|
+
scatter_kws: Optional[Dict] = None,
|
|
22
|
+
) -> DataFrame:
|
|
23
|
+
"""Prepare coordinates for topics scatter plot.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
model : object
|
|
28
|
+
Topic model instance.
|
|
29
|
+
labels : Optional[Sequence]
|
|
30
|
+
Topics labels.
|
|
31
|
+
corpus : Optional[List], optional
|
|
32
|
+
Corpus (must be specified for a `gensim` model).
|
|
33
|
+
dist_kws : dict, optional
|
|
34
|
+
Keyword arguments passed to :py:meth:`tmplot.get_topics_dist()`.
|
|
35
|
+
scatter_kws : dict, optional
|
|
36
|
+
Keyword arguments passed to :py:meth:`tmplot.get_topics_scatter()`.
|
|
37
|
+
"""
|
|
38
|
+
if not dist_kws:
|
|
39
|
+
dist_kws = {}
|
|
40
|
+
if not scatter_kws:
|
|
41
|
+
scatter_kws = {}
|
|
42
|
+
|
|
43
|
+
phi = get_phi(model)
|
|
44
|
+
theta = get_theta(model, corpus=corpus)
|
|
45
|
+
topics_dists = get_topics_dist(phi, **dist_kws)
|
|
46
|
+
topics_coords = get_topics_scatter(topics_dists, theta, **scatter_kws)
|
|
47
|
+
topics_coords["label"] = labels or theta.index
|
|
48
|
+
return topics_coords
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def report(
|
|
52
|
+
model: object,
|
|
53
|
+
docs: Sequence[str],
|
|
54
|
+
*,
|
|
55
|
+
topics_labels: Optional[Sequence[str]] = None,
|
|
56
|
+
corpus: Optional[List] = None,
|
|
57
|
+
layout: Optional[wdg.Layout] = None,
|
|
58
|
+
show_headers: bool = True,
|
|
59
|
+
show_docs: bool = True,
|
|
60
|
+
show_words: bool = True,
|
|
61
|
+
show_topics: bool = True,
|
|
62
|
+
topics_kws: Optional[dict] = None,
|
|
63
|
+
height: int = 500,
|
|
64
|
+
width: int = 300,
|
|
65
|
+
coords_kws: Optional[dict] = None,
|
|
66
|
+
words_kws: Optional[dict] = None,
|
|
67
|
+
docs_kws: Optional[dict] = None,
|
|
68
|
+
top_docs_kws: Optional[dict] = None,
|
|
69
|
+
) -> wdg.VBox:
|
|
70
|
+
"""Interactive report interface.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
model : object
|
|
75
|
+
Topic model instance.
|
|
76
|
+
docs : Sequence[str]
|
|
77
|
+
Documents.
|
|
78
|
+
topics_labels : Optional[Sequence[str]], optional
|
|
79
|
+
Topics labels.
|
|
80
|
+
corpus : Optional[List[str]], optional
|
|
81
|
+
Gensim corpus (must be specified if you are using a `gensim` model).
|
|
82
|
+
layout : wdg.Layout, optional
|
|
83
|
+
Interface layout instance.
|
|
84
|
+
show_headers : bool, optional
|
|
85
|
+
Show headers.
|
|
86
|
+
show_docs : bool, optional
|
|
87
|
+
Show documents widget.
|
|
88
|
+
show_words : bool, optional
|
|
89
|
+
Show words widget.
|
|
90
|
+
show_topics : bool, optional
|
|
91
|
+
Show topics scatter plot widget.
|
|
92
|
+
topics_kws : dict, optional
|
|
93
|
+
Keyword arguments passed to :py:meth:`tmplot.plot_scatter_topics()`.
|
|
94
|
+
coords_kws : dict, optional
|
|
95
|
+
Keyword arguments passed to :py:meth:`tmplot.prepare_coords()`.
|
|
96
|
+
words_kws : dict, optional
|
|
97
|
+
Keyword arguments passed to :py:meth:`tmplot.plot_terms()`.
|
|
98
|
+
docs_kws : dict, optional
|
|
99
|
+
Keyword arguments passed to :py:meth:`tmplot.plot_docs()`.
|
|
100
|
+
top_docs_kws : dict, optional
|
|
101
|
+
Keyword arguments passed to :py:meth:`tmplot.get_top_docs()`.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
ipywidgets.widgets.widget_box.VBox
|
|
106
|
+
Report interface as a VBox instance.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
_topics_kws = (
|
|
110
|
+
{"chart_kws": {"height": height, "width": width}}
|
|
111
|
+
if not topics_kws
|
|
112
|
+
else deepcopy(topics_kws)
|
|
113
|
+
)
|
|
114
|
+
_coords_kws = {"corpus": corpus} if not coords_kws else deepcopy(coords_kws)
|
|
115
|
+
_words_kws = (
|
|
116
|
+
{"chart_kws": {"height": height, "width": width}}
|
|
117
|
+
if not words_kws
|
|
118
|
+
else deepcopy(words_kws)
|
|
119
|
+
)
|
|
120
|
+
_top_docs_kws = {} if not docs_kws else deepcopy(top_docs_kws)
|
|
121
|
+
_docs_kws = {} if not docs_kws else deepcopy(docs_kws)
|
|
122
|
+
|
|
123
|
+
# Headers init
|
|
124
|
+
topics_header = (
|
|
125
|
+
wdg.HTML("<b>Intertopic distance plot</b>")
|
|
126
|
+
if show_headers and show_topics
|
|
127
|
+
else None
|
|
128
|
+
)
|
|
129
|
+
words_header = (
|
|
130
|
+
wdg.HTML("<b>Relevant words (terms)</b>")
|
|
131
|
+
if show_headers and show_words
|
|
132
|
+
else None
|
|
133
|
+
)
|
|
134
|
+
docs_header = (
|
|
135
|
+
wdg.HTML("<b>Top documents in a topic</b>")
|
|
136
|
+
if show_headers and show_docs
|
|
137
|
+
else None
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Layout init
|
|
141
|
+
grid_cols = " ".join(["1fr"] * sum([show_docs, show_words, show_topics]))
|
|
142
|
+
layout = (
|
|
143
|
+
wdg.Layout(
|
|
144
|
+
grid_template_columns=grid_cols,
|
|
145
|
+
# justify_items='center'
|
|
146
|
+
)
|
|
147
|
+
if not layout
|
|
148
|
+
else layout
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Children widgets list init
|
|
152
|
+
children = []
|
|
153
|
+
|
|
154
|
+
if "topics_coords" not in _topics_kws:
|
|
155
|
+
topics_coords = prepare_coords(model, **_coords_kws)
|
|
156
|
+
_topics_kws.update(
|
|
157
|
+
{
|
|
158
|
+
"topics_coords": topics_coords,
|
|
159
|
+
"label_col": "label",
|
|
160
|
+
"size_col": "size",
|
|
161
|
+
"topic": 0,
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if "terms_probs" not in _words_kws:
|
|
166
|
+
phi = get_phi(model)
|
|
167
|
+
terms_probs = calc_terms_probs_ratio(phi, topic=0)
|
|
168
|
+
_words_kws.update({"terms_probs": terms_probs})
|
|
169
|
+
|
|
170
|
+
if "docs" not in _docs_kws:
|
|
171
|
+
theta = get_theta(model, corpus=corpus).values
|
|
172
|
+
_top_docs_kws.update(
|
|
173
|
+
{"docs": docs, "theta": theta, "topics": [0], "docs_num": 2}
|
|
174
|
+
)
|
|
175
|
+
top_docs = get_top_docs(**_top_docs_kws)
|
|
176
|
+
top_docs.columns = [""]
|
|
177
|
+
_docs_kws.update({"docs": top_docs})
|
|
178
|
+
|
|
179
|
+
# Topic selection
|
|
180
|
+
def _on_select_topic(sel):
|
|
181
|
+
topic = sel['new']
|
|
182
|
+
|
|
183
|
+
if show_words:
|
|
184
|
+
words_plot_output.clear_output(wait=False)
|
|
185
|
+
with words_plot_output:
|
|
186
|
+
terms_probs = calc_terms_probs_ratio(
|
|
187
|
+
phi, topic=topic, lambda_=lambda_slider.value)
|
|
188
|
+
_words_kws.update({'terms_probs': terms_probs})
|
|
189
|
+
display(plot_terms(**_words_kws))
|
|
190
|
+
|
|
191
|
+
if show_topics:
|
|
192
|
+
topics_plot_output.clear_output(wait=False)
|
|
193
|
+
with topics_plot_output:
|
|
194
|
+
_topics_kws.update({'topic': topic})
|
|
195
|
+
display(plot_scatter_topics(**_topics_kws))
|
|
196
|
+
|
|
197
|
+
if show_docs:
|
|
198
|
+
docs_plot_output.clear_output(wait=False)
|
|
199
|
+
with docs_plot_output:
|
|
200
|
+
_top_docs_kws.update({'topics': [sel['new']]})
|
|
201
|
+
top_docs = get_top_docs(**_top_docs_kws)
|
|
202
|
+
top_docs.columns = ['']
|
|
203
|
+
_docs_kws.update({'docs': top_docs})
|
|
204
|
+
display(plot_docs(**_docs_kws))
|
|
205
|
+
|
|
206
|
+
topics_ids = list(range(len(_topics_kws["topics_coords"])))
|
|
207
|
+
topics_labels = topics_labels or topics_ids
|
|
208
|
+
select_topic = wdg.Dropdown(options=list(zip(topics_labels, topics_ids)), value=0)
|
|
209
|
+
select_topic.observe(_on_select_topic, names="value")
|
|
210
|
+
select_topic_header = wdg.HTML("<b>Select a topic</b>:")
|
|
211
|
+
select_topic_widget = wdg.HBox([select_topic_header, select_topic])
|
|
212
|
+
select_topic_wrapper = wdg.VBox(
|
|
213
|
+
[select_topic_widget], layout={"align_items": "center"}
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Topics scatter
|
|
217
|
+
def _on_select_topics_method(names):
|
|
218
|
+
topics_plot_output.clear_output(wait=False)
|
|
219
|
+
with topics_plot_output:
|
|
220
|
+
_coords_kws.update({"scatter_kws": {"method": names["new"]}})
|
|
221
|
+
topics_coords = prepare_coords(model, **_coords_kws)
|
|
222
|
+
_topics_kws.update(
|
|
223
|
+
{"topics_coords": topics_coords, "topic": select_topic.value}
|
|
224
|
+
)
|
|
225
|
+
display(plot_scatter_topics(**_topics_kws))
|
|
226
|
+
|
|
227
|
+
if show_topics:
|
|
228
|
+
topics_plot_children = [topics_header] if show_headers else []
|
|
229
|
+
options_methods = [
|
|
230
|
+
("TSNE", "tsne"),
|
|
231
|
+
("Spectral Embedding", "sem"),
|
|
232
|
+
("MDS", "mds"),
|
|
233
|
+
("Locally Linear Embedding (Standard)", "lle"),
|
|
234
|
+
("Locally Linear Embedding (LTSA)", "ltsa"),
|
|
235
|
+
("Isomap", "isomap"),
|
|
236
|
+
]
|
|
237
|
+
topics_method_header = wdg.HTML("Select a method:")
|
|
238
|
+
topics_method = wdg.Dropdown(
|
|
239
|
+
options=options_methods,
|
|
240
|
+
value="tsne",
|
|
241
|
+
layout=wdg.Layout(width=f"{width/1.25}px"),
|
|
242
|
+
)
|
|
243
|
+
topics_method_widget = wdg.HBox([topics_method_header, topics_method])
|
|
244
|
+
topics_method.observe(_on_select_topics_method, names="value")
|
|
245
|
+
topics_plot_output = wdg.Output()
|
|
246
|
+
topics_plot = plot_scatter_topics(**_topics_kws)
|
|
247
|
+
topics_plot_output.append_display_data(topics_plot)
|
|
248
|
+
topics_plot_children.extend([topics_method_widget, topics_plot_output])
|
|
249
|
+
topics_widget = wdg.VBox(topics_plot_children, layout={"align_items": "center"})
|
|
250
|
+
children.append(topics_widget)
|
|
251
|
+
|
|
252
|
+
# Words
|
|
253
|
+
if show_words:
|
|
254
|
+
|
|
255
|
+
def _on_select_lambda(sel):
|
|
256
|
+
topic = select_topic.value
|
|
257
|
+
lambda_ = lambda_slider.value
|
|
258
|
+
words_plot_output.clear_output(wait=False)
|
|
259
|
+
with words_plot_output:
|
|
260
|
+
terms_probs = calc_terms_probs_ratio(phi, topic=topic, lambda_=lambda_)
|
|
261
|
+
_words_kws.update({"terms_probs": terms_probs})
|
|
262
|
+
display(plot_terms(**_words_kws))
|
|
263
|
+
|
|
264
|
+
lambda_slider = wdg.FloatSlider(
|
|
265
|
+
value=0.6,
|
|
266
|
+
min=0.0,
|
|
267
|
+
max=1.0,
|
|
268
|
+
step=0.01,
|
|
269
|
+
description="",
|
|
270
|
+
continuous_update=False,
|
|
271
|
+
orientation="horizontal",
|
|
272
|
+
readout=True,
|
|
273
|
+
readout_format=".2f",
|
|
274
|
+
layout=wdg.Layout(width=f"{width/1.25}px"),
|
|
275
|
+
)
|
|
276
|
+
lambda_slider.observe(_on_select_lambda, names="value")
|
|
277
|
+
lambda_slider_header = wdg.HTML("Lambda value:")
|
|
278
|
+
lambda_slider_widget = wdg.HBox([lambda_slider_header, lambda_slider])
|
|
279
|
+
words_plot = plot_terms(**_words_kws)
|
|
280
|
+
words_plot_output = wdg.Output()
|
|
281
|
+
words_plot_output.append_display_data(words_plot)
|
|
282
|
+
words_plot_children = (
|
|
283
|
+
[words_header, lambda_slider_widget]
|
|
284
|
+
if show_headers
|
|
285
|
+
else [lambda_slider_widget]
|
|
286
|
+
)
|
|
287
|
+
words_plot_children.append(words_plot_output)
|
|
288
|
+
words_widget = wdg.VBox(words_plot_children, layout={"align_items": "center"})
|
|
289
|
+
children.append(words_widget)
|
|
290
|
+
|
|
291
|
+
# Docs
|
|
292
|
+
if show_docs:
|
|
293
|
+
|
|
294
|
+
def _on_select_docs_num(_):
|
|
295
|
+
docs_num = docs_num_slider.value
|
|
296
|
+
docs_plot_output.clear_output(wait=False)
|
|
297
|
+
with docs_plot_output:
|
|
298
|
+
_top_docs_kws.update({"docs_num": docs_num})
|
|
299
|
+
top_docs = get_top_docs(**_top_docs_kws)
|
|
300
|
+
top_docs.columns = [""]
|
|
301
|
+
_docs_kws.update({"docs": top_docs})
|
|
302
|
+
display(plot_docs(**_docs_kws))
|
|
303
|
+
|
|
304
|
+
docs_num_slider = wdg.IntSlider(
|
|
305
|
+
value=2,
|
|
306
|
+
min=1,
|
|
307
|
+
max=100,
|
|
308
|
+
continuous_update=False,
|
|
309
|
+
orientation="horizontal",
|
|
310
|
+
readout=True,
|
|
311
|
+
readout_format="d",
|
|
312
|
+
layout=wdg.Layout(width=f"{width/1.25}px"),
|
|
313
|
+
)
|
|
314
|
+
docs_num_slider.observe(_on_select_docs_num, names="value")
|
|
315
|
+
docs_num_slider_header = wdg.HTML("Documents number:")
|
|
316
|
+
docs_num_slider_widget = wdg.HBox([docs_num_slider_header, docs_num_slider])
|
|
317
|
+
|
|
318
|
+
docs_plot = plot_docs(**_docs_kws)
|
|
319
|
+
docs_plot_output = wdg.Output()
|
|
320
|
+
docs_plot_output.append_display_data(docs_plot)
|
|
321
|
+
docs_plot_children = (
|
|
322
|
+
[docs_header, docs_num_slider_widget]
|
|
323
|
+
if show_headers
|
|
324
|
+
else [docs_num_slider_widget]
|
|
325
|
+
)
|
|
326
|
+
docs_plot_children.append(docs_plot_output)
|
|
327
|
+
docs_widget = wdg.VBox(docs_plot_children, layout={"align_items": "center"})
|
|
328
|
+
children.append(docs_widget)
|
|
329
|
+
|
|
330
|
+
grid_box = wdg.GridBox(children, layout=layout)
|
|
331
|
+
hr_line = wdg.HTML('<hr style="border: 0; border-bottom: 1px solid #aaa">')
|
|
332
|
+
app = wdg.VBox([select_topic_wrapper, hr_line, grid_box])
|
|
333
|
+
|
|
334
|
+
return app
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tmplot
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Visualization of Topic Modeling Results
|
|
5
5
|
Author-email: Maksim Terpilovskii <maximtrp@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -96,6 +96,12 @@ Requires-Dist: bitermplus; extra == "models"
|
|
|
96
96
|
- LocallyLinearEmbedding
|
|
97
97
|
- Isomap
|
|
98
98
|
|
|
99
|
+
## Donate
|
|
100
|
+
|
|
101
|
+
If you find this package useful, please consider donating any amount of money. This will help me spend more time on supporting open-source software.
|
|
102
|
+
|
|
103
|
+
<a href="https://www.buymeacoffee.com/maximtrp" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
104
|
+
|
|
99
105
|
## Installation
|
|
100
106
|
|
|
101
107
|
The package can be installed from PyPi:
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
__all__ = ['prepare_coords', 'report']
|
|
2
|
-
import warnings
|
|
3
|
-
from typing import Optional, Sequence, List
|
|
4
|
-
from copy import deepcopy
|
|
5
|
-
from ipywidgets import widgets as wdg
|
|
6
|
-
from pandas import DataFrame
|
|
7
|
-
from ._distance import get_topics_dist, get_topics_scatter
|
|
8
|
-
from ._vis import plot_scatter_topics, plot_terms, plot_docs
|
|
9
|
-
from ._helpers import (
|
|
10
|
-
calc_terms_probs_ratio,
|
|
11
|
-
get_phi, get_theta,
|
|
12
|
-
get_top_docs)
|
|
13
|
-
|
|
14
|
-
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
15
|
-
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def prepare_coords(
|
|
19
|
-
model: object,
|
|
20
|
-
labels: Optional[Sequence] = None,
|
|
21
|
-
dist_kws: dict = None,
|
|
22
|
-
scatter_kws: dict = None) -> DataFrame:
|
|
23
|
-
"""Prepare coordinates for topics scatter plot.
|
|
24
|
-
|
|
25
|
-
Parameters
|
|
26
|
-
----------
|
|
27
|
-
model : object
|
|
28
|
-
Topic model instance.
|
|
29
|
-
labels : Optional[Sequence]
|
|
30
|
-
Topics labels.
|
|
31
|
-
dist_kws : dict, optional
|
|
32
|
-
Keyword arguments passed to :py:meth:`tmplot.get_topics_dist()`.
|
|
33
|
-
scatter_kws : dict, optional
|
|
34
|
-
Keyword arguments passed to :py:meth:`tmplot.get_topics_scatter()`.
|
|
35
|
-
"""
|
|
36
|
-
if not dist_kws:
|
|
37
|
-
dist_kws = {}
|
|
38
|
-
if not scatter_kws:
|
|
39
|
-
scatter_kws = {}
|
|
40
|
-
|
|
41
|
-
phi = get_phi(model)
|
|
42
|
-
theta = get_theta(model)
|
|
43
|
-
topics_dists = get_topics_dist(phi, **dist_kws)
|
|
44
|
-
topics_coords = get_topics_scatter(topics_dists, theta, **scatter_kws)
|
|
45
|
-
topics_coords['label'] = labels or theta.index
|
|
46
|
-
return topics_coords
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def report(
|
|
50
|
-
model: object,
|
|
51
|
-
docs: Sequence[str],
|
|
52
|
-
*,
|
|
53
|
-
topics_labels: Optional[Sequence[str]] = None,
|
|
54
|
-
corpus: Optional[List] = None,
|
|
55
|
-
layout: wdg.Layout = None,
|
|
56
|
-
show_headers: bool = True,
|
|
57
|
-
show_docs: bool = True,
|
|
58
|
-
show_words: bool = True,
|
|
59
|
-
show_topics: bool = True,
|
|
60
|
-
topics_kws: dict = None,
|
|
61
|
-
height: int = 500,
|
|
62
|
-
width: int = 300,
|
|
63
|
-
coords_kws: dict = None,
|
|
64
|
-
words_kws: dict = None,
|
|
65
|
-
docs_kws: dict = None,
|
|
66
|
-
top_docs_kws: dict = None) -> wdg.VBox:
|
|
67
|
-
"""Interactive report interface.
|
|
68
|
-
|
|
69
|
-
Parameters
|
|
70
|
-
----------
|
|
71
|
-
model : object
|
|
72
|
-
Topic model instance.
|
|
73
|
-
docs : Sequence[str]
|
|
74
|
-
Documents.
|
|
75
|
-
topics_labels : Optional[Sequence[str]], optional
|
|
76
|
-
Topics labels.
|
|
77
|
-
corpus : Optional[List[str]], optional
|
|
78
|
-
Gensim corpus.
|
|
79
|
-
layout : wdg.Layout, optional
|
|
80
|
-
Interface layout instance.
|
|
81
|
-
show_headers : bool, optional
|
|
82
|
-
Show headers.
|
|
83
|
-
show_docs : bool, optional
|
|
84
|
-
Show documents widget.
|
|
85
|
-
show_words : bool, optional
|
|
86
|
-
Show words widget.
|
|
87
|
-
show_topics : bool, optional
|
|
88
|
-
Show topics scatter plot widget.
|
|
89
|
-
topics_kws : dict, optional
|
|
90
|
-
Keyword arguments passed to :py:meth:`tmplot.plot_scatter_topics()`.
|
|
91
|
-
coords_kws : dict, optional
|
|
92
|
-
Keyword arguments passed to :py:meth:`tmplot.prepare_coords()`.
|
|
93
|
-
words_kws : dict, optional
|
|
94
|
-
Keyword arguments passed to :py:meth:`tmplot.plot_terms()`.
|
|
95
|
-
docs_kws : dict, optional
|
|
96
|
-
Keyword arguments passed to :py:meth:`tmplot.plot_docs()`.
|
|
97
|
-
top_docs_kws : dict, optional
|
|
98
|
-
Keyword arguments passed to :py:meth:`tmplot.get_top_docs()`.
|
|
99
|
-
|
|
100
|
-
Returns
|
|
101
|
-
-------
|
|
102
|
-
ipywidgets.widgets.widget_box.VBox
|
|
103
|
-
Report interface as a VBox instance.
|
|
104
|
-
"""
|
|
105
|
-
from IPython.display import display
|
|
106
|
-
|
|
107
|
-
_topics_kws = {
|
|
108
|
-
'chart_kws': {'height': height, 'width': width}}\
|
|
109
|
-
if not topics_kws else deepcopy(topics_kws)
|
|
110
|
-
_coords_kws = {} if not coords_kws else deepcopy(coords_kws)
|
|
111
|
-
_words_kws = {
|
|
112
|
-
'chart_kws': {'height': height, 'width': width}}\
|
|
113
|
-
if not words_kws else deepcopy(words_kws)
|
|
114
|
-
_top_docs_kws = {} if not docs_kws else deepcopy(top_docs_kws)
|
|
115
|
-
_docs_kws = {} if not docs_kws else deepcopy(docs_kws)
|
|
116
|
-
|
|
117
|
-
# Headers init
|
|
118
|
-
topics_header = wdg.HTML('<b>Intertopic distance plot</b>')\
|
|
119
|
-
if show_headers and show_topics else None
|
|
120
|
-
words_header = wdg.HTML('<b>Relevant words (terms)</b>')\
|
|
121
|
-
if show_headers and show_words else None
|
|
122
|
-
docs_header = wdg.HTML('<b>Top documents in a topic</b>')\
|
|
123
|
-
if show_headers and show_docs else None
|
|
124
|
-
|
|
125
|
-
# Layout init
|
|
126
|
-
grid_cols = " ".join(['1fr'] * sum([show_docs, show_words, show_topics]))
|
|
127
|
-
layout = wdg.Layout(
|
|
128
|
-
grid_template_columns=grid_cols,
|
|
129
|
-
# justify_items='center'
|
|
130
|
-
)\
|
|
131
|
-
if not layout else layout
|
|
132
|
-
|
|
133
|
-
# Children widgets list init
|
|
134
|
-
children = []
|
|
135
|
-
|
|
136
|
-
if 'topics_coords' not in _topics_kws:
|
|
137
|
-
topics_coords = prepare_coords(model, **_coords_kws)
|
|
138
|
-
_topics_kws.update({
|
|
139
|
-
'topics_coords': topics_coords,
|
|
140
|
-
'label_col': 'label',
|
|
141
|
-
'size_col': 'size',
|
|
142
|
-
'topic': 0
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
if 'terms_probs' not in _words_kws:
|
|
146
|
-
phi = get_phi(model)
|
|
147
|
-
terms_probs = calc_terms_probs_ratio(phi, topic=0)
|
|
148
|
-
_words_kws.update({'terms_probs': terms_probs})
|
|
149
|
-
|
|
150
|
-
if 'docs' not in _docs_kws:
|
|
151
|
-
theta = get_theta(model, corpus=corpus).values
|
|
152
|
-
_top_docs_kws.update({
|
|
153
|
-
'docs': docs, 'theta': theta,
|
|
154
|
-
'topics': [0], 'docs_num': 2})
|
|
155
|
-
top_docs = get_top_docs(**_top_docs_kws)
|
|
156
|
-
top_docs.columns = ['']
|
|
157
|
-
_docs_kws.update({'docs': top_docs})
|
|
158
|
-
|
|
159
|
-
# Topic selection
|
|
160
|
-
def _on_select_topic(sel):
|
|
161
|
-
topic = sel['new']
|
|
162
|
-
topics_plot_output.clear_output(wait=False)
|
|
163
|
-
words_plot_output.clear_output(wait=False)
|
|
164
|
-
docs_plot_output.clear_output(wait=False)
|
|
165
|
-
with words_plot_output:
|
|
166
|
-
terms_probs = calc_terms_probs_ratio(
|
|
167
|
-
phi, topic=topic, lambda_=lambda_slider.value)
|
|
168
|
-
_words_kws.update({'terms_probs': terms_probs})
|
|
169
|
-
display(plot_terms(**_words_kws))
|
|
170
|
-
with topics_plot_output:
|
|
171
|
-
_topics_kws.update({'topic': topic})
|
|
172
|
-
display(plot_scatter_topics(**_topics_kws))
|
|
173
|
-
with docs_plot_output:
|
|
174
|
-
_top_docs_kws.update({'topics': [sel['new']]})
|
|
175
|
-
top_docs = get_top_docs(**_top_docs_kws)
|
|
176
|
-
top_docs.columns = ['']
|
|
177
|
-
_docs_kws.update({'docs': top_docs})
|
|
178
|
-
display(plot_docs(**_docs_kws))
|
|
179
|
-
|
|
180
|
-
topics_ids = list(range(len(_topics_kws['topics_coords'])))
|
|
181
|
-
topics_labels = topics_labels or topics_ids
|
|
182
|
-
select_topic = wdg.Dropdown(
|
|
183
|
-
options=list(zip(topics_labels, topics_ids)), value=0)
|
|
184
|
-
select_topic.observe(_on_select_topic, names='value')
|
|
185
|
-
select_topic_header = wdg.HTML('<b>Select a topic</b>:')
|
|
186
|
-
select_topic_widget = wdg.HBox([select_topic_header, select_topic])
|
|
187
|
-
select_topic_wrapper = wdg.VBox(
|
|
188
|
-
[select_topic_widget], layout={'align_items': 'center'})
|
|
189
|
-
|
|
190
|
-
# Topics scatter
|
|
191
|
-
def _on_select_topics_method(names):
|
|
192
|
-
topics_plot_output.clear_output(wait=False)
|
|
193
|
-
with topics_plot_output:
|
|
194
|
-
_coords_kws.update({'scatter_kws': {'method': names['new']}})
|
|
195
|
-
topics_coords = prepare_coords(model, **_coords_kws)
|
|
196
|
-
_topics_kws.update({
|
|
197
|
-
'topics_coords': topics_coords,
|
|
198
|
-
'topic': select_topic.value
|
|
199
|
-
})
|
|
200
|
-
display(plot_scatter_topics(**_topics_kws))
|
|
201
|
-
|
|
202
|
-
if show_topics:
|
|
203
|
-
topics_plot_children = [topics_header] if show_headers else []
|
|
204
|
-
options_methods = [
|
|
205
|
-
('TSNE', 'tsne'),
|
|
206
|
-
('Spectral Embedding', 'sem'),
|
|
207
|
-
('MDS', 'mds'),
|
|
208
|
-
('Locally Linear Embedding (Standard)', 'lle'),
|
|
209
|
-
('Locally Linear Embedding (LTSA)', 'ltsa'),
|
|
210
|
-
('Isomap', 'isomap')
|
|
211
|
-
]
|
|
212
|
-
topics_method_header = wdg.HTML('Select a method:')
|
|
213
|
-
topics_method = wdg.Dropdown(
|
|
214
|
-
options=options_methods,
|
|
215
|
-
value='tsne',
|
|
216
|
-
layout=wdg.Layout(width=f'{width/1.25}px')
|
|
217
|
-
)
|
|
218
|
-
topics_method_widget = wdg.HBox([topics_method_header, topics_method])
|
|
219
|
-
topics_method.observe(_on_select_topics_method, names='value')
|
|
220
|
-
topics_plot_output = wdg.Output()
|
|
221
|
-
topics_plot = plot_scatter_topics(**_topics_kws)
|
|
222
|
-
topics_plot_output.append_display_data(topics_plot)
|
|
223
|
-
topics_plot_children.extend([topics_method_widget, topics_plot_output])
|
|
224
|
-
topics_widget = wdg.VBox(
|
|
225
|
-
topics_plot_children,
|
|
226
|
-
layout={'align_items': 'center'})
|
|
227
|
-
children.append(topics_widget)
|
|
228
|
-
|
|
229
|
-
# Words
|
|
230
|
-
if show_words:
|
|
231
|
-
def _on_select_lambda(sel):
|
|
232
|
-
topic = select_topic.value
|
|
233
|
-
lambda_ = lambda_slider.value
|
|
234
|
-
words_plot_output.clear_output(wait=False)
|
|
235
|
-
with words_plot_output:
|
|
236
|
-
terms_probs = calc_terms_probs_ratio(
|
|
237
|
-
phi, topic=topic, lambda_=lambda_)
|
|
238
|
-
_words_kws.update({'terms_probs': terms_probs})
|
|
239
|
-
display(plot_terms(**_words_kws))
|
|
240
|
-
|
|
241
|
-
lambda_slider = wdg.FloatSlider(
|
|
242
|
-
value=0.6,
|
|
243
|
-
min=0.0,
|
|
244
|
-
max=1.0,
|
|
245
|
-
step=0.01,
|
|
246
|
-
description='',
|
|
247
|
-
continuous_update=False,
|
|
248
|
-
orientation='horizontal',
|
|
249
|
-
readout=True,
|
|
250
|
-
readout_format='.2f',
|
|
251
|
-
layout=wdg.Layout(width=f'{width/1.25}px')
|
|
252
|
-
)
|
|
253
|
-
lambda_slider.observe(_on_select_lambda, names='value')
|
|
254
|
-
lambda_slider_header = wdg.HTML('Lambda value:')
|
|
255
|
-
lambda_slider_widget = wdg.HBox([lambda_slider_header, lambda_slider])
|
|
256
|
-
words_plot = plot_terms(**_words_kws)
|
|
257
|
-
words_plot_output = wdg.Output()
|
|
258
|
-
words_plot_output.append_display_data(words_plot)
|
|
259
|
-
words_plot_children = [words_header, lambda_slider_widget]\
|
|
260
|
-
if show_headers else [lambda_slider_widget]
|
|
261
|
-
words_plot_children.append(words_plot_output)
|
|
262
|
-
words_widget = wdg.VBox(
|
|
263
|
-
words_plot_children,
|
|
264
|
-
layout={'align_items': 'center'})
|
|
265
|
-
children.append(words_widget)
|
|
266
|
-
|
|
267
|
-
# Docs
|
|
268
|
-
if show_docs:
|
|
269
|
-
def _on_select_docs_num(_):
|
|
270
|
-
docs_num = docs_num_slider.value
|
|
271
|
-
docs_plot_output.clear_output(wait=False)
|
|
272
|
-
with docs_plot_output:
|
|
273
|
-
_top_docs_kws.update({'docs_num': docs_num})
|
|
274
|
-
top_docs = get_top_docs(**_top_docs_kws)
|
|
275
|
-
top_docs.columns = ['']
|
|
276
|
-
_docs_kws.update({'docs': top_docs})
|
|
277
|
-
display(plot_docs(**_docs_kws))
|
|
278
|
-
|
|
279
|
-
docs_num_slider = wdg.IntSlider(
|
|
280
|
-
value=2,
|
|
281
|
-
min=1,
|
|
282
|
-
max=100,
|
|
283
|
-
continuous_update=False,
|
|
284
|
-
orientation='horizontal',
|
|
285
|
-
readout=True,
|
|
286
|
-
readout_format='d',
|
|
287
|
-
layout=wdg.Layout(width=f'{width/1.25}px')
|
|
288
|
-
)
|
|
289
|
-
docs_num_slider.observe(_on_select_docs_num, names='value')
|
|
290
|
-
docs_num_slider_header = wdg.HTML('Documents number:')
|
|
291
|
-
docs_num_slider_widget = wdg.HBox(
|
|
292
|
-
[docs_num_slider_header, docs_num_slider])
|
|
293
|
-
|
|
294
|
-
docs_plot = plot_docs(**_docs_kws)
|
|
295
|
-
docs_plot_output = wdg.Output()
|
|
296
|
-
docs_plot_output.append_display_data(docs_plot)
|
|
297
|
-
docs_plot_children = [docs_header, docs_num_slider_widget]\
|
|
298
|
-
if show_headers else [docs_num_slider_widget]
|
|
299
|
-
docs_plot_children.append(docs_plot_output)
|
|
300
|
-
docs_widget = wdg.VBox(
|
|
301
|
-
docs_plot_children,
|
|
302
|
-
layout={'align_items': 'center'})
|
|
303
|
-
children.append(docs_widget)
|
|
304
|
-
|
|
305
|
-
grid_box = wdg.GridBox(children, layout=layout)
|
|
306
|
-
hr_line = wdg.HTML('<hr style="border: 0; border-bottom: 1px solid #aaa">')
|
|
307
|
-
app = wdg.VBox([select_topic_wrapper, hr_line, grid_box])
|
|
308
|
-
|
|
309
|
-
return app
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|