tmplot 0.1.3__tar.gz → 0.2.0__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.3/src/tmplot.egg-info → tmplot-0.2.0}/PKG-INFO +2 -2
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/__init__.py +1 -1
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_distance.py +50 -49
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_helpers.py +19 -24
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_stability.py +29 -21
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_vis.py +108 -102
- {tmplot-0.1.3 → tmplot-0.2.0/src/tmplot.egg-info}/PKG-INFO +2 -2
- {tmplot-0.1.3 → tmplot-0.2.0}/LICENSE +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/MANIFEST.in +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/README.md +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/pyproject.toml +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/setup.cfg +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_metrics.py +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot/_report.py +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot.egg-info/SOURCES.txt +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot.egg-info/dependency_links.txt +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot.egg-info/requires.txt +0 -0
- {tmplot-0.1.3 → tmplot-0.2.0}/src/tmplot.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
__all__ = [
|
|
2
|
-
|
|
3
|
-
from typing import Union, List
|
|
1
|
+
__all__ = ["get_topics_dist", "get_topics_scatter", "get_top_topic_words"]
|
|
2
|
+
from typing import Optional, Union, List
|
|
4
3
|
from itertools import combinations
|
|
5
|
-
from pandas import DataFrame
|
|
4
|
+
from pandas import DataFrame, Index
|
|
6
5
|
import numpy as np
|
|
7
6
|
from scipy.special import kl_div
|
|
8
7
|
from scipy.spatial import distance
|
|
9
8
|
from sklearn.manifold import (
|
|
10
|
-
TSNE,
|
|
9
|
+
TSNE,
|
|
10
|
+
Isomap,
|
|
11
|
+
LocallyLinearEmbedding,
|
|
12
|
+
MDS,
|
|
13
|
+
SpectralEmbedding,
|
|
14
|
+
)
|
|
11
15
|
from ._helpers import calc_topics_marg_probs
|
|
12
16
|
|
|
13
17
|
|
|
@@ -28,15 +32,14 @@ def _dist_jsd(a1: np.ndarray, a2: np.ndarray):
|
|
|
28
32
|
|
|
29
33
|
def _dist_jef(a1: np.ndarray, a2: np.ndarray):
|
|
30
34
|
vals = (a1 - a2) * (np.log(a1) - np.log(a2))
|
|
31
|
-
vals[(vals <= 0) | ~np.isfinite(vals)] = 0.
|
|
35
|
+
vals[(vals <= 0) | ~np.isfinite(vals)] = 0.0
|
|
32
36
|
return vals.sum()
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
def _dist_hel(a1: np.ndarray, a2: np.ndarray):
|
|
36
40
|
a1[(a1 <= 0) | ~np.isfinite(a1)] = 1e-64
|
|
37
41
|
a2[(a2 <= 0) | ~np.isfinite(a2)] = 1e-64
|
|
38
|
-
hel_val = distance.euclidean(
|
|
39
|
-
np.sqrt(a1), np.sqrt(a2)) / np.sqrt(2)
|
|
42
|
+
hel_val = distance.euclidean(np.sqrt(a1), np.sqrt(a2)) / np.sqrt(2)
|
|
40
43
|
return hel_val
|
|
41
44
|
|
|
42
45
|
|
|
@@ -52,9 +55,9 @@ def _dist_tv(a1: np.ndarray, a2: np.ndarray):
|
|
|
52
55
|
return dist
|
|
53
56
|
|
|
54
57
|
|
|
55
|
-
def _dist_jac(a1: np.ndarray, a2: np.ndarray,
|
|
56
|
-
a = np.argsort(a1)[
|
|
57
|
-
b = np.argsort(a2)[
|
|
58
|
+
def _dist_jac(a1: np.ndarray, a2: np.ndarray, top_words=100):
|
|
59
|
+
a = np.argsort(a1)[: -top_words - 1 : -1]
|
|
60
|
+
b = np.argsort(a2)[: -top_words - 1 : -1]
|
|
58
61
|
j_num = np.intersect1d(a, b, assume_unique=False).size
|
|
59
62
|
j_den = np.union1d(a, b).size
|
|
60
63
|
jac_val = 1 - j_num / j_den
|
|
@@ -62,9 +65,8 @@ def _dist_jac(a1: np.ndarray, a2: np.ndarray, top_words=100):
|
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
def get_topics_dist(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
**kwargs) -> np.ndarray:
|
|
68
|
+
phi: Union[np.ndarray, DataFrame], method: str = "sklb", **kwargs
|
|
69
|
+
) -> np.ndarray:
|
|
68
70
|
"""Finding closest topics in models.
|
|
69
71
|
|
|
70
72
|
Parameters
|
|
@@ -110,16 +112,18 @@ def get_topics_dist(
|
|
|
110
112
|
for i, j in topics_pairs:
|
|
111
113
|
_dist_func = dist_funcs.get(method, "sklb")
|
|
112
114
|
topics_dists[((i, j), (j, i))] = _dist_func(
|
|
113
|
-
phi_copy[:, i], phi_copy[:, j], **kwargs
|
|
115
|
+
phi_copy[:, i], phi_copy[:, j], **kwargs
|
|
116
|
+
)
|
|
114
117
|
|
|
115
118
|
return topics_dists
|
|
116
119
|
|
|
117
120
|
|
|
118
121
|
def get_topics_scatter(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
topic_dists: np.ndarray,
|
|
123
|
+
theta: np.ndarray,
|
|
124
|
+
method: str = "tsne",
|
|
125
|
+
method_kws: Optional[dict] = None,
|
|
126
|
+
) -> DataFrame:
|
|
123
127
|
"""Calculate topics coordinates for a scatter plot.
|
|
124
128
|
|
|
125
129
|
Parameters
|
|
@@ -146,52 +150,52 @@ def get_topics_scatter(
|
|
|
146
150
|
Topics scatter coordinates.
|
|
147
151
|
"""
|
|
148
152
|
if not method_kws:
|
|
149
|
-
method_kws = {
|
|
153
|
+
method_kws = {"n_components": 2}
|
|
150
154
|
|
|
151
|
-
if method ==
|
|
152
|
-
method_kws.setdefault(
|
|
153
|
-
method_kws.setdefault(
|
|
154
|
-
method_kws.setdefault(
|
|
155
|
-
'perplexity', min(50, max(topic_dists.shape[0] // 2, 1)))
|
|
155
|
+
if method == "tsne":
|
|
156
|
+
method_kws.setdefault("init", "pca")
|
|
157
|
+
method_kws.setdefault("learning_rate", "auto")
|
|
158
|
+
method_kws.setdefault("perplexity", min(50, max(topic_dists.shape[0] // 2, 1)))
|
|
156
159
|
transformer = TSNE(**method_kws)
|
|
157
160
|
|
|
158
|
-
elif method ==
|
|
159
|
-
method_kws.setdefault(
|
|
161
|
+
elif method == "sem":
|
|
162
|
+
method_kws.setdefault("affinity", "precomputed")
|
|
160
163
|
transformer = SpectralEmbedding(**method_kws)
|
|
161
164
|
|
|
162
|
-
elif method ==
|
|
163
|
-
method_kws.setdefault(
|
|
164
|
-
method_kws.setdefault(
|
|
165
|
+
elif method == "mds":
|
|
166
|
+
method_kws.setdefault("dissimilarity", "precomputed")
|
|
167
|
+
method_kws.setdefault("normalized_stress", "auto")
|
|
165
168
|
transformer = MDS(**method_kws)
|
|
166
169
|
|
|
167
|
-
elif method ==
|
|
168
|
-
method_kws[
|
|
170
|
+
elif method == "lle":
|
|
171
|
+
method_kws["method"] = "standard"
|
|
169
172
|
transformer = LocallyLinearEmbedding(**method_kws)
|
|
170
173
|
|
|
171
|
-
elif method ==
|
|
172
|
-
method_kws[
|
|
174
|
+
elif method == "ltsa":
|
|
175
|
+
method_kws["method"] = "ltsa"
|
|
173
176
|
transformer = LocallyLinearEmbedding(**method_kws)
|
|
174
177
|
|
|
175
|
-
elif method ==
|
|
178
|
+
elif method == "isomap":
|
|
176
179
|
transformer = Isomap(**method_kws)
|
|
177
180
|
|
|
178
181
|
coords = transformer.fit_transform(topic_dists)
|
|
179
182
|
|
|
180
|
-
topics_xy = DataFrame(coords, columns=[
|
|
181
|
-
topics_xy[
|
|
182
|
-
topics_xy[
|
|
183
|
-
size_sum = topics_xy[
|
|
183
|
+
topics_xy = DataFrame(coords, columns=Index(["x", "y"]))
|
|
184
|
+
topics_xy["topic"] = topics_xy.index.astype(int)
|
|
185
|
+
topics_xy["size"] = calc_topics_marg_probs(theta)
|
|
186
|
+
size_sum = topics_xy["size"].sum()
|
|
184
187
|
if size_sum > 0:
|
|
185
|
-
topics_xy[
|
|
188
|
+
topics_xy["size"] *= 100 / topics_xy["size"].sum()
|
|
186
189
|
else:
|
|
187
|
-
topics_xy[
|
|
190
|
+
topics_xy["size"] = np.nan
|
|
188
191
|
return topics_xy
|
|
189
192
|
|
|
190
193
|
|
|
191
194
|
def get_top_topic_words(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
phi: DataFrame,
|
|
196
|
+
words_num: int = 20,
|
|
197
|
+
topics_idx: Optional[Union[List[int], np.ndarray]] = None,
|
|
198
|
+
) -> DataFrame:
|
|
195
199
|
"""Select top topic words from a fitted model.
|
|
196
200
|
|
|
197
201
|
Parameters
|
|
@@ -209,9 +213,6 @@ def get_top_topic_words(
|
|
|
209
213
|
DataFrame
|
|
210
214
|
Words with highest probabilities in all (or selected) topics.
|
|
211
215
|
"""
|
|
212
|
-
return phi.loc[:, topics_idx or phi.columns]
|
|
213
|
-
.
|
|
214
|
-
lambda x: x
|
|
215
|
-
.sort_values(ascending=False)
|
|
216
|
-
.head(words_num).index, axis=0
|
|
216
|
+
return phi.loc[:, topics_idx or phi.columns].apply(
|
|
217
|
+
lambda x: x.sort_values(ascending=False).head(words_num).index, axis=0
|
|
217
218
|
)
|
|
@@ -249,7 +249,7 @@ def get_top_docs(
|
|
|
249
249
|
|
|
250
250
|
def calc_topics_marg_probs(
|
|
251
251
|
theta: Union[DataFrame, ndarray], topic_id: Optional[int] = None
|
|
252
|
-
) ->
|
|
252
|
+
) -> ndarray:
|
|
253
253
|
"""Calculate marginal topics probabilities.
|
|
254
254
|
|
|
255
255
|
Parameters
|
|
@@ -264,18 +264,18 @@ def calc_topics_marg_probs(
|
|
|
264
264
|
Union[pandas.DataFrame, numpy.ndarray]
|
|
265
265
|
Marginal topics probabilities.
|
|
266
266
|
"""
|
|
267
|
+
p_t = array(theta).sum(axis=1)
|
|
268
|
+
p_t /= p_t.sum()
|
|
267
269
|
if topic_id is not None:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if isinstance(theta, DataFrame):
|
|
271
|
-
return theta.iloc[topic_id, :].sum()
|
|
272
|
-
|
|
273
|
-
return theta.sum(axis=1)
|
|
270
|
+
return p_t[topic_id]
|
|
271
|
+
return p_t
|
|
274
272
|
|
|
275
273
|
|
|
276
274
|
def calc_terms_marg_probs(
|
|
277
|
-
phi: Union[ndarray, DataFrame],
|
|
278
|
-
|
|
275
|
+
phi: Union[ndarray, DataFrame],
|
|
276
|
+
p_t: Union[ndarray, Series],
|
|
277
|
+
word_id: Optional[int] = None,
|
|
278
|
+
) -> ndarray:
|
|
279
279
|
"""Calculate marginal terms probabilities.
|
|
280
280
|
|
|
281
281
|
Parameters
|
|
@@ -290,16 +290,13 @@ def calc_terms_marg_probs(
|
|
|
290
290
|
Union[numpy.ndarray, pandas.Series]
|
|
291
291
|
Marginal terms probabilities.
|
|
292
292
|
"""
|
|
293
|
+
p_w = (array(phi) * array(p_t)).sum(axis=1)
|
|
293
294
|
if word_id is not None:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if isinstance(phi, DataFrame):
|
|
297
|
-
return phi.iloc[word_id, :].sum()
|
|
298
|
-
|
|
299
|
-
return phi.sum(axis=1)
|
|
295
|
+
return p_w[word_id]
|
|
296
|
+
return p_w
|
|
300
297
|
|
|
301
298
|
|
|
302
|
-
def get_salient_terms(
|
|
299
|
+
def get_salient_terms(phi: ndarray, theta: ndarray) -> ndarray:
|
|
303
300
|
"""Get salient terms.
|
|
304
301
|
|
|
305
302
|
Calculated as:
|
|
@@ -308,8 +305,6 @@ def get_salient_terms(terms_freqs: ndarray, phi: ndarray, theta: ndarray) -> nda
|
|
|
308
305
|
|
|
309
306
|
Parameters
|
|
310
307
|
----------
|
|
311
|
-
terms_freqs : numpy.ndarray
|
|
312
|
-
Words frequencies.
|
|
313
308
|
phi : numpy.ndarray
|
|
314
309
|
Words vs topics matrix.
|
|
315
310
|
theta : numpy.ndarray
|
|
@@ -320,15 +315,15 @@ def get_salient_terms(terms_freqs: ndarray, phi: ndarray, theta: ndarray) -> nda
|
|
|
320
315
|
numpy.ndarray
|
|
321
316
|
Terms saliency values.
|
|
322
317
|
"""
|
|
323
|
-
p_t =
|
|
324
|
-
p_w =
|
|
318
|
+
p_t = calc_topics_marg_probs(theta)
|
|
319
|
+
p_w = calc_terms_marg_probs(phi, p_t)
|
|
325
320
|
|
|
326
321
|
def _p_tw(phi, w, t):
|
|
327
|
-
return phi[w, t] * p_t[t] / p_w[w]
|
|
322
|
+
return array(phi)[w, t] * p_t[t] / p_w[w]
|
|
328
323
|
|
|
329
324
|
saliency = array(
|
|
330
|
-
|
|
331
|
-
|
|
325
|
+
[
|
|
326
|
+
p_w[w]
|
|
332
327
|
* sum(
|
|
333
328
|
(
|
|
334
329
|
_p_tw(phi, w, t) * log(_p_tw(phi, w, t) / p_t[t])
|
|
@@ -336,7 +331,7 @@ def get_salient_terms(terms_freqs: ndarray, phi: ndarray, theta: ndarray) -> nda
|
|
|
336
331
|
)
|
|
337
332
|
)
|
|
338
333
|
for w in range(phi.shape[0])
|
|
339
|
-
|
|
334
|
+
]
|
|
340
335
|
)
|
|
341
336
|
# saliency(term w) = frequency(w)
|
|
342
337
|
# * [sum_t p(t | w) * log(p(t | w)/p(t))] for topics t
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
__all__ = [
|
|
1
|
+
__all__ = ["get_closest_topics", "get_stable_topics"]
|
|
2
2
|
from typing import List, Tuple, Any
|
|
3
3
|
import numpy as np
|
|
4
4
|
import tqdm
|
|
5
|
-
from ._distance import
|
|
6
|
-
|
|
5
|
+
from ._distance import (
|
|
6
|
+
_dist_klb,
|
|
7
|
+
_dist_sklb,
|
|
8
|
+
_dist_jsd,
|
|
9
|
+
_dist_jef,
|
|
10
|
+
_dist_hel,
|
|
11
|
+
_dist_bhat,
|
|
12
|
+
_dist_jac,
|
|
13
|
+
_dist_tv,
|
|
14
|
+
)
|
|
7
15
|
from ._helpers import get_phi
|
|
8
16
|
|
|
9
17
|
dist_funcs = {
|
|
@@ -19,11 +27,12 @@ dist_funcs = {
|
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
def get_closest_topics(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
models: List[Any],
|
|
31
|
+
ref: int = 0,
|
|
32
|
+
method: str = "sklb",
|
|
33
|
+
top_words: int = 100,
|
|
34
|
+
verbose: bool = True,
|
|
35
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
27
36
|
"""Finding closest topics in models.
|
|
28
37
|
|
|
29
38
|
Parameters
|
|
@@ -93,7 +102,6 @@ def get_closest_topics(
|
|
|
93
102
|
|
|
94
103
|
# Iterating over all models
|
|
95
104
|
for mid, model in enum_func(models):
|
|
96
|
-
|
|
97
105
|
# Current model is equal to reference model, skipping
|
|
98
106
|
if mid == ref:
|
|
99
107
|
continue
|
|
@@ -105,7 +113,8 @@ def get_closest_topics(
|
|
|
105
113
|
for t_ref in range(topics_num):
|
|
106
114
|
for t in range(topics_num):
|
|
107
115
|
all_vs_all_dists[t_ref, t] = dist_func(
|
|
108
|
-
model_ref_phi.iloc[:, t_ref], get_phi(model).iloc[:, t]
|
|
116
|
+
model_ref_phi.iloc[:, t_ref], get_phi(model).iloc[:, t]
|
|
117
|
+
)
|
|
109
118
|
|
|
110
119
|
# Creating two arrays for the closest topics ids and distance values
|
|
111
120
|
if method == "jac":
|
|
@@ -119,14 +128,15 @@ def get_closest_topics(
|
|
|
119
128
|
|
|
120
129
|
|
|
121
130
|
def get_stable_topics(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
closest_topics: np.ndarray,
|
|
132
|
+
dist: np.ndarray,
|
|
133
|
+
norm: bool = True,
|
|
134
|
+
inverse: bool = True,
|
|
135
|
+
inverse_factor: float = 1.0,
|
|
136
|
+
ref: int = 0,
|
|
137
|
+
thres: float = 0.9,
|
|
138
|
+
thres_models: int = 2,
|
|
139
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
130
140
|
"""Finding stable topics in models.
|
|
131
141
|
|
|
132
142
|
Parameters
|
|
@@ -179,7 +189,5 @@ def get_stable_topics(
|
|
|
179
189
|
dist_arr = np.asarray(dist)
|
|
180
190
|
dist_ready = dist_arr / dist_arr.max() if norm else dist_arr.copy()
|
|
181
191
|
dist_ready = inverse_factor - dist_ready if inverse else dist_ready
|
|
182
|
-
mask = (
|
|
183
|
-
np.sum(np.delete(dist_ready, ref, axis=1) >= thres, axis=1)
|
|
184
|
-
>= thres_models)
|
|
192
|
+
mask = np.sum(np.delete(dist_ready, ref, axis=1) >= thres, axis=1) >= thres_models
|
|
185
193
|
return closest_topics[mask], dist_ready[mask]
|
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
# TODO: heatmap of docs in topics
|
|
2
2
|
# TODO: topic dynamics in time
|
|
3
3
|
# TODO: word cloud
|
|
4
|
-
__all__ = [
|
|
5
|
-
|
|
6
|
-
from
|
|
4
|
+
__all__ = ["plot_scatter_topics", "plot_terms", "plot_docs"]
|
|
5
|
+
from typing import Optional, Union, Sequence
|
|
6
|
+
from IPython.display import HTML
|
|
7
7
|
from pandas import DataFrame, option_context
|
|
8
8
|
from numpy import ndarray
|
|
9
9
|
from altair import (
|
|
10
|
-
AxisConfig,
|
|
10
|
+
AxisConfig,
|
|
11
|
+
Chart,
|
|
12
|
+
X,
|
|
13
|
+
Y,
|
|
14
|
+
LayerChart,
|
|
15
|
+
Size,
|
|
16
|
+
Color,
|
|
17
|
+
value,
|
|
18
|
+
Text,
|
|
19
|
+
Scale,
|
|
20
|
+
Legend,
|
|
21
|
+
)
|
|
11
22
|
|
|
12
23
|
|
|
13
24
|
def plot_scatter_topics(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
topics_coords: Union[ndarray, DataFrame],
|
|
26
|
+
x_col: str = "x",
|
|
27
|
+
y_col: str = "y",
|
|
28
|
+
topic: int = None,
|
|
29
|
+
size_col: str = None,
|
|
30
|
+
label_col: str = None,
|
|
31
|
+
color_col: str = None,
|
|
32
|
+
topic_col: str = None,
|
|
33
|
+
font_size: int = 13,
|
|
34
|
+
x_kws: dict = None,
|
|
35
|
+
y_kws: dict = None,
|
|
36
|
+
chart_kws: dict = None,
|
|
37
|
+
circle_kws: dict = None,
|
|
38
|
+
circle_enc_kws: dict = None,
|
|
39
|
+
text_kws: dict = None,
|
|
40
|
+
text_enc_kws: dict = None,
|
|
41
|
+
size_kws: dict = None,
|
|
42
|
+
color_kws: dict = None,
|
|
43
|
+
) -> LayerChart:
|
|
32
44
|
"""Topics scatter plot in 2D.
|
|
33
45
|
|
|
34
46
|
Parameters
|
|
@@ -83,18 +95,19 @@ def plot_scatter_topics(
|
|
|
83
95
|
chart_kws = {}
|
|
84
96
|
|
|
85
97
|
if not x_kws:
|
|
86
|
-
x_kws = {
|
|
98
|
+
x_kws = {"shorthand": x_col, "axis": None}
|
|
87
99
|
|
|
88
100
|
if not y_kws:
|
|
89
|
-
y_kws = {
|
|
101
|
+
y_kws = {"shorthand": y_col, "axis": None}
|
|
90
102
|
|
|
91
103
|
if not circle_kws:
|
|
92
|
-
circle_kws = {"opacity": 0.33, "stroke":
|
|
104
|
+
circle_kws = {"opacity": 0.33, "stroke": "black", "strokeWidth": 1}
|
|
93
105
|
|
|
94
106
|
if not size_kws:
|
|
95
107
|
size_kws = {
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
"title": "Marginal topic distribution",
|
|
109
|
+
"scale": Scale(range=[0, 3000]),
|
|
110
|
+
}
|
|
98
111
|
|
|
99
112
|
if not circle_enc_kws:
|
|
100
113
|
circle_enc_kws = {
|
|
@@ -102,21 +115,24 @@ def plot_scatter_topics(
|
|
|
102
115
|
"y": Y(**y_kws),
|
|
103
116
|
"size": Size(size_col, **size_kws)
|
|
104
117
|
if size_col and not topics_coords[size_col].isna().any()
|
|
105
|
-
else value(500)
|
|
118
|
+
else value(500),
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
if not text_kws:
|
|
109
122
|
text_kws = {"align": "center", "baseline": "middle"}
|
|
110
123
|
|
|
111
124
|
if not color_kws:
|
|
112
|
-
color_kws =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
color_kws = (
|
|
126
|
+
{}
|
|
127
|
+
if topic is None
|
|
128
|
+
else {"condition": {"test": f"datum['topic'] == {topic}", "value": "red"}}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
data = (
|
|
132
|
+
DataFrame(topics_coords, columns=[x_col, y_col])
|
|
133
|
+
if isinstance(topics_coords, ndarray)
|
|
119
134
|
else topics_coords.copy()
|
|
135
|
+
)
|
|
120
136
|
|
|
121
137
|
if not topic_col:
|
|
122
138
|
topic_col = "topic"
|
|
@@ -127,7 +143,8 @@ def plot_scatter_topics(
|
|
|
127
143
|
"x": X(**x_kws),
|
|
128
144
|
"y": Y(**y_kws),
|
|
129
145
|
"text": Text(topic_col),
|
|
130
|
-
"size": value(font_size)
|
|
146
|
+
"size": value(font_size),
|
|
147
|
+
}
|
|
131
148
|
|
|
132
149
|
# Tooltips initialization
|
|
133
150
|
tooltips = []
|
|
@@ -137,57 +154,47 @@ def plot_scatter_topics(
|
|
|
137
154
|
tooltips.append(size_col)
|
|
138
155
|
|
|
139
156
|
if tooltips:
|
|
140
|
-
circle_enc_kws.update({
|
|
141
|
-
text_enc_kws.update({
|
|
157
|
+
circle_enc_kws.update({"tooltip": tooltips})
|
|
158
|
+
text_enc_kws.update({"tooltip": tooltips})
|
|
142
159
|
|
|
143
160
|
if color_kws:
|
|
144
|
-
circle_enc_kws.update({
|
|
161
|
+
circle_enc_kws.update({"color": Color(**color_kws)})
|
|
145
162
|
|
|
146
163
|
base = Chart(data, **chart_kws)
|
|
147
164
|
|
|
148
|
-
rule = base
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.mark_circle(**circle_kws)\
|
|
164
|
-
.encode(**circle_enc_kws)
|
|
165
|
-
|
|
166
|
-
text = base\
|
|
167
|
-
.mark_text(**text_kws)\
|
|
168
|
-
.encode(**text_enc_kws)
|
|
169
|
-
|
|
170
|
-
return (rule + rule2 + points + text)\
|
|
171
|
-
.configure_axis(labelFontSize=font_size, titleFontSize=font_size, grid=False)\
|
|
172
|
-
.configure(axis=AxisConfig(disable=True))\
|
|
173
|
-
.configure_view(stroke='transparent', strokeWidth=0)\
|
|
165
|
+
rule = base.mark_rule().encode(y="average(y)", color=value("gray"), size=value(0.2))
|
|
166
|
+
|
|
167
|
+
rule2 = base.mark_rule().encode(
|
|
168
|
+
x="average(x)", color=value("gray"), size=value(0.2)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
points = base.mark_circle(**circle_kws).encode(**circle_enc_kws)
|
|
172
|
+
|
|
173
|
+
text = base.mark_text(**text_kws).encode(**text_enc_kws)
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
(rule + rule2 + points + text)
|
|
177
|
+
.configure_axis(labelFontSize=font_size, titleFontSize=font_size, grid=False)
|
|
178
|
+
.configure(axis=AxisConfig(disable=True))
|
|
179
|
+
.configure_view(stroke="transparent", strokeWidth=0)
|
|
174
180
|
.configure_legend(
|
|
175
|
-
orient=
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
orient="bottom", labelFontSize=font_size, titleFontSize=font_size
|
|
182
|
+
)
|
|
183
|
+
)
|
|
178
184
|
|
|
179
185
|
|
|
180
186
|
def plot_terms(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
terms_probs: DataFrame,
|
|
188
|
+
x_col: str = "Probability",
|
|
189
|
+
y_col: str = "Terms",
|
|
190
|
+
color_col: str = "Type",
|
|
191
|
+
font_size: int = 13,
|
|
192
|
+
chart_kws: Optional[dict] = None,
|
|
193
|
+
bar_kws: Optional[dict] = None,
|
|
194
|
+
x_kws: Optional[dict] = None,
|
|
195
|
+
y_kws: Optional[dict] = None,
|
|
196
|
+
color_kws: Optional[dict] = None,
|
|
197
|
+
) -> Chart:
|
|
191
198
|
"""Plot words conditional and marginal probabilities.
|
|
192
199
|
|
|
193
200
|
Parameters
|
|
@@ -219,37 +226,36 @@ def plot_terms(
|
|
|
219
226
|
Terms probabilities chart.
|
|
220
227
|
"""
|
|
221
228
|
if not x_kws:
|
|
222
|
-
x_kws = {
|
|
229
|
+
x_kws = {"stack": None}
|
|
223
230
|
if not y_kws:
|
|
224
|
-
y_kws = {
|
|
231
|
+
y_kws = {"sort": None, "title": None}
|
|
225
232
|
if not color_kws:
|
|
226
233
|
color_kws = {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
234
|
+
"shorthand": color_col,
|
|
235
|
+
"legend": Legend(orient="bottom"),
|
|
236
|
+
"scale": Scale(scheme="category20"),
|
|
230
237
|
}
|
|
231
238
|
if not chart_kws:
|
|
232
239
|
chart_kws = {}
|
|
233
240
|
if not bar_kws:
|
|
234
241
|
bar_kws = {}
|
|
235
242
|
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
color=Color(**color_kws)
|
|
242
|
-
)\
|
|
243
|
-
.configure_axis(labelFontSize=font_size, titleFontSize=font_size)\
|
|
243
|
+
return (
|
|
244
|
+
Chart(data=terms_probs, **chart_kws)
|
|
245
|
+
.mark_bar(**bar_kws)
|
|
246
|
+
.encode(x=X(x_col, **x_kws), y=Y(y_col, **y_kws), color=Color(**color_kws))
|
|
247
|
+
.configure_axis(labelFontSize=font_size, titleFontSize=font_size)
|
|
244
248
|
.configure_legend(
|
|
245
|
-
labelFontSize=font_size, titleFontSize=font_size,
|
|
246
|
-
|
|
249
|
+
labelFontSize=font_size, titleFontSize=font_size, columns=1, labelLimit=250
|
|
250
|
+
)
|
|
251
|
+
)
|
|
247
252
|
|
|
248
253
|
|
|
249
254
|
def plot_docs(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
docs: Union[Sequence[str], DataFrame],
|
|
256
|
+
styles: Optional[str] = None,
|
|
257
|
+
html_kws: Optional[dict] = None,
|
|
258
|
+
) -> HTML:
|
|
253
259
|
"""Documents plotting functionality for report interface.
|
|
254
260
|
|
|
255
261
|
Parameters
|
|
@@ -267,11 +273,11 @@ def plot_docs(
|
|
|
267
273
|
ipywidgets.HTML
|
|
268
274
|
Topic documents.
|
|
269
275
|
"""
|
|
270
|
-
from IPython.display import HTML
|
|
271
|
-
|
|
272
276
|
if styles is None:
|
|
273
|
-
styles =
|
|
274
|
-
|
|
277
|
+
styles = (
|
|
278
|
+
"<style>table td{text-align: left !important}"
|
|
279
|
+
+ "table th{text-align: center !important}</style>"
|
|
280
|
+
)
|
|
275
281
|
if html_kws is None:
|
|
276
282
|
# html_kws = {'classes': 'plot'}
|
|
277
283
|
html_kws = {}
|
|
@@ -279,8 +285,8 @@ def plot_docs(
|
|
|
279
285
|
if isinstance(docs, DataFrame):
|
|
280
286
|
df_docs = docs.copy()
|
|
281
287
|
else:
|
|
282
|
-
df_docs = DataFrame({
|
|
288
|
+
df_docs = DataFrame({"docs": docs})
|
|
283
289
|
|
|
284
|
-
with option_context(
|
|
290
|
+
with option_context("display.max_colwidth", 0):
|
|
285
291
|
# df_docs.style.set_properties(**{'text-align': 'center'})
|
|
286
292
|
return HTML(styles + df_docs.to_html(**html_kws))
|
|
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
|