tmplot 0.1.2__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.2/src/tmplot.egg-info → tmplot-0.2.0}/PKG-INFO +8 -2
- {tmplot-0.1.2 → tmplot-0.2.0}/README.md +6 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/__init__.py +1 -1
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/_distance.py +50 -49
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/_helpers.py +102 -99
- tmplot-0.2.0/src/tmplot/_report.py +334 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/_stability.py +29 -21
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/_vis.py +108 -102
- {tmplot-0.1.2 → tmplot-0.2.0/src/tmplot.egg-info}/PKG-INFO +8 -2
- tmplot-0.1.2/src/tmplot/_report.py +0 -309
- {tmplot-0.1.2 → tmplot-0.2.0}/LICENSE +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/MANIFEST.in +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/pyproject.toml +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/setup.cfg +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot/_metrics.py +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot.egg-info/SOURCES.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot.egg-info/dependency_links.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot.egg-info/requires.txt +0 -0
- {tmplot-0.1.2 → tmplot-0.2.0}/src/tmplot.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: tmplot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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,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
|
)
|
|
@@ -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
|
+
) -> ndarray:
|
|
248
253
|
"""Calculate marginal topics probabilities.
|
|
249
254
|
|
|
250
255
|
Parameters
|
|
@@ -259,18 +264,18 @@ def calc_topics_marg_probs(
|
|
|
259
264
|
Union[pandas.DataFrame, numpy.ndarray]
|
|
260
265
|
Marginal topics probabilities.
|
|
261
266
|
"""
|
|
267
|
+
p_t = array(theta).sum(axis=1)
|
|
268
|
+
p_t /= p_t.sum()
|
|
262
269
|
if topic_id is not None:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if isinstance(theta, DataFrame):
|
|
266
|
-
return theta.iloc[topic_id, :].sum()
|
|
267
|
-
|
|
268
|
-
return theta.sum(axis=1)
|
|
270
|
+
return p_t[topic_id]
|
|
271
|
+
return p_t
|
|
269
272
|
|
|
270
273
|
|
|
271
274
|
def calc_terms_marg_probs(
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
phi: Union[ndarray, DataFrame],
|
|
276
|
+
p_t: Union[ndarray, Series],
|
|
277
|
+
word_id: Optional[int] = None,
|
|
278
|
+
) -> ndarray:
|
|
274
279
|
"""Calculate marginal terms probabilities.
|
|
275
280
|
|
|
276
281
|
Parameters
|
|
@@ -285,19 +290,13 @@ def calc_terms_marg_probs(
|
|
|
285
290
|
Union[numpy.ndarray, pandas.Series]
|
|
286
291
|
Marginal terms probabilities.
|
|
287
292
|
"""
|
|
293
|
+
p_w = (array(phi) * array(p_t)).sum(axis=1)
|
|
288
294
|
if word_id is not None:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if isinstance(phi, DataFrame):
|
|
292
|
-
return phi.iloc[word_id, :].sum()
|
|
293
|
-
|
|
294
|
-
return phi.sum(axis=1)
|
|
295
|
+
return p_w[word_id]
|
|
296
|
+
return p_w
|
|
295
297
|
|
|
296
298
|
|
|
297
|
-
def get_salient_terms(
|
|
298
|
-
terms_freqs: ndarray,
|
|
299
|
-
phi: ndarray,
|
|
300
|
-
theta: ndarray) -> ndarray:
|
|
299
|
+
def get_salient_terms(phi: ndarray, theta: ndarray) -> ndarray:
|
|
301
300
|
"""Get salient terms.
|
|
302
301
|
|
|
303
302
|
Calculated as:
|
|
@@ -306,8 +305,6 @@ def get_salient_terms(
|
|
|
306
305
|
|
|
307
306
|
Parameters
|
|
308
307
|
----------
|
|
309
|
-
terms_freqs : numpy.ndarray
|
|
310
|
-
Words frequencies.
|
|
311
308
|
phi : numpy.ndarray
|
|
312
309
|
Words vs topics matrix.
|
|
313
310
|
theta : numpy.ndarray
|
|
@@ -318,18 +315,24 @@ def get_salient_terms(
|
|
|
318
315
|
numpy.ndarray
|
|
319
316
|
Terms saliency values.
|
|
320
317
|
"""
|
|
321
|
-
p_t =
|
|
322
|
-
p_w =
|
|
318
|
+
p_t = calc_topics_marg_probs(theta)
|
|
319
|
+
p_w = calc_terms_marg_probs(phi, p_t)
|
|
323
320
|
|
|
324
321
|
def _p_tw(phi, w, t):
|
|
325
|
-
return phi[w, t] * p_t[t] / p_w[w]
|
|
326
|
-
|
|
327
|
-
saliency = array(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
322
|
+
return array(phi)[w, t] * p_t[t] / p_w[w]
|
|
323
|
+
|
|
324
|
+
saliency = array(
|
|
325
|
+
[
|
|
326
|
+
p_w[w]
|
|
327
|
+
* sum(
|
|
328
|
+
(
|
|
329
|
+
_p_tw(phi, w, t) * log(_p_tw(phi, w, t) / p_t[t])
|
|
330
|
+
for t in range(phi.shape[1])
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
for w in range(phi.shape[0])
|
|
334
|
+
]
|
|
335
|
+
)
|
|
333
336
|
# saliency(term w) = frequency(w)
|
|
334
337
|
# * [sum_t p(t | w) * log(p(t | w)/p(t))] for topics t
|
|
335
338
|
# p(t | w) = p(w | t) * p(t) / p(w)
|
|
@@ -337,10 +340,8 @@ def get_salient_terms(
|
|
|
337
340
|
|
|
338
341
|
|
|
339
342
|
def calc_terms_probs_ratio(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
terms_num: int = 30,
|
|
343
|
-
lambda_: float = 0.6) -> DataFrame:
|
|
343
|
+
phi: DataFrame, topic: int, terms_num: int = 30, lambda_: float = 0.6
|
|
344
|
+
) -> DataFrame:
|
|
344
345
|
"""Get terms conditional and marginal probabilities.
|
|
345
346
|
|
|
346
347
|
Parameters
|
|
@@ -368,35 +369,38 @@ def calc_terms_probs_ratio(
|
|
|
368
369
|
pandas.DataFrame
|
|
369
370
|
Words conditional and marginal probabilities.
|
|
370
371
|
"""
|
|
371
|
-
p_cond_name =
|
|
372
|
-
p_cond =
|
|
373
|
-
.rename(p_cond_name)
|
|
374
|
-
if isinstance(phi, DataFrame)
|
|
372
|
+
p_cond_name = "Conditional term probability, p(w | t)"
|
|
373
|
+
p_cond = (
|
|
374
|
+
phi.iloc[:, topic].rename(p_cond_name)
|
|
375
|
+
if isinstance(phi, DataFrame)
|
|
375
376
|
else Series(phi[:, topic], name=p_cond_name)
|
|
377
|
+
)
|
|
376
378
|
|
|
377
|
-
p_marg_name =
|
|
378
|
-
p_marg =
|
|
379
|
-
.rename(p_marg_name)
|
|
380
|
-
if isinstance(phi, DataFrame)
|
|
379
|
+
p_marg_name = "Marginal term probability, p(w)"
|
|
380
|
+
p_marg = (
|
|
381
|
+
phi.sum(axis=1).rename(p_marg_name)
|
|
382
|
+
if isinstance(phi, DataFrame)
|
|
381
383
|
else Series(phi[:, topic], name=p_marg_name)
|
|
384
|
+
)
|
|
382
385
|
|
|
383
386
|
terms_probs = concat((p_marg, p_cond), axis=1)
|
|
384
387
|
relevant_idx = get_relevant_terms(phi, topic, lambda_).index
|
|
385
388
|
terms_probs_slice = terms_probs.loc[relevant_idx].head(terms_num)
|
|
386
389
|
|
|
387
|
-
return
|
|
388
|
-
.reset_index(drop=False)
|
|
390
|
+
return (
|
|
391
|
+
terms_probs_slice.reset_index(drop=False)
|
|
389
392
|
.melt(
|
|
390
393
|
id_vars=[terms_probs_slice.index.name],
|
|
391
|
-
var_name=
|
|
392
|
-
value_name=
|
|
393
|
-
|
|
394
|
+
var_name="Type",
|
|
395
|
+
value_name="Probability",
|
|
396
|
+
)
|
|
397
|
+
.rename(columns={terms_probs_slice.index.name: "Terms"})
|
|
398
|
+
)
|
|
394
399
|
|
|
395
400
|
|
|
396
401
|
def get_relevant_terms(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
lambda_: float = 0.6) -> Series:
|
|
402
|
+
phi: Union[ndarray, DataFrame], topic: int, lambda_: float = 0.6
|
|
403
|
+
) -> Series:
|
|
400
404
|
"""Select relevant terms.
|
|
401
405
|
|
|
402
406
|
Parameters
|
|
@@ -422,11 +426,10 @@ def get_relevant_terms(
|
|
|
422
426
|
pandas.Series
|
|
423
427
|
Terms sorted by relevance (descendingly).
|
|
424
428
|
"""
|
|
425
|
-
phi_topic = phi.iloc[:, topic]
|
|
426
|
-
if isinstance(phi, DataFrame)\
|
|
427
|
-
else phi[:, topic]
|
|
429
|
+
phi_topic = phi.iloc[:, topic] if isinstance(phi, DataFrame) else phi[:, topic]
|
|
428
430
|
|
|
429
431
|
# relevance = lambda * log(p(w | t)) + (1 - lambda) * log(p(w | t) / p(w))
|
|
430
|
-
relevance = lambda_ * nplog(phi_topic)
|
|
431
|
-
|
|
432
|
+
relevance = lambda_ * nplog(phi_topic) + (1 - lambda_) * nplog(
|
|
433
|
+
phi_topic / phi.sum(axis=1)
|
|
434
|
+
)
|
|
432
435
|
return relevance.sort_values(ascending=False)
|