cratonml 0.1.1.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.
- cratonml-0.1.1.0/PKG-INFO +45 -0
- cratonml-0.1.1.0/README.md +23 -0
- cratonml-0.1.1.0/cratonml/__init__.py +2 -0
- cratonml-0.1.1.0/cratonml/calculate/Classification.py +221 -0
- cratonml-0.1.1.0/cratonml/calculate/Clustering.py +252 -0
- cratonml-0.1.1.0/cratonml/calculate/DimensionReduction.py +52 -0
- cratonml-0.1.1.0/cratonml/calculate/PostProcessing.py +68 -0
- cratonml-0.1.1.0/cratonml/calculate/Prepare.py +312 -0
- cratonml-0.1.1.0/cratonml/calculate/Statistics.py +140 -0
- cratonml-0.1.1.0/cratonml/calculate/__init__.py +0 -0
- cratonml-0.1.1.0/cratonml/calculate/curve_utils.py +31 -0
- cratonml-0.1.1.0/cratonml/calculate/metrics.py +6 -0
- cratonml-0.1.1.0/cratonml/data/Cube.py +256 -0
- cratonml-0.1.1.0/cratonml/data/Grid.py +184 -0
- cratonml-0.1.1.0/cratonml/data/Well.py +267 -0
- cratonml-0.1.1.0/cratonml/data/__init__.py +3 -0
- cratonml-0.1.1.0/pyproject.toml +29 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cratonml
|
|
3
|
+
Version: 0.1.1.0
|
|
4
|
+
Summary: Python application for W-Seis
|
|
5
|
+
Author: KamashevAM
|
|
6
|
+
Author-email: kamashev41@gmail.com
|
|
7
|
+
Requires-Python: >=3.11,<3.13
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: cratonapi (>=0.1.0.9,<0.2.0.0)
|
|
12
|
+
Requires-Dist: imbalanced-learn (>=0.12.4,<0.13.0)
|
|
13
|
+
Requires-Dist: nose2 (>=0.15.1,<0.16.0)
|
|
14
|
+
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
|
15
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
16
|
+
Requires-Dist: pywin32 (>=306,<307)
|
|
17
|
+
Requires-Dist: pywin32-ctypes (>=0.2.2,<0.3.0)
|
|
18
|
+
Requires-Dist: scikit-learn (>=1.5.0,<2.0.0)
|
|
19
|
+
Requires-Dist: scipy (>=1.14.0,<2.0.0)
|
|
20
|
+
Requires-Dist: seaborn (>=0.13.2,<0.14.0)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# MLToolBox
|
|
24
|
+
|
|
25
|
+
# Использование:
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
+ Установка зависимостей
|
|
29
|
+
```
|
|
30
|
+
$ pip install mltoolbox
|
|
31
|
+
```
|
|
32
|
+
+ Запустите Desmana, GisWell и подгрузите сейсмический проект
|
|
33
|
+
```
|
|
34
|
+
# Логика версионирования CHANGELOG:
|
|
35
|
+
|
|
36
|
+
M.N.K.L
|
|
37
|
+
|
|
38
|
+
M - за глобальный релиз отвечает (выход в прод, закрытие этапа)
|
|
39
|
+
N - при добавлении нового модуля, либо изменения старых, но без поддержки расчета прошлых версий
|
|
40
|
+
K - при добавлении новых функциональностей в существующих модулях (функции, класс и тд)
|
|
41
|
+
L - при изменении текущего функционала
|
|
42
|
+
|
|
43
|
+
При изменении номера, все что правее по индексу меняется на ноль.
|
|
44
|
+
Подразумевается что тесты не входят в пулл changelog, но если отдельно заливаются то менять L
|
|
45
|
+
Новые записи вносить вверх файла CHANGELOG
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# MLToolBox
|
|
2
|
+
|
|
3
|
+
# Использование:
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
+ Установка зависимостей
|
|
7
|
+
```
|
|
8
|
+
$ pip install mltoolbox
|
|
9
|
+
```
|
|
10
|
+
+ Запустите Desmana, GisWell и подгрузите сейсмический проект
|
|
11
|
+
```
|
|
12
|
+
# Логика версионирования CHANGELOG:
|
|
13
|
+
|
|
14
|
+
M.N.K.L
|
|
15
|
+
|
|
16
|
+
M - за глобальный релиз отвечает (выход в прод, закрытие этапа)
|
|
17
|
+
N - при добавлении нового модуля, либо изменения старых, но без поддержки расчета прошлых версий
|
|
18
|
+
K - при добавлении новых функциональностей в существующих модулях (функции, класс и тд)
|
|
19
|
+
L - при изменении текущего функционала
|
|
20
|
+
|
|
21
|
+
При изменении номера, все что правее по индексу меняется на ноль.
|
|
22
|
+
Подразумевается что тесты не входят в пулл changelog, но если отдельно заливаются то менять L
|
|
23
|
+
Новые записи вносить вверх файла CHANGELOG
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sklearn.ensemble import RandomForestClassifier as RandomForest
|
|
3
|
+
from sklearn.naive_bayes import GaussianNB
|
|
4
|
+
from sklearn.tree import DecisionTreeClassifier as DecisionTree
|
|
5
|
+
from sklearn.linear_model import LogisticRegression
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GaussianNaiveBayesClassifier:
|
|
9
|
+
"""Класс для классификации алгоритмом Gaussian Naive Bayes."""
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def get_model(x_train: np.ndarray, y_train: np.ndarray):
|
|
13
|
+
"""
|
|
14
|
+
Возвращает модель обученную на тренировочных данных.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
x_train: np.ndarray
|
|
19
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
20
|
+
y_train: np.ndarray
|
|
21
|
+
Массив значений размера N.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
GaussianNB
|
|
26
|
+
Обученная модель.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
assert x_train.ndim == 2, "x_train: Ожидался 2D массив"
|
|
30
|
+
assert y_train.ndim == 1, "y_train: Ожидался 1D массив"
|
|
31
|
+
assert x_train.shape[0] == y_train.shape[0], "Массив y_train имеет несоответствующую длину"
|
|
32
|
+
|
|
33
|
+
model = GaussianNB()
|
|
34
|
+
model.fit(x_train, y_train)
|
|
35
|
+
return model
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def predict(model: GaussianNB, x_test: np.ndarray):
|
|
39
|
+
"""
|
|
40
|
+
Прогнозирует метки для тестового набора данных.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
model: GaussianNB
|
|
45
|
+
Обученная модель.
|
|
46
|
+
x_test: np.ndarray
|
|
47
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
np.ndarray
|
|
52
|
+
Массив меток размера N.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
assert x_test.ndim == 2, "x_test: Ожидался 2D массив"
|
|
56
|
+
|
|
57
|
+
prediction = model.predict(x_test)
|
|
58
|
+
return prediction
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class LogisticRegressionClassifier:
|
|
62
|
+
"""Класс для классификации алгоритмом Logistic Regression."""
|
|
63
|
+
@staticmethod
|
|
64
|
+
def get_model(x_train: np.ndarray, y_train: np.ndarray):
|
|
65
|
+
"""
|
|
66
|
+
Возвращает модель обученную на тренировочных данных.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
x_train: np.ndarray
|
|
71
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
72
|
+
y_train: np.ndarray
|
|
73
|
+
Массив значений размера N.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
LogisticRegression
|
|
78
|
+
Обученная модель.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
assert x_train.ndim == 2, "x_train: Ожидался 2D массив"
|
|
82
|
+
assert y_train.ndim == 1, "y_train: Ожидался 1D массив"
|
|
83
|
+
assert x_train.shape[0] == y_train.shape[0], "Массив y_train имеет несоответствующую длину"
|
|
84
|
+
|
|
85
|
+
model = LogisticRegression()
|
|
86
|
+
model.fit(x_train, y_train)
|
|
87
|
+
return model
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def predict(model: LogisticRegression, x_test: np.ndarray):
|
|
91
|
+
"""
|
|
92
|
+
Прогнозирует метки для тестового набора данных.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
model: LogisticRegression
|
|
97
|
+
Обученная модель.
|
|
98
|
+
x_test: np.ndarray
|
|
99
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
np.ndarray
|
|
104
|
+
Массив меток размера N.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
assert x_test.ndim == 2, "x_test: Ожидался 2D массив"
|
|
108
|
+
|
|
109
|
+
prediction = model.predict(x_test)
|
|
110
|
+
return prediction
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class DecisionTreeClassifier:
|
|
114
|
+
"""Класс для классификации Decision Tree."""
|
|
115
|
+
@staticmethod
|
|
116
|
+
def get_model(x_train: np.ndarray, y_train: np.ndarray, min_samples_split: int or float = 2, max_depth: int = None):
|
|
117
|
+
"""
|
|
118
|
+
Возвращает модель обученную на тренировочных данных.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
x_train: np.ndarray
|
|
123
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
124
|
+
y_train: np.ndarray
|
|
125
|
+
Массив значений размера N.
|
|
126
|
+
min_samples_split: int or float = 2
|
|
127
|
+
Минимальное количество выборок, необходимое для разделения внутреннего узла.(параметр DecisionTree).
|
|
128
|
+
max_depth: int = None
|
|
129
|
+
Максимальная глубина дерева(параметр DecisionTree).
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
DecisionTree
|
|
134
|
+
Обученная модель.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
assert x_train.ndim == 2, "x_train: Ожидался 2D массив"
|
|
138
|
+
assert y_train.ndim == 1, "y_train: Ожидался 1D массив"
|
|
139
|
+
assert x_train.shape[0] == y_train.shape[0], "Массив y_train имеет несоответствующую длину"
|
|
140
|
+
|
|
141
|
+
model = DecisionTree(min_samples_split=min_samples_split, max_depth=max_depth)
|
|
142
|
+
model.fit(x_train, y_train)
|
|
143
|
+
return model
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def predict(model: DecisionTree, x_test: np.ndarray):
|
|
147
|
+
"""
|
|
148
|
+
Прогнозирует метки для тестового набора данных.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
model: DecisionTree
|
|
153
|
+
Обученная модель.
|
|
154
|
+
x_test: np.ndarray
|
|
155
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
np.ndarray
|
|
160
|
+
Массив меток размера N.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
assert x_test.ndim == 2, "x_test: Ожидался 2D массив"
|
|
164
|
+
|
|
165
|
+
prediction = model.predict(x_test)
|
|
166
|
+
return prediction
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class RandomForestClassifier:
|
|
170
|
+
"""Класс для классификации Random Forest."""
|
|
171
|
+
@staticmethod
|
|
172
|
+
def get_model(x_train: np.ndarray, y_train: np.ndarray, n_estimators: int = 100, max_depth: int = None):
|
|
173
|
+
"""
|
|
174
|
+
Возвращает модель обученную на тренировочных данных.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
x_train: np.ndarray
|
|
179
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
180
|
+
y_train: np.ndarray
|
|
181
|
+
Массив значений размера N.
|
|
182
|
+
n_estimators: int = 100
|
|
183
|
+
Количество деревьев в лесу.(параметр RandomForest).
|
|
184
|
+
max_depth: int = None
|
|
185
|
+
Максимальная глубина дерева(параметр RandomForest).
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
RandomForest
|
|
189
|
+
Обученная модель.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
assert x_train.ndim == 2, "x_train: Ожидался 2D массив"
|
|
193
|
+
assert y_train.ndim == 1, "y_train: Ожидался 1D массив"
|
|
194
|
+
assert x_train.shape[0] == y_train.shape[0], "Массив y_train имеет несоответствующую длину"
|
|
195
|
+
|
|
196
|
+
model = RandomForest(n_estimators=n_estimators, max_depth=max_depth)
|
|
197
|
+
model.fit(x_train,y_train)
|
|
198
|
+
return model
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def predict(model: RandomForest, x_test: np.ndarray):
|
|
202
|
+
"""
|
|
203
|
+
Прогнозирует метки для тестового набора данных.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
model: RandomForest
|
|
208
|
+
Обученная модель.
|
|
209
|
+
x_test: np.ndarray
|
|
210
|
+
Массив значений размера (N, M). Где N - количество точек, M - количество атрибутов.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
np.ndarray
|
|
215
|
+
Массив меток размера N.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
assert x_test.ndim == 2, "x_test: Ожидался 2D массив"
|
|
219
|
+
|
|
220
|
+
prediction = model.predict(x_test)
|
|
221
|
+
return prediction
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
from sklearn.cluster import KMeans, HDBSCAN
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy import stats
|
|
4
|
+
from sklearn.mixture import GaussianMixture
|
|
5
|
+
|
|
6
|
+
RANDOM_STATE = 42
|
|
7
|
+
ALPHA_K = 0.02
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class KMeansClassifier:
|
|
11
|
+
"""Класс для KMeans кластеризации."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def __get_scaled_inertia(data, cluster):
|
|
15
|
+
inertia_o = np.square((data - data.mean(axis=0))).sum()
|
|
16
|
+
scaled_inertia = cluster.inertia_ / inertia_o + ALPHA_K * cluster.n_clusters
|
|
17
|
+
return scaled_inertia
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def __get_cluster(data, n_clusters):
|
|
21
|
+
cluster = KMeans(n_clusters=n_clusters,
|
|
22
|
+
random_state=RANDOM_STATE, algorithm='elkan')
|
|
23
|
+
cluster.fit(data)
|
|
24
|
+
return cluster
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def __get_cluster_centers(kmeans):
|
|
28
|
+
kmeans_centers = np.unique(np.sum(kmeans.cluster_centers_, axis=1))
|
|
29
|
+
return kmeans_centers
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def __get_labels(data, n_clusters):
|
|
33
|
+
kmeans = KMeansClassifier.__get_cluster(data, n_clusters)
|
|
34
|
+
kmeans_labels_before_sorting = kmeans.labels_
|
|
35
|
+
kmeans_centers = KMeansClassifier.__get_cluster_centers(kmeans)
|
|
36
|
+
kmeans_uniq_labels = np.unique(kmeans_labels_before_sorting)
|
|
37
|
+
sorted_kmeans_uniq_labels = kmeans_uniq_labels[np.argsort(kmeans_centers)]
|
|
38
|
+
|
|
39
|
+
kmeans_labels = np.zeros_like(kmeans_labels_before_sorting)
|
|
40
|
+
for i, label in enumerate(sorted_kmeans_uniq_labels):
|
|
41
|
+
kmeans_labels[kmeans_labels_before_sorting == label] = i
|
|
42
|
+
|
|
43
|
+
return kmeans_labels
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def find_the_best_number_of_clusters(data: np.ndarray) -> int:
|
|
47
|
+
"""
|
|
48
|
+
Подбирает оптимальное количество кластеров на основании взвешенной инерции.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
data: np.ndarray
|
|
53
|
+
2D массив значений.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
int
|
|
58
|
+
Оптимальное количество кластеров.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
62
|
+
|
|
63
|
+
scaled_inertia_list = []
|
|
64
|
+
number_of_clusters_list = range(2, min(15, data.shape[0]))
|
|
65
|
+
for number_of_clusters in number_of_clusters_list:
|
|
66
|
+
cluster = KMeansClassifier.__get_cluster(data, number_of_clusters)
|
|
67
|
+
scaled_inertia = KMeansClassifier.__get_scaled_inertia(data, cluster)
|
|
68
|
+
scaled_inertia_list.append(scaled_inertia)
|
|
69
|
+
scaled_inertia_list = np.asarray(scaled_inertia_list)
|
|
70
|
+
best_number_of_clusters = number_of_clusters_list[np.argmin(scaled_inertia_list)]
|
|
71
|
+
return best_number_of_clusters
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def calculate(data: np.ndarray, n_clusters: int) -> np.ndarray:
|
|
75
|
+
"""
|
|
76
|
+
Распределяет объекты по n_clusters кластерам по сходству. Для вычисления этого сходства используется евклидово расстояние в качестве меры.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
data: np.ndarray
|
|
81
|
+
Массив значений (N, M). Где N-количество точек, M - количество атрибутов.
|
|
82
|
+
n_clusters: int
|
|
83
|
+
Количество кластеров.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
np.ndarray
|
|
88
|
+
Массив меток размера N.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
92
|
+
assert n_clusters > 0, "Параметр n_clusters должен принимать положительное значение"
|
|
93
|
+
|
|
94
|
+
labels = KMeansClassifier.__get_labels(data=data, n_clusters=n_clusters)
|
|
95
|
+
return labels
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class GaussianMixtureClassifier:
|
|
99
|
+
"""Класс для Gaussian Mixture кластеризации."""
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def __get_cluster(data, number_of_clusters):
|
|
103
|
+
cluster = GaussianMixture(n_components=number_of_clusters,
|
|
104
|
+
random_state=RANDOM_STATE)
|
|
105
|
+
cluster.fit(data)
|
|
106
|
+
return cluster
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def __get_cluster_centers(gm, data):
|
|
110
|
+
centers = np.empty(shape=(gm.n_components, data.shape[1]))
|
|
111
|
+
for i in range(gm.n_components):
|
|
112
|
+
density = stats.multivariate_normal(cov=gm.covariances_[i], mean=gm.means_[i]).logpdf(data)
|
|
113
|
+
centers[i, :] = data[np.argmax(density)]
|
|
114
|
+
centers = np.sum(centers, axis=1)
|
|
115
|
+
return centers
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def __get_labels(data, n_clusters):
|
|
119
|
+
gm = GaussianMixtureClassifier.__get_cluster(data, n_clusters)
|
|
120
|
+
gm_labels_before_sorting = gm.predict(data)
|
|
121
|
+
gm_centers = GaussianMixtureClassifier.__get_cluster_centers(gm, data)
|
|
122
|
+
gm_uniq_labels = np.unique(gm_labels_before_sorting)
|
|
123
|
+
sorted_gm_uniq_labels = gm_uniq_labels[np.argsort(gm_centers)]
|
|
124
|
+
|
|
125
|
+
gm_labels = np.zeros_like(gm_labels_before_sorting)
|
|
126
|
+
for i, label in enumerate(sorted_gm_uniq_labels):
|
|
127
|
+
gm_labels[gm_labels_before_sorting == label] = i
|
|
128
|
+
|
|
129
|
+
return gm_labels
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def find_the_best_number_of_clusters(data: np.ndarray) -> int:
|
|
133
|
+
"""
|
|
134
|
+
Подбирает оптимальное количество кластеров на основе Байесовского критерия.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
data: np.ndarray
|
|
139
|
+
2D массив значений.
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
int
|
|
144
|
+
Оптимальное количество кластеров.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
148
|
+
|
|
149
|
+
bic_list = []
|
|
150
|
+
number_of_clusters_list = range(2, min(15, data.shape[0] + 1))
|
|
151
|
+
for number_of_clusters in number_of_clusters_list:
|
|
152
|
+
gm = GaussianMixtureClassifier.__get_cluster(data, number_of_clusters)
|
|
153
|
+
bic = -gm.bic(data)
|
|
154
|
+
if bic < 0:
|
|
155
|
+
bic = np.nan
|
|
156
|
+
bic_list.append(bic)
|
|
157
|
+
bic_list = np.asarray(bic_list)
|
|
158
|
+
# bic_list_log = np.log(bic_list)
|
|
159
|
+
# best_number_of_clusters = number_of_clusters_list[np.nanargmin(bic_list_log)]
|
|
160
|
+
best_number_of_clusters = number_of_clusters_list[np.nanargmin(bic_list)]
|
|
161
|
+
return best_number_of_clusters
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def calculate(data: np.ndarray, n_clusters: int) -> np.ndarray:
|
|
165
|
+
"""
|
|
166
|
+
Распределяет объекты по n_clusters кластерам, предполагая, что данные состоят из смеси гауссовых распределений.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
data: np.ndarray
|
|
171
|
+
Массив значений (N, M). Где N - количество точек, M - количество атрибутов.
|
|
172
|
+
n_clusters: int
|
|
173
|
+
Количество кластеров.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
np.ndarray
|
|
178
|
+
Массив меток размера N.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
182
|
+
assert n_clusters > 0, "Параметр n_clusters должен принимать положительное значение"
|
|
183
|
+
|
|
184
|
+
labels = GaussianMixtureClassifier.__get_labels(data=data, n_clusters=n_clusters)
|
|
185
|
+
return labels
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class HDBSCANClassifier:
|
|
189
|
+
"""Класс для HDBSCAN кластеризации."""
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def __get_cluster(data, min_cluster_size, min_samples, cluster_selection_epsilon):
|
|
193
|
+
cluster = HDBSCAN(min_cluster_size=min_cluster_size, min_samples=min_samples,
|
|
194
|
+
cluster_selection_epsilon=cluster_selection_epsilon)
|
|
195
|
+
cluster.fit(data)
|
|
196
|
+
return cluster
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def __get_labels(data, min_cluster_size, min_samples, cluster_selection_epsilon):
|
|
200
|
+
hdbscan = HDBSCANClassifier.__get_cluster(data, min_cluster_size, min_samples, cluster_selection_epsilon)
|
|
201
|
+
hdbscan_labels = hdbscan.labels_
|
|
202
|
+
return hdbscan_labels
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def get_number_of_clusters(labels: np.ndarray) -> int:
|
|
206
|
+
"""
|
|
207
|
+
Возвращает количество кластеров.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
labels: np.ndarray
|
|
212
|
+
1D массив меток.
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
int
|
|
217
|
+
Количество кластеров.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
assert labels.ndim == 1, "labels: Ожидался 1D массив"
|
|
221
|
+
|
|
222
|
+
return np.unique(labels).shape[0]
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def calculate(data: np.ndarray,
|
|
226
|
+
min_cluster_size: int = 5,
|
|
227
|
+
min_samples: int = None,
|
|
228
|
+
cluster_selection_epsilon: float = 0.0) -> np.ndarray:
|
|
229
|
+
"""
|
|
230
|
+
Выявляет кластеры в наборе данных на основе распределения плотности точек данных.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
data: np.ndarray
|
|
235
|
+
Массив значений (N, M). Где N-количество точек, M - количество атрибутов.
|
|
236
|
+
min_cluster_size: int, default=5
|
|
237
|
+
Минимальное количество выборок, чтобы эта группа считалась кластером(параметр HDBSCAN).
|
|
238
|
+
min_samples: int, default=None
|
|
239
|
+
Минимальное количество выборок в окрестности, чтобы точка считалась центральной(параметр HDBSCAN).
|
|
240
|
+
cluster_selection_epsilon: float, default=0.0
|
|
241
|
+
Максимальное расстояние, допустимое между точками, чтобы они считались связанными в процессе кластеризации на основе плотности(параметр HDBSCAN).
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
np.ndarray
|
|
246
|
+
Массив меток размера N.
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
250
|
+
|
|
251
|
+
labels = HDBSCANClassifier.__get_labels(data, min_cluster_size, min_samples, cluster_selection_epsilon)
|
|
252
|
+
return labels
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sklearn.decomposition import PCA
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
RANDOM_STATE = 42
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_pca_statistics(data: np.ndarray) -> np.ndarray:
|
|
9
|
+
"""
|
|
10
|
+
Считает долю дисперсии для каждого компонента.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
data: np.ndarray
|
|
15
|
+
Массив значений, размера (N, M).
|
|
16
|
+
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
np.ndarray
|
|
20
|
+
Массив доль дисперсии, размера N.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
24
|
+
|
|
25
|
+
pca = PCA(n_components=data.shape[1],
|
|
26
|
+
random_state=RANDOM_STATE)
|
|
27
|
+
pca.fit(data)
|
|
28
|
+
return pca.explained_variance_ratio_
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def pca_transform(data: np.ndarray, n_components: int) -> np.ndarray:
|
|
32
|
+
"""
|
|
33
|
+
Понижение размерности с помощью PCA путём проецирования данных на главные (собственные) вектора.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
data: np.ndarray
|
|
38
|
+
Массив значений, размера (N, M).
|
|
39
|
+
n_components: int
|
|
40
|
+
Количество сохраняемых компонентов.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
np.ndarray
|
|
45
|
+
Преобразованный массив, размера (N, n_components).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
assert data.ndim == 2, "data: Ожидался 2D массив"
|
|
49
|
+
assert n_components > 0, "Параметр n_components должен принимать положительное значение"
|
|
50
|
+
|
|
51
|
+
return PCA(n_components=n_components,
|
|
52
|
+
random_state=RANDOM_STATE).fit_transform(data)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from .curve_utils import get_peaks, remove_single_width_peaks, remove_given_width_peaks
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def fill_with_value_by_mask(data: np.ndarray, mask: np.ndarray, blank_code: float) -> np.ndarray:
|
|
6
|
+
"""
|
|
7
|
+
Востанавливает массив, заполняя кодом бланковки по маске.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
data: np.ndarray
|
|
12
|
+
Массив значений, размером N.
|
|
13
|
+
mask: np.ndarray
|
|
14
|
+
Маска(True - в строке был nan, False - в строке не было nan), размером M >= N.
|
|
15
|
+
blank_code: float
|
|
16
|
+
Код бланковки.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
np.ndarray
|
|
21
|
+
Востановленный массив, размером M.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
assert data.ndim == 1, "data: Ожидался 1D массив"
|
|
25
|
+
assert mask.ndim == 1, "mask: Ожидался 1D массив"
|
|
26
|
+
assert mask.shape[0] >= data.shape[0], "Длина массива mask должна быть не меньше длины data"
|
|
27
|
+
|
|
28
|
+
data_with_blank_code = np.zeros(mask.shape[0])
|
|
29
|
+
data_with_blank_code[mask] = blank_code
|
|
30
|
+
data_with_blank_code[~mask] = data
|
|
31
|
+
return data_with_blank_code
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def curve_peaks_processing(curve: np.ndarray, depth: np.ndarray, minimal_width_in_meter: float) -> np.ndarray:
|
|
35
|
+
"""
|
|
36
|
+
Обработка пиков кривой. Удаление пиков, которые меньше заданной ширины (minimal_width_in_meter).
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
curve: np.ndarray
|
|
41
|
+
Массив значений кривой, размером N.
|
|
42
|
+
depth: np.ndarray
|
|
43
|
+
Массив глубин кривой, размером N.
|
|
44
|
+
minimal_width_in_meter: float
|
|
45
|
+
Минимальная ширина пиков в метрах.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
np.ndarray
|
|
50
|
+
Обработанная кривая, размером N.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
assert curve.ndim == 1, "curve: Ожидался 1D массив"
|
|
54
|
+
assert depth.ndim == 1, "depth: Ожидался 1D массив"
|
|
55
|
+
assert minimal_width_in_meter > 0, "Параметр minimal_width_in_meter должен принимать положительное значение"
|
|
56
|
+
|
|
57
|
+
dz = np.diff(depth)[0]
|
|
58
|
+
minimal_width_in_sample = int(minimal_width_in_meter / dz)
|
|
59
|
+
|
|
60
|
+
single_peaks = get_peaks(curve, 1)
|
|
61
|
+
single_filled_curve = remove_single_width_peaks(curve, single_peaks)
|
|
62
|
+
|
|
63
|
+
given_width_peaks = get_peaks(single_filled_curve, minimal_width_in_sample)
|
|
64
|
+
filled_curve = remove_given_width_peaks(single_filled_curve,
|
|
65
|
+
given_width_peaks,
|
|
66
|
+
minimal_width_in_sample)
|
|
67
|
+
filled_curve = abs(filled_curve - 1)
|
|
68
|
+
return filled_curve
|