PineBioML 1.2.0__py3-none-any.whl
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.
- PineBioML/IO/__init__.py +158 -0
- PineBioML/__init__.py +0 -0
- PineBioML/model/__init__.py +0 -0
- PineBioML/model/supervised/Classification.py +989 -0
- PineBioML/model/supervised/Regression.py +783 -0
- PineBioML/model/supervised/Survival.py +1 -0
- PineBioML/model/supervised/__init__.py +414 -0
- PineBioML/model/utils.py +393 -0
- PineBioML/preprocessing/__init__.py +168 -0
- PineBioML/preprocessing/bagging.py +221 -0
- PineBioML/preprocessing/impute.py +143 -0
- PineBioML/preprocessing/utils.py +161 -0
- PineBioML/report/__init__.py +0 -0
- PineBioML/report/utils.py +763 -0
- PineBioML/selection/Volcano.py +201 -0
- PineBioML/selection/__init__.py +145 -0
- PineBioML/selection/classification.py +836 -0
- PineBioML/selection/regression.py +737 -0
- PineBioML-1.2.0.dist-info/LICENSE +21 -0
- PineBioML-1.2.0.dist-info/METADATA +148 -0
- PineBioML-1.2.0.dist-info/RECORD +23 -0
- PineBioML-1.2.0.dist-info/WHEEL +5 -0
- PineBioML-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
from matplotlib.colors import Normalize
|
|
9
|
+
from seaborn import scatterplot, heatmap, pairplot, color_palette
|
|
10
|
+
|
|
11
|
+
from sklearn.decomposition import PCA
|
|
12
|
+
from sklearn.cross_decomposition import PLSRegression
|
|
13
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
14
|
+
import sklearn.metrics as metrics
|
|
15
|
+
|
|
16
|
+
from umap import UMAP
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class basic_plot(ABC):
|
|
20
|
+
"""
|
|
21
|
+
the base class of plots.
|
|
22
|
+
Using make_figure(x, y) to generate a figure.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self,
|
|
27
|
+
prefix: str = "",
|
|
28
|
+
save_path: str = "./",
|
|
29
|
+
save_fig: bool = False,
|
|
30
|
+
show_fig: bool = True):
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
35
|
+
save_path (str, optional): the path to export the figure. Defaults to "./".
|
|
36
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to False.
|
|
37
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
self.prefix = prefix
|
|
41
|
+
self.save_path = save_path
|
|
42
|
+
self.save_fig = save_fig
|
|
43
|
+
self.show_fig = show_fig
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def save_name(self):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def reference(self) -> dict[str, str]:
|
|
50
|
+
"""
|
|
51
|
+
This function will return reference of this method in python dict.
|
|
52
|
+
If you want to access it in PineBioML api document, then click on the >Expand source code
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
dict[str, str]: a dict of reference.
|
|
56
|
+
"""
|
|
57
|
+
refer = {
|
|
58
|
+
"matplotlib document": "https://matplotlib.org/",
|
|
59
|
+
"seaborn document": "https://seaborn.pydata.org/"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return refer
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def draw(self, x: pd.DataFrame, y: pd.Series = None):
|
|
66
|
+
"""
|
|
67
|
+
How and what to draw should be implemented in here.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
x (pd.DataFrame): feature
|
|
71
|
+
y (pd.Series, optional): label. Defaults to None.
|
|
72
|
+
"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def make_figure(self, x: pd.DataFrame, y: pd.Series = None):
|
|
76
|
+
"""
|
|
77
|
+
1. draw(x, y)
|
|
78
|
+
2. To save or to show the result.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
x (pd.DataFrame): features
|
|
82
|
+
y (pd.Series, optional): label. Defaults to None.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
self.draw(x, y)
|
|
86
|
+
|
|
87
|
+
if self.save_fig:
|
|
88
|
+
plt.savefig(self.save_path + self.save_name(), bbox_inches='tight')
|
|
89
|
+
if self.show_fig:
|
|
90
|
+
plt.show()
|
|
91
|
+
else:
|
|
92
|
+
plt.clf()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class pca_plot(basic_plot):
|
|
96
|
+
"""
|
|
97
|
+
Calling pca_plot().make_figure(x) or pca_plot().make_figure(x, y) to draw a pca plot of x with n_pc components.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(self,
|
|
102
|
+
n_pc: int = 4,
|
|
103
|
+
discrete_legend: bool = True,
|
|
104
|
+
prefix: str = "",
|
|
105
|
+
save_path: str = "./output/images/",
|
|
106
|
+
save_fig: bool = True,
|
|
107
|
+
show_fig: bool = True):
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
n_pc (int, optional): number of precipal compoment to plot. Defaults to 4.
|
|
112
|
+
discrete_legend (bool, optional): To color the plot based on y in discrete hue or continuous color bar. If y is continuous, then you should set it to False. Defaults to True.
|
|
113
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
114
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
115
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
116
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
117
|
+
"""
|
|
118
|
+
super().__init__(prefix=prefix,
|
|
119
|
+
save_path=save_path,
|
|
120
|
+
save_fig=save_fig,
|
|
121
|
+
show_fig=show_fig)
|
|
122
|
+
self.n_pc = n_pc
|
|
123
|
+
self.discrete_legend = discrete_legend
|
|
124
|
+
self.name = "PCA"
|
|
125
|
+
|
|
126
|
+
def save_name(self) -> str:
|
|
127
|
+
"""
|
|
128
|
+
Returns:
|
|
129
|
+
str: {prefix} PCA plot
|
|
130
|
+
"""
|
|
131
|
+
return "{} {} plot".format(self.prefix, self.name)
|
|
132
|
+
|
|
133
|
+
def reference(self) -> dict[str, str]:
|
|
134
|
+
"""
|
|
135
|
+
This function will return reference of this method in python dict.
|
|
136
|
+
If you want to access it in PineBioML api document, then click on the >Expand source code
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
dict[str, str]: a dict of reference.
|
|
140
|
+
"""
|
|
141
|
+
refer = super().reference()
|
|
142
|
+
refer[
|
|
143
|
+
self.name +
|
|
144
|
+
" document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html"
|
|
145
|
+
|
|
146
|
+
return refer
|
|
147
|
+
|
|
148
|
+
def draw(self, x: pd.DataFrame, y: pd.Series = None):
|
|
149
|
+
"""
|
|
150
|
+
Using x to draw a pca plot. The difference between pca_plot().draw(x) and pca_plot().draw(x, y) is that:
|
|
151
|
+
- pca_plot().draw(x) will give a normal pca plot.
|
|
152
|
+
- pca_plot().draw(x, y) will coloring the points on the figure based on y. Set discrete_legend to True if y is continuous, False otherwise.
|
|
153
|
+
|
|
154
|
+
What we do here is:
|
|
155
|
+
1. standardize x
|
|
156
|
+
2. fit a PCA(n_pc) on x and storing the result in pd.DataFrame
|
|
157
|
+
3. Decide how to color the plots by various cases of y and discrete_legend.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
x (pd.DataFrame): feature
|
|
161
|
+
y (pd.Series, optional): label. Defaults to None.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
# calculate pca and store in pd.DataFrame
|
|
165
|
+
pcs = PCA(self.n_pc).fit_transform((x - x.mean()) / (x.std() + 1e-4))
|
|
166
|
+
pcs = pd.DataFrame(
|
|
167
|
+
pcs,
|
|
168
|
+
index=x.index,
|
|
169
|
+
columns=["pc_" + str(i + 1) for i in range(self.n_pc)])
|
|
170
|
+
|
|
171
|
+
if y is None:
|
|
172
|
+
y_name = None
|
|
173
|
+
else:
|
|
174
|
+
# add y into the DataFrame for coloring the data points
|
|
175
|
+
y_name = "y" if y.name is None else y.name
|
|
176
|
+
pcs[y_name] = y
|
|
177
|
+
|
|
178
|
+
if self.discrete_legend:
|
|
179
|
+
# discrete legend
|
|
180
|
+
plot = pairplot(data=pcs, hue=y_name)
|
|
181
|
+
elif not y is None:
|
|
182
|
+
# color bar
|
|
183
|
+
cmap = color_palette('ch:', as_cmap=True)
|
|
184
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=Normalize())
|
|
185
|
+
sm.set_array([])
|
|
186
|
+
plot = pairplot(data=pcs, hue=y_name)
|
|
187
|
+
plot.legend.remove()
|
|
188
|
+
|
|
189
|
+
cbar = plt.colorbar(sm, ax=plot.axes)
|
|
190
|
+
cbar.set_label(y_name)
|
|
191
|
+
else:
|
|
192
|
+
# vanilla
|
|
193
|
+
plot = pairplot(data=pcs)
|
|
194
|
+
|
|
195
|
+
plot.figure.suptitle("{} {} Scatter plot".format(
|
|
196
|
+
self.prefix, self.name),
|
|
197
|
+
y=1.01)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class pls_plot(basic_plot):
|
|
201
|
+
"""
|
|
202
|
+
PLS-DA is a supervied method in dimension decomposition.
|
|
203
|
+
This function will plot the result of PLS-DA of given data.
|
|
204
|
+
|
|
205
|
+
Using pls_plot().make_figure(x, y) to generate a figure.
|
|
206
|
+
|
|
207
|
+
Warning: PLS-DA is a limited tool in multi-class classification and unlinear regression problem.
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
def __init__(self,
|
|
212
|
+
is_classification: bool,
|
|
213
|
+
discrete_legend: bool = True,
|
|
214
|
+
prefix: str = "",
|
|
215
|
+
save_path: str = "./output/images/",
|
|
216
|
+
save_fig: bool = True,
|
|
217
|
+
show_fig: bool = True):
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
is_classification (bool): If (x, y) is a classification task, then set is_classification to True.
|
|
222
|
+
discrete_legend (bool, optional): To color the plot based on y in discrete hue or continuous color bar. If y is continuous, then you should set it to False. Defaults to True.
|
|
223
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
224
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
225
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
226
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
227
|
+
"""
|
|
228
|
+
super().__init__(prefix=prefix,
|
|
229
|
+
save_path=save_path,
|
|
230
|
+
save_fig=save_fig,
|
|
231
|
+
show_fig=show_fig)
|
|
232
|
+
self.discrete_legend = discrete_legend
|
|
233
|
+
self.is_classification = is_classification
|
|
234
|
+
self.name = "PLS"
|
|
235
|
+
|
|
236
|
+
def save_name(self):
|
|
237
|
+
return "{} {} plot".format(self.prefix, self.name)
|
|
238
|
+
|
|
239
|
+
def reference(self) -> dict[str, str]:
|
|
240
|
+
"""
|
|
241
|
+
This function will return reference of this method in python dict.
|
|
242
|
+
If you want to access it in PineBioML api document, then click on the >Expand source code
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict[str, str]: a dict of reference.
|
|
246
|
+
"""
|
|
247
|
+
refer = super().reference()
|
|
248
|
+
refer[
|
|
249
|
+
self.name +
|
|
250
|
+
" document"] = "https://scikit-learn.org/stable/modules/generated/sklearn.cross_decomposition.PLSRegression.html"
|
|
251
|
+
|
|
252
|
+
return refer
|
|
253
|
+
|
|
254
|
+
def draw(self, x: pd.DataFrame, y: pd.Series):
|
|
255
|
+
"""
|
|
256
|
+
What we do here is:
|
|
257
|
+
1. If is_classification => one-hot encode the y.
|
|
258
|
+
2. Do PLS-DA
|
|
259
|
+
3. Decide how to color the plots by various cases of y and discrete_legend.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
x (pd.DataFrame): features
|
|
263
|
+
y (pd.Series, optional): label.
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
TypeError: _description_
|
|
267
|
+
"""
|
|
268
|
+
# one hot encoder for classification
|
|
269
|
+
if self.is_classification:
|
|
270
|
+
OneHot_y = OneHotEncoder(sparse_output=False).fit_transform(
|
|
271
|
+
y.to_numpy().reshape(-1, 1))
|
|
272
|
+
# fit pls regression
|
|
273
|
+
pls = PLSRegression(n_components=2).fit(x, OneHot_y)
|
|
274
|
+
else:
|
|
275
|
+
if y.dtype == "O":
|
|
276
|
+
raise TypeError(
|
|
277
|
+
"the dtype of y can't be object while is_classification was setting to False, which means it is a regression task and y should be float or int."
|
|
278
|
+
)
|
|
279
|
+
# fit pls regression
|
|
280
|
+
pls = PLSRegression(n_components=2).fit(x, y)
|
|
281
|
+
|
|
282
|
+
# project x
|
|
283
|
+
plscs = pls.transform(x)
|
|
284
|
+
plscs = pd.DataFrame(
|
|
285
|
+
plscs,
|
|
286
|
+
index=x.index,
|
|
287
|
+
columns=[self.name + " componet 1", self.name + " componet 2"])
|
|
288
|
+
|
|
289
|
+
if y is None:
|
|
290
|
+
y_name = None
|
|
291
|
+
else:
|
|
292
|
+
# add y into the DataFrame for coloring the data points
|
|
293
|
+
y_name = "y" if y.name is None else y.name
|
|
294
|
+
plscs[y_name] = y
|
|
295
|
+
|
|
296
|
+
if self.discrete_legend:
|
|
297
|
+
# discrete legend
|
|
298
|
+
plot = scatterplot(data=plscs,
|
|
299
|
+
x=self.name + " componet 1",
|
|
300
|
+
y=self.name + " componet 2",
|
|
301
|
+
hue=y_name)
|
|
302
|
+
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
|
303
|
+
elif not y is None:
|
|
304
|
+
# color bar
|
|
305
|
+
cmap = color_palette('ch:', as_cmap=True)
|
|
306
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=Normalize())
|
|
307
|
+
plot = scatterplot(data=plscs,
|
|
308
|
+
x=self.name + " componet 1",
|
|
309
|
+
y=self.name + " componet 2",
|
|
310
|
+
hue=y_name,
|
|
311
|
+
hue_norm=sm.norm,
|
|
312
|
+
palette=cmap,
|
|
313
|
+
legend=False)
|
|
314
|
+
cbar = plt.colorbar(sm, ax=plt.gca())
|
|
315
|
+
cbar.set_label(y_name)
|
|
316
|
+
else:
|
|
317
|
+
# vanilla
|
|
318
|
+
plot = scatterplot(data=plscs,
|
|
319
|
+
x=self.name + " componet 1",
|
|
320
|
+
y=self.name + " componet 2")
|
|
321
|
+
|
|
322
|
+
plot.set_title("{} {} Scatter plot".format(self.prefix, self.name))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class umap_plot(basic_plot):
|
|
326
|
+
"""
|
|
327
|
+
Umap is a unsupervissed method to reduce the number of dimension of given data. It's based on manifold learning and it has uncertainty.
|
|
328
|
+
|
|
329
|
+
Using umap_plot().make_figure(x) or umap_plot().make_figure(x, y) to generate a Umap plot.
|
|
330
|
+
|
|
331
|
+
Warning: Only the clustering tendency is reliable on the graph.
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
def __init__(self,
|
|
335
|
+
discrete_legend=True,
|
|
336
|
+
prefix="",
|
|
337
|
+
save_path="./output/images/",
|
|
338
|
+
save_fig=True,
|
|
339
|
+
show_fig=True):
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
discrete_legend (bool, optional): To color the plot based on y in discrete hue or continuous color bar. If y is continuous, then you should set it to False. Defaults to True.
|
|
344
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
345
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
346
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
347
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
348
|
+
"""
|
|
349
|
+
super().__init__(prefix=prefix,
|
|
350
|
+
save_path=save_path,
|
|
351
|
+
save_fig=save_fig,
|
|
352
|
+
show_fig=show_fig)
|
|
353
|
+
self.discrete_legend = discrete_legend
|
|
354
|
+
self.name = "UMAP"
|
|
355
|
+
|
|
356
|
+
def save_name(self):
|
|
357
|
+
return "{} {} plot".format(self.prefix, self.name)
|
|
358
|
+
|
|
359
|
+
def reference(self) -> dict[str, str]:
|
|
360
|
+
"""
|
|
361
|
+
This function will return reference of this method in python dict.
|
|
362
|
+
If you want to access it in PineBioML api document, then click on the >Expand source code
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
dict[str, str]: a dict of reference.
|
|
366
|
+
"""
|
|
367
|
+
refer = super().reference()
|
|
368
|
+
refer[self.name +
|
|
369
|
+
" document"] = "https://umap-learn.readthedocs.io/en/latest/"
|
|
370
|
+
refer[
|
|
371
|
+
self.name +
|
|
372
|
+
" publication"] = "https://joss.theoj.org/papers/10.21105/joss.00861"
|
|
373
|
+
|
|
374
|
+
return refer
|
|
375
|
+
|
|
376
|
+
def draw(self, x: pd.DataFrame, y: pd.Series = None):
|
|
377
|
+
"""
|
|
378
|
+
Using Umap do transform x into 2D plane. The difference between umap_plot().draw(x) and umap_plot().draw(x, y) is that:
|
|
379
|
+
- umap_plot().draw(x) will give a normal umap scatter plot.
|
|
380
|
+
- umap_plot().draw(x, y) will coloring the points on the figure based on y. Set discrete_legend to True if y is continuous, False otherwise.
|
|
381
|
+
|
|
382
|
+
What we do here is:
|
|
383
|
+
1. standardize x
|
|
384
|
+
2. fit a UMAP(n_neighbors=np.log2(n_sample) on x and storing the result in pd.DataFrame
|
|
385
|
+
3. Decide how to color the plots by various cases of y and discrete_legend.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
x (pd.DataFrame): feature
|
|
389
|
+
y (pd.Series, optional): label. Defaults to None.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
# fit a umap for x, you can change n_neighbors to any other feasible value.
|
|
393
|
+
umapcs = UMAP(n_neighbors=round(np.log2(x.shape[0])),
|
|
394
|
+
n_components=2).fit_transform(
|
|
395
|
+
(x - x.mean()) / (x.std() + 1e-6))
|
|
396
|
+
umapcs = pd.DataFrame(
|
|
397
|
+
umapcs,
|
|
398
|
+
index=x.index,
|
|
399
|
+
columns=[self.name + " dimension 1", self.name + " dimension 2"])
|
|
400
|
+
|
|
401
|
+
if y is None:
|
|
402
|
+
y_name = None
|
|
403
|
+
else:
|
|
404
|
+
# add y into the DataFrame for coloring the data points
|
|
405
|
+
y_name = "y" if y.name is None else y.name
|
|
406
|
+
umapcs[y_name] = y
|
|
407
|
+
|
|
408
|
+
if self.discrete_legend:
|
|
409
|
+
# discrete legend
|
|
410
|
+
plot = scatterplot(data=umapcs,
|
|
411
|
+
x=self.name + " dimension 1",
|
|
412
|
+
y=self.name + " dimension 2",
|
|
413
|
+
hue=y_name)
|
|
414
|
+
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
|
415
|
+
elif not y is None:
|
|
416
|
+
# color bar
|
|
417
|
+
cmap = color_palette('ch:', as_cmap=True)
|
|
418
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=Normalize())
|
|
419
|
+
plot = scatterplot(data=umapcs,
|
|
420
|
+
x=self.name + " dimension 1",
|
|
421
|
+
y=self.name + " dimension 2",
|
|
422
|
+
hue=y_name,
|
|
423
|
+
hue_norm=sm.norm,
|
|
424
|
+
palette=cmap,
|
|
425
|
+
legend=False)
|
|
426
|
+
cbar = plt.colorbar(sm, ax=plt.gca())
|
|
427
|
+
cbar.set_label(y_name)
|
|
428
|
+
else:
|
|
429
|
+
# vanilla
|
|
430
|
+
plot = scatterplot(data=umapcs,
|
|
431
|
+
x=self.name + " dimension 1",
|
|
432
|
+
y=self.name + " dimension 2")
|
|
433
|
+
|
|
434
|
+
plot.set_title("{} {} Scatter plot".format(self.prefix, self.name))
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class corr_heatmap_plot(basic_plot):
|
|
438
|
+
"""
|
|
439
|
+
plot correlation coefficients of given data in heatmap.
|
|
440
|
+
|
|
441
|
+
Using corr_heatmap_plot().make_figure(x) or corr_heatmap_plot().make_figure(x, y) to generate a figure.
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
def __init__(self,
|
|
445
|
+
prefix="",
|
|
446
|
+
save_path="./output/images/",
|
|
447
|
+
save_fig=True,
|
|
448
|
+
show_fig=True):
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
453
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
454
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
455
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
456
|
+
"""
|
|
457
|
+
super().__init__(prefix=prefix,
|
|
458
|
+
save_path=save_path,
|
|
459
|
+
save_fig=save_fig,
|
|
460
|
+
show_fig=show_fig)
|
|
461
|
+
self.name = "Correlation Heatmap"
|
|
462
|
+
|
|
463
|
+
def save_name(self):
|
|
464
|
+
return "{} {} plot".format(self.prefix, self.name)
|
|
465
|
+
|
|
466
|
+
def draw(self, x: pd.DataFrame, y: pd.Series = None):
|
|
467
|
+
"""
|
|
468
|
+
what we do here is:
|
|
469
|
+
1. add y into x as a new column if y is not None.
|
|
470
|
+
2. compute correlation between x's columns.
|
|
471
|
+
3. drawint the result in heatmap.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
x (pd.DataFrame): feature
|
|
475
|
+
y (pd.Series, optional): Label. Defaults to None.
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
data = x.copy()
|
|
479
|
+
if y is None:
|
|
480
|
+
y_name = None
|
|
481
|
+
else:
|
|
482
|
+
# add y into the DataFrame for coloring the data points
|
|
483
|
+
y_name = "y" if y.name is None else y.name
|
|
484
|
+
data[y_name] = y
|
|
485
|
+
|
|
486
|
+
plot = heatmap(data.corr(), vmin=-1, vmax=1, cmap='RdBu')
|
|
487
|
+
|
|
488
|
+
plot.set_title("{} {}".format(self.prefix, self.name))
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
class confusion_matrix_plot(basic_plot):
|
|
492
|
+
"""
|
|
493
|
+
plot confusion matrix of given ground true label and predictions.
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
def __init__(self,
|
|
497
|
+
prefix="",
|
|
498
|
+
save_path="./output/images/",
|
|
499
|
+
save_fig=True,
|
|
500
|
+
show_fig=True):
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
505
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
506
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
507
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
508
|
+
|
|
509
|
+
Todo:
|
|
510
|
+
value normalize by y_true and crowding problem in multi-class classification.
|
|
511
|
+
"""
|
|
512
|
+
super().__init__(prefix=prefix,
|
|
513
|
+
save_path=save_path,
|
|
514
|
+
save_fig=save_fig,
|
|
515
|
+
show_fig=show_fig)
|
|
516
|
+
self.name = "Confusion Matrix"
|
|
517
|
+
self.normalize = None
|
|
518
|
+
|
|
519
|
+
def save_name(self):
|
|
520
|
+
return "{} {}".format(self.prefix, self.name)
|
|
521
|
+
|
|
522
|
+
def draw(self, y_true: pd.Series, y_pred: pd.Series):
|
|
523
|
+
"""
|
|
524
|
+
plot the confusion matrix using y_true and y_pred.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
y_true (pd.Series): Ground true
|
|
528
|
+
y_pred (pd.Series): prediction from an estimator.
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
plot = metrics.ConfusionMatrixDisplay.from_predictions(
|
|
532
|
+
y_true,
|
|
533
|
+
y_pred,
|
|
534
|
+
normalize=self.normalize,
|
|
535
|
+
xticks_rotation="vertical")
|
|
536
|
+
plot.ax_.set_title("{} {}".format(self.prefix, self.name))
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class roc_plot(basic_plot):
|
|
540
|
+
"""
|
|
541
|
+
Depends on how the number of class in y_true and, pos_label, this function will plot a roc curve or several curves of given data.
|
|
542
|
+
"""
|
|
543
|
+
|
|
544
|
+
def __init__(self,
|
|
545
|
+
pos_label: Union[str, int, float] = None,
|
|
546
|
+
prefix="",
|
|
547
|
+
save_path="./output/images/",
|
|
548
|
+
save_fig=True,
|
|
549
|
+
show_fig=True):
|
|
550
|
+
"""
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
pos_label (Union[str, int, float], optional): If not None, the result will be pos_label vs rest (ovr) roc curve. Defaults to None.
|
|
554
|
+
prefix (str, optional): the describe or title for the plot. the prefix will be added into the title of plot and saving name. Defaults to "".
|
|
555
|
+
save_path (str, optional): the path to export the figure. Defaults to "./output/images/".
|
|
556
|
+
save_fig (bool, optional): whether to export the figure or not. Defaults to True.
|
|
557
|
+
show_fig (bool, optional): whether to show the figure or not. Defaults to True.
|
|
558
|
+
"""
|
|
559
|
+
super().__init__(prefix=prefix,
|
|
560
|
+
save_path=save_path,
|
|
561
|
+
save_fig=save_fig,
|
|
562
|
+
show_fig=show_fig)
|
|
563
|
+
self.name = "ROC Curve"
|
|
564
|
+
self.pos_label = pos_label
|
|
565
|
+
|
|
566
|
+
def save_name(self):
|
|
567
|
+
return "{} {}".format(self.prefix, self.name)
|
|
568
|
+
|
|
569
|
+
def draw(self, y_true: pd.Series, y_pred_prob: pd.DataFrame):
|
|
570
|
+
"""
|
|
571
|
+
draw roc curve
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
y_true (pd.Series): Ground true
|
|
575
|
+
y_pred_prob (pd.DataFrame): The probability that model predicted for each class. y_pred_prob should have shape (n_samples, n_class)
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
# ROC curve
|
|
579
|
+
if len(y_true.value_counts()) <= 2:
|
|
580
|
+
# binary ROC curve
|
|
581
|
+
fpr, tpr, threshold = metrics.roc_curve(y_true,
|
|
582
|
+
y_pred_prob.iloc[:, 1],
|
|
583
|
+
pos_label=self.pos_label)
|
|
584
|
+
roc_auc = metrics.auc(fpr, tpr)
|
|
585
|
+
plt.plot(fpr, tpr, 'b', label='AUC = %0.3f' % roc_auc)
|
|
586
|
+
plt.title(self.prefix + 'ROC curve')
|
|
587
|
+
|
|
588
|
+
else:
|
|
589
|
+
# one vs rest ROC curve
|
|
590
|
+
for label in y_pred_prob.columns:
|
|
591
|
+
label_prob = y_pred_prob[label]
|
|
592
|
+
|
|
593
|
+
fpr, tpr, threshold = metrics.roc_curve(
|
|
594
|
+
y_true == label, label_prob)
|
|
595
|
+
roc_auc = metrics.auc(fpr, tpr)
|
|
596
|
+
|
|
597
|
+
plt.plot(fpr, tpr, label=str(label) + ' (AUC=%0.3f)' % roc_auc)
|
|
598
|
+
plt.title(self.prefix + 'one vs rest ROC curves')
|
|
599
|
+
|
|
600
|
+
plt.plot([0, 1], [0, 1], 'r--')
|
|
601
|
+
plt.xlim([0, 1])
|
|
602
|
+
plt.ylim([0, 1])
|
|
603
|
+
plt.ylabel('True Positive Rate')
|
|
604
|
+
plt.xlabel('False Positive Rate')
|
|
605
|
+
plt.legend(loc='lower right')
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def data_overview(input_x: pd.DataFrame,
|
|
609
|
+
y: pd.Series,
|
|
610
|
+
is_classification: bool = True,
|
|
611
|
+
discrete_legend=True,
|
|
612
|
+
n_pc=4,
|
|
613
|
+
prefix="",
|
|
614
|
+
save_fig=True,
|
|
615
|
+
save_path="./output/images/",
|
|
616
|
+
show_fig=True):
|
|
617
|
+
"""
|
|
618
|
+
Make a glance to data. Specifically it will:
|
|
619
|
+
1. make a pca plot.
|
|
620
|
+
2. make a pls plot.
|
|
621
|
+
3. make a Umap plot.
|
|
622
|
+
4. make a correlation heatmap.
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
input_x (pd.DataFrame): the input feature (or the explanatory variables).
|
|
626
|
+
y (pd.Series): the target variable (or the response variable).
|
|
627
|
+
is_classification (bool, optional): whether the task to fit y is a classification task or a regression task. Defaults to True.
|
|
628
|
+
discrete_legend (bool, optional): whether to use a discrete legend, otherwise a color bar will be used. If is_classification is True, then discrete_legend will forced to be Ture. Defaults to True.
|
|
629
|
+
prefix (str, optional): the name or the description to the task. It will be add into the title and the export figure name. Defaults to "".
|
|
630
|
+
save_fig (bool, optional): To export the figure or not. Defaults to True.
|
|
631
|
+
save_path (str, optional): The export path of the figure. Defaults to "./output/images/".
|
|
632
|
+
show_fig (bool, optional): To show the figure before export or not. Defaults to True.
|
|
633
|
+
"""
|
|
634
|
+
|
|
635
|
+
x = input_x.copy()
|
|
636
|
+
if is_classification:
|
|
637
|
+
discrete_legend = True
|
|
638
|
+
|
|
639
|
+
# PCA
|
|
640
|
+
pca_plot(n_pc=n_pc,
|
|
641
|
+
discrete_legend=discrete_legend,
|
|
642
|
+
prefix=prefix,
|
|
643
|
+
save_path=save_path,
|
|
644
|
+
save_fig=save_fig,
|
|
645
|
+
show_fig=show_fig).make_figure(x, y)
|
|
646
|
+
|
|
647
|
+
# PLS
|
|
648
|
+
pls_plot(is_classification=is_classification,
|
|
649
|
+
discrete_legend=discrete_legend,
|
|
650
|
+
prefix=prefix,
|
|
651
|
+
save_path=save_path,
|
|
652
|
+
save_fig=save_fig,
|
|
653
|
+
show_fig=show_fig).make_figure(x, y)
|
|
654
|
+
|
|
655
|
+
# UMAP
|
|
656
|
+
umap_plot(discrete_legend=discrete_legend,
|
|
657
|
+
prefix=prefix,
|
|
658
|
+
save_path=save_path,
|
|
659
|
+
save_fig=save_fig,
|
|
660
|
+
show_fig=show_fig).make_figure(x, y)
|
|
661
|
+
|
|
662
|
+
# Correlation heatmap
|
|
663
|
+
if y.dtype == "O":
|
|
664
|
+
corr_heatmap_plot(prefix=prefix,
|
|
665
|
+
save_path=save_path,
|
|
666
|
+
save_fig=save_fig,
|
|
667
|
+
show_fig=show_fig).make_figure(x)
|
|
668
|
+
else:
|
|
669
|
+
corr_heatmap_plot(prefix=prefix,
|
|
670
|
+
save_path=save_path,
|
|
671
|
+
save_fig=save_fig,
|
|
672
|
+
show_fig=show_fig).make_figure(x, y)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def classification_summary(y_true,
|
|
676
|
+
y_pred_prob,
|
|
677
|
+
target_label=None,
|
|
678
|
+
prefix="",
|
|
679
|
+
save_path="./output/images/",
|
|
680
|
+
save_fig=True,
|
|
681
|
+
show_fig=True):
|
|
682
|
+
"""
|
|
683
|
+
Give a classification summary, including:
|
|
684
|
+
1. recall, precision, f1 and accuracy.
|
|
685
|
+
2. confusion matrix.
|
|
686
|
+
3. ROC curve.
|
|
687
|
+
|
|
688
|
+
Args:
|
|
689
|
+
y_true (pandas.Series or a 1D array): Bool, the label
|
|
690
|
+
y_pred_prob (pandas.Series or a 1D array): float in [0, 1]. prediction from model
|
|
691
|
+
Todo:
|
|
692
|
+
1. support to multi-class classification(on going)
|
|
693
|
+
2. the label matching between y_true and y_pred
|
|
694
|
+
3. support to regression
|
|
695
|
+
"""
|
|
696
|
+
y_pred = y_pred_prob.idxmax(axis=1)
|
|
697
|
+
print("\n", prefix)
|
|
698
|
+
print(metrics.classification_report(y_true, y_pred))
|
|
699
|
+
|
|
700
|
+
if not target_label is None:
|
|
701
|
+
# binary classification
|
|
702
|
+
confusion_scores = metrics.classification_report(
|
|
703
|
+
y_true == target_label, y_pred == target_label, output_dict=True)
|
|
704
|
+
sensitivity = confusion_scores["True"]["recall"]
|
|
705
|
+
specificity = confusion_scores["False"]["recall"]
|
|
706
|
+
|
|
707
|
+
print("sensitivity: {:.3f}".format(sensitivity))
|
|
708
|
+
print("specificity: {:.3f}".format(specificity))
|
|
709
|
+
|
|
710
|
+
# confusion matrix
|
|
711
|
+
confusion_matrix_plot(prefix=prefix,
|
|
712
|
+
save_path=save_path,
|
|
713
|
+
show_fig=show_fig,
|
|
714
|
+
save_fig=save_fig).make_figure(y_true, y_pred)
|
|
715
|
+
|
|
716
|
+
# roc cuve
|
|
717
|
+
roc_plot(pos_label=target_label,
|
|
718
|
+
prefix=prefix,
|
|
719
|
+
save_path=save_path,
|
|
720
|
+
show_fig=show_fig,
|
|
721
|
+
save_fig=save_fig).make_figure(y_true, y_pred_prob)
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def regression_summary(y_true: pd.Series,
|
|
725
|
+
y_pred: pd.Series,
|
|
726
|
+
x: pd.DataFrame = None,
|
|
727
|
+
prefix="",
|
|
728
|
+
save_path="./output/images/",
|
|
729
|
+
save_fig=True,
|
|
730
|
+
show_fig=True):
|
|
731
|
+
"""
|
|
732
|
+
1. compute rmse, r square and mape if y is all positive.
|
|
733
|
+
2. feature pca residual plot if x is not None.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
y_true (pd.Series): Ground true
|
|
737
|
+
y_pred (pd.Series): The estimates from model.
|
|
738
|
+
x (pd.DataFrame, optional): features. If not None, it will be used to plot a feature pca residual plot. Defaults to None.
|
|
739
|
+
prefix (str, optional): prefix of figure title. Defaults to "".
|
|
740
|
+
save_path (str, optional): The export path of the figure. Defaults to "./output/images/".
|
|
741
|
+
save_fig (bool, optional): To export the figure or not. Defaults to True.
|
|
742
|
+
show_fig (bool, optional): To show the figure before export or not. Defaults to True.
|
|
743
|
+
"""
|
|
744
|
+
residual = y_true - y_pred
|
|
745
|
+
|
|
746
|
+
print("\n", prefix, " performance:")
|
|
747
|
+
print(" r2 : {:.3f}".format(
|
|
748
|
+
metrics.root_mean_squared_error(y_true, y_pred)))
|
|
749
|
+
print(" rmse : {:.3f}".format(metrics.r2_score(y_true, y_pred)))
|
|
750
|
+
print(" mae : {:.3f}".format(
|
|
751
|
+
metrics.mean_absolute_error(y_true, y_pred)))
|
|
752
|
+
if (y_true > 0).all():
|
|
753
|
+
print(" mape : {:.3f}".format(
|
|
754
|
+
metrics.mean_absolute_percentage_error(y_true, y_pred)))
|
|
755
|
+
print(" support: {:.3f}".format(len(y_true)))
|
|
756
|
+
|
|
757
|
+
if not x is None:
|
|
758
|
+
pca_plot(n_pc=2,
|
|
759
|
+
discrete_legend=False,
|
|
760
|
+
prefix=prefix + " Residual",
|
|
761
|
+
save_path=save_path,
|
|
762
|
+
save_fig=save_fig,
|
|
763
|
+
show_fig=show_fig).make_figure(x, residual)
|