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,201 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from scipy.stats import t
|
|
5
|
+
from . import SelectionPipeline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Volcano_selection(SelectionPipeline):
|
|
9
|
+
"""
|
|
10
|
+
volcano plot.
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self,
|
|
15
|
+
k,
|
|
16
|
+
strategy="fold",
|
|
17
|
+
p_threshold=0.05,
|
|
18
|
+
fc_threshold=2,
|
|
19
|
+
log_domain=False,
|
|
20
|
+
absolute=True):
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
strategy (str, optional): Choosing strategy. One of {"p" or "fold"} Defaults to "fold".
|
|
25
|
+
p_threshold (float, optional): p-value threshold. Only feature has p-value higher than threshold will be considered. Defaults to 0.05.
|
|
26
|
+
fc_threshold (int, optional): fold change threshold. Only feature has fold change higher than threshold will be considered. Defaults to 2.
|
|
27
|
+
log_domain (bool, optional): Whether input data is in log_domain. Defaults to False.
|
|
28
|
+
absolute (bool, optional): If true, then take absolute value on score while strategy == "p". Defaults to True.
|
|
29
|
+
"""
|
|
30
|
+
super().__init__(k=k)
|
|
31
|
+
self.strategy = strategy
|
|
32
|
+
self.fc_threshold = fc_threshold
|
|
33
|
+
self.p_threshold = p_threshold
|
|
34
|
+
self.log_domain = log_domain
|
|
35
|
+
self.absolute = absolute
|
|
36
|
+
self.name = "Volcano Plot_" + self.strategy
|
|
37
|
+
self.missing_value = 0
|
|
38
|
+
|
|
39
|
+
def Scoring(self, x, y):
|
|
40
|
+
"""
|
|
41
|
+
Compute the fold change and p-value on each feature.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
x (pandas.DataFrame or a 2D array): The data to extract information.
|
|
45
|
+
y (pandas.Series or a 1D array): The target label for methods. Defaults to None.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
pandas.DataFrame: A dataframe records p-value and fold change.
|
|
49
|
+
"""
|
|
50
|
+
positive = y == 1
|
|
51
|
+
negative = y == 0
|
|
52
|
+
|
|
53
|
+
x = x.replace(0, np.nan)
|
|
54
|
+
|
|
55
|
+
# fold change
|
|
56
|
+
if not self.log_domain:
|
|
57
|
+
log_fold = np.log2(x[positive].mean(axis=0) /
|
|
58
|
+
x[negative].mean(axis=0))
|
|
59
|
+
else:
|
|
60
|
+
log_fold = x[positive].mean(axis=0) - x[negative].mean(axis=0)
|
|
61
|
+
|
|
62
|
+
# Welch t test:
|
|
63
|
+
# normal assumption
|
|
64
|
+
# diffirent sample size
|
|
65
|
+
# diffirent varience
|
|
66
|
+
# unpaired
|
|
67
|
+
n_positive = x[positive].shape[0]
|
|
68
|
+
n_negative = x[negative].shape[0]
|
|
69
|
+
diff = x[positive].mean(axis=0) - x[negative].mean(axis=0)
|
|
70
|
+
|
|
71
|
+
s_positive = ((x[positive] - x[positive].mean(axis=0))**
|
|
72
|
+
2).sum(axis=0) / (n_positive - 1)
|
|
73
|
+
s_negative = ((x[negative] - x[negative].mean(axis=0))**
|
|
74
|
+
2).sum(axis=0) / (n_negative - 1)
|
|
75
|
+
st = np.sqrt(s_positive / n_positive + s_negative / n_negative)
|
|
76
|
+
|
|
77
|
+
t_statistic = np.abs(diff / st)
|
|
78
|
+
df = (s_positive / n_positive + s_negative / n_negative)**2 / (
|
|
79
|
+
(s_positive / n_positive)**2 / (n_positive - 1) +
|
|
80
|
+
(s_negative / n_negative)**2 / (n_negative - 1))
|
|
81
|
+
|
|
82
|
+
# 2 side testing
|
|
83
|
+
#print("t statistic: ", t_statistic.min(), t_statistic.mean(), t_statistic.max())
|
|
84
|
+
#print("degree of freedom: ", df.min(), df.mean(), df.max())
|
|
85
|
+
|
|
86
|
+
p_value = t.cdf(x=-t_statistic, df=df) * 2
|
|
87
|
+
log_p = -np.log10(p_value)
|
|
88
|
+
|
|
89
|
+
self.scores = pd.DataFrame(
|
|
90
|
+
{
|
|
91
|
+
"log_p_value": log_p,
|
|
92
|
+
"log_fold_change": log_fold
|
|
93
|
+
},
|
|
94
|
+
index=log_fold.index)
|
|
95
|
+
return self.scores.copy()
|
|
96
|
+
|
|
97
|
+
def Choose(self, scores):
|
|
98
|
+
"""
|
|
99
|
+
Choosing the features which has score higher than threshold in assigned strategy.
|
|
100
|
+
|
|
101
|
+
If strategy == "fold": sort in fold change and return p-value
|
|
102
|
+
|
|
103
|
+
If strategy == "p": sort in p-value and return fold change
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
scores (pandas.DataFrame): A dataframe records p-value and fold change.
|
|
107
|
+
k (int): Number of features to select.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
pandas.Series: The score for k selected features in assigned strategy.
|
|
111
|
+
"""
|
|
112
|
+
log_fold = scores["log_fold_change"]
|
|
113
|
+
log_p = scores["log_p_value"]
|
|
114
|
+
# choose fold change > 2 and p value < 0.05 in log scale
|
|
115
|
+
significant = np.logical_and(
|
|
116
|
+
np.abs(log_fold) >= np.log2(self.fc_threshold), log_p
|
|
117
|
+
> -np.log10(self.p_threshold))
|
|
118
|
+
self.significant = significant
|
|
119
|
+
|
|
120
|
+
# choose top k logged p-value
|
|
121
|
+
if self.strategy == "fold":
|
|
122
|
+
selected = np.abs(log_fold).loc[significant].sort_values().tail(
|
|
123
|
+
self.k)
|
|
124
|
+
selected_score = pd.Series(log_p.loc[selected.index],
|
|
125
|
+
index=selected.index,
|
|
126
|
+
name=self.name)
|
|
127
|
+
elif self.strategy == "p":
|
|
128
|
+
selected = log_p.loc[significant].sort_values().tail(self.k)
|
|
129
|
+
if self.absolute:
|
|
130
|
+
selected_score = pd.Series(
|
|
131
|
+
np.abs(log_fold.loc[selected.index]),
|
|
132
|
+
index=selected.index,
|
|
133
|
+
name=self.name).sort_values(ascending=False)
|
|
134
|
+
else:
|
|
135
|
+
selected_score = pd.Series(
|
|
136
|
+
log_fold.loc[selected.index],
|
|
137
|
+
index=selected.index,
|
|
138
|
+
name=self.name).sort_values(ascending=False)
|
|
139
|
+
else:
|
|
140
|
+
raise "select_by must be one of {fold} or {p}"
|
|
141
|
+
|
|
142
|
+
# volcano plot importance
|
|
143
|
+
self.selected_score = selected_score
|
|
144
|
+
return self.selected_score.copy()
|
|
145
|
+
|
|
146
|
+
def plotting(self,
|
|
147
|
+
external=False,
|
|
148
|
+
external_score=None,
|
|
149
|
+
title="Welch t-test volcano",
|
|
150
|
+
show=True,
|
|
151
|
+
saving=False,
|
|
152
|
+
save_path="./output/"):
|
|
153
|
+
"""
|
|
154
|
+
Plotting
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
external (bool, optional): True to use external score. Defaults to False.
|
|
158
|
+
external_score (_type_, optional): External score to be used. Only activate when external == True. Defaults to None.
|
|
159
|
+
title (str, optional): plot title. Defaults to "Welch t-test volcano".
|
|
160
|
+
show (bool, optional): True to show the plot. Defaults to True.
|
|
161
|
+
saving (bool, optional): True to save the plot. Defaults to False.
|
|
162
|
+
save_path (str, optional): The path to save plot. Only activate when saving == True. Defaults to "./output/images/".
|
|
163
|
+
"""
|
|
164
|
+
log_fold = self.scores["log_fold_change"]
|
|
165
|
+
log_p = self.scores["log_p_value"]
|
|
166
|
+
# choose fold change > 2 and p value < 0.05 in log scale
|
|
167
|
+
significant = np.logical_and(
|
|
168
|
+
np.abs(log_fold) >= np.log2(self.fc_threshold), log_p
|
|
169
|
+
> -np.log10(self.p_threshold))
|
|
170
|
+
|
|
171
|
+
if external:
|
|
172
|
+
selected = pd.Series(False, index=self.scores.index)
|
|
173
|
+
selected.loc[external_score.index] = True
|
|
174
|
+
else:
|
|
175
|
+
selected = pd.Series(False, index=significant.index)
|
|
176
|
+
selected.loc[self.selected_score.index] = True
|
|
177
|
+
|
|
178
|
+
# silent
|
|
179
|
+
plt.scatter(x=log_fold[~significant],
|
|
180
|
+
y=log_p[~significant],
|
|
181
|
+
s=0.5,
|
|
182
|
+
color='gray')
|
|
183
|
+
# not selected
|
|
184
|
+
plt.scatter(x=log_fold[significant][~selected],
|
|
185
|
+
y=log_p[significant][~selected],
|
|
186
|
+
s=2)
|
|
187
|
+
# selected
|
|
188
|
+
if external:
|
|
189
|
+
plt.scatter(x=log_fold[selected], y=log_p[selected], s=2)
|
|
190
|
+
else:
|
|
191
|
+
plt.scatter(x=log_fold[selected], y=log_p[selected], s=2)
|
|
192
|
+
|
|
193
|
+
plt.title(title)
|
|
194
|
+
plt.xlabel("log_2 fold")
|
|
195
|
+
plt.axvline(1, linestyle="dotted", color="gray")
|
|
196
|
+
plt.axvline(-1, linestyle="dotted", color="gray")
|
|
197
|
+
plt.axhline(-np.log10(0.05), linestyle="dotted", color="gray")
|
|
198
|
+
plt.ylabel("log_10 p")
|
|
199
|
+
if saving:
|
|
200
|
+
plt.savefig(save_path + title, format="png")
|
|
201
|
+
plt.show()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from sklearn.exceptions import ConvergenceWarning
|
|
5
|
+
|
|
6
|
+
# Suppress only ConvergenceWarning
|
|
7
|
+
warnings.filterwarnings("ignore", category=ConvergenceWarning)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SelectionPipeline:
|
|
11
|
+
"""
|
|
12
|
+
The basic pipeline for selection methods. It includes 2 parts: Scoring and Choosing.
|
|
13
|
+
The detail methods is to be determinded.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, k=None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize the selection pipeline.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
k (int or None): select top k important feature. k = -1 means selecting all, k = None means selecting the feature that have standarized score > 1. Default = None
|
|
23
|
+
"""
|
|
24
|
+
self.k = k
|
|
25
|
+
self.name = "base"
|
|
26
|
+
self.scores = None
|
|
27
|
+
self.selected_score = None
|
|
28
|
+
|
|
29
|
+
def Scoring(self, x, y=None):
|
|
30
|
+
"""
|
|
31
|
+
The method to scores features is to be implemented.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
x (pandas.DataFrame or a 2D array): The data to extract information.
|
|
35
|
+
y (pandas.Series or a 1D array): The target label for methods. Defaults to None.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
pandas.Series or pandas.DataFrame: The score for each feature. Some elements may be empty.
|
|
39
|
+
"""
|
|
40
|
+
self.scores = x.max().sort_values(ascending=False) # top is better
|
|
41
|
+
return self.scores.copy()
|
|
42
|
+
|
|
43
|
+
def Choose(self, scores):
|
|
44
|
+
"""
|
|
45
|
+
Choosing features according to scores.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
scores (pandas.Series or pandas.DataFrame): The score for each feature. Some elements may be empty.
|
|
49
|
+
k (int): Number of feature to select. The result may less than k
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
pandas.Series or pandas.DataFrame: The score for k selected features. May less than k.
|
|
53
|
+
"""
|
|
54
|
+
self.selected_score = scores.head(self.k)
|
|
55
|
+
self.selected_score = self.selected_score[self.selected_score != 0]
|
|
56
|
+
|
|
57
|
+
return self.selected_score.copy()
|
|
58
|
+
|
|
59
|
+
def Select(self, x, y):
|
|
60
|
+
"""
|
|
61
|
+
A functional stack of: Scoring -> Choosing
|
|
62
|
+
if k == None, choose k such that:
|
|
63
|
+
z-scores = (scores - scores.mean())/scores.std()
|
|
64
|
+
k = # (z-scores > 1)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
x (pandas.DataFrame or a 2D array): The data to extract information.
|
|
69
|
+
y (pandas.Series or a 1D array): The target label for methods.
|
|
70
|
+
k (int): Number of feature to select. The result may less than k.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
pandas.Series: The score for k selected features. May less than k.
|
|
74
|
+
"""
|
|
75
|
+
# x should be a pd dataframe or a numpy array without missing value
|
|
76
|
+
scores = self.Scoring(x, y)
|
|
77
|
+
if self.k:
|
|
78
|
+
# k not None
|
|
79
|
+
selected_score = self.Choose(scores)
|
|
80
|
+
else:
|
|
81
|
+
# k is None
|
|
82
|
+
z_scores = (scores - scores.mean()) / (scores.std() + 1e-4)
|
|
83
|
+
selected_score = scores[z_scores > 1.]
|
|
84
|
+
|
|
85
|
+
return selected_score
|
|
86
|
+
|
|
87
|
+
def fit(self, x, y):
|
|
88
|
+
"""
|
|
89
|
+
sklearn api
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
x (pandas.DataFrame or a 2D array): The data to extract information.
|
|
93
|
+
y (pandas.Series or a 1D array): The target label for methods.
|
|
94
|
+
"""
|
|
95
|
+
self.Select(x, y)
|
|
96
|
+
return self
|
|
97
|
+
|
|
98
|
+
def transform(self, x):
|
|
99
|
+
return x[self.selected_score.index]
|
|
100
|
+
|
|
101
|
+
def fit_transform(self, x, y):
|
|
102
|
+
self.fit(x, y)
|
|
103
|
+
return self.transform(x)
|
|
104
|
+
|
|
105
|
+
def what_matters(self):
|
|
106
|
+
return self.selected_score
|
|
107
|
+
|
|
108
|
+
def Plotting(self):
|
|
109
|
+
"""
|
|
110
|
+
plot hist graph of selectied feature importance
|
|
111
|
+
"""
|
|
112
|
+
fig, ax = plt.subplots(1, 1)
|
|
113
|
+
ax.bar(self.selected_score.index, self.selected_score)
|
|
114
|
+
for label in ax.get_xticklabels(which='major'):
|
|
115
|
+
label.set(rotation=45, horizontalalignment='right')
|
|
116
|
+
ax.set_title(self.name + " score")
|
|
117
|
+
|
|
118
|
+
plt.show()
|
|
119
|
+
|
|
120
|
+
def reference(self) -> dict[str, str]:
|
|
121
|
+
"""
|
|
122
|
+
This function will return reference of this method in python dict.
|
|
123
|
+
If you want to access it in PineBioML api document, then click on the >Expand source code
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
dict[str, str]: a dict of reference.
|
|
127
|
+
"""
|
|
128
|
+
refers = {
|
|
129
|
+
"sklearn publication":
|
|
130
|
+
"https://dl.acm.org/doi/10.5555/1953048.2078195"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return refers
|
|
134
|
+
|
|
135
|
+
def Diagnose(self):
|
|
136
|
+
"""
|
|
137
|
+
Give diagnose of selection.
|
|
138
|
+
"""
|
|
139
|
+
#pass
|
|
140
|
+
|
|
141
|
+
def Report(self):
|
|
142
|
+
"""
|
|
143
|
+
Give diagnose of selection.
|
|
144
|
+
"""
|
|
145
|
+
#pass
|