Skip to content

Commit

Permalink
Merge pull request #73 from afraniomelo/main
Browse files Browse the repository at this point in the history
version 1.2.0
  • Loading branch information
afraniomelo authored Jan 13, 2025
2 parents 42117e9 + 776c10c commit d3953d6
Show file tree
Hide file tree
Showing 5 changed files with 631 additions and 110 deletions.
116 changes: 109 additions & 7 deletions bibmon/_generic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import seaborn as sns
import sklearn.model_selection
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

from abc import ABC, abstractmethod

Expand Down Expand Up @@ -53,6 +54,11 @@ class GenericModel (ABC):
Square Prediction Error. One-dimensional vectors containing
the SPE values calculated at each time step in
training and testing.
SPE_contrib_train, SPE_contrib_test: pd.DataFrame
Contributions to SPE. Bidimensional matrices containing
the SPE contributions of each variable at each time step in
training and testing. Valid for reconstruction models.
* Training parameters:
----------
Expand Down Expand Up @@ -117,11 +123,13 @@ class GenericModel (ABC):
map_from_X (@abstractmethod)
set_hyperparameters
load_model
compute_SPE_contributions
pre_train
train
pre_test
test
plot_SPE
plot_SPE_contributions
fit
predict
"""
Expand Down Expand Up @@ -210,6 +218,30 @@ def load_model (self, limSPE, SPE_mean, count_window_size,

###########################################################################

def compute_SPE_contributions (self, pred, X):
"""
Calculation of SPE contributions for diagnosis based on partial
decomposition analysis. Valid for reconstruction models,
in which self.has_Y = False.
Parâmetros:
----------
pred, X: numpy.array
Data windows to compute contributions.
Retornos:
----------
SPE_contributions: numpy.array
Contributions to the SPE.
"""

X = np.array(X)
X_pred = np.array(pred)

return np.absolute(X*(X-X_pred))

###########################################################################

def pre_train (self, X_train, Y_train = None,
f_pp = ['remove_empty_variables',
'ffill_nan',
Expand Down Expand Up @@ -352,6 +384,12 @@ def train (self, lim_conf = 0.99, delete_training_data = False):
self.limSPE = iSPE[int(lim_conf*self.X_train.shape[0])]

self.limSPE_train = copy.deepcopy(self.limSPE)

# calculating contributions
if not self.has_Y:
self.SPE_contrib_train = self.compute_SPE_contributions(
self.X_train_pred.values,
self.X_train.values)

# denormalizing

Expand All @@ -372,7 +410,12 @@ def train (self, lim_conf = 0.99, delete_training_data = False):
self.index_train = self.X_train.index

self.SPE_train = pd.Series(self.SPE_train, index = self.index_train)


if not self.has_Y:
self.SPE_contrib_train = pd.DataFrame(self.SPE_contrib_train,
index = self.index_train,
columns = self.tags_X)

# deleting training data, if applicable
if delete_training_data:
del self.X_train, self.X_train_orig, \
Expand All @@ -381,7 +424,8 @@ def train (self, lim_conf = 0.99, delete_training_data = False):
del self.Y_train, self.Y_train_orig, \
self.Y_train_pred, self.Y_train_pred_orig
else:
del self.X_train_pred, self.X_train_pred_orig
del self.X_train_pred, self.X_train_pred_orig, \
self.SPE_contrib_train

###########################################################################

Expand Down Expand Up @@ -619,7 +663,7 @@ def test (self, redefine_limit = False, delete_testing_data = False):

self.test_time = end_test - start_test

# storing results and calculating SPE
# storing results and calculating SPE and SPE_contrib

if self.has_Y:
self.Y_test_pred = pd.DataFrame(test_pred,
Expand All @@ -636,7 +680,10 @@ def test (self, redefine_limit = False, delete_testing_data = False):
self.SPE_test = np.sum((self.X_test.values-
self.X_test_pred.values)**2,
axis=1)

self.SPE_contrib_test = self.compute_SPE_contributions(
self.X_test_pred.values,
self.X_test.values)

# redefining the limit, for the validation case

if redefine_limit:
Expand Down Expand Up @@ -681,6 +728,11 @@ def test (self, redefine_limit = False, delete_testing_data = False):
self.SPE_test = pd.Series(self.SPE_test,
index = self.X_test.index)

if not self.has_Y:
self.SPE_contrib_test = pd.DataFrame(self.SPE_contrib_test,
index = self.X_test.index,
columns = self.tags_test_X)

self.alarms = {}

self.alarmOutlier = pd.Series(self.alarmOutlier,
Expand All @@ -696,12 +748,14 @@ def test (self, redefine_limit = False, delete_testing_data = False):
# deleting testing data, if applicable
if delete_testing_data:
del self.X_test, self.X_test_orig, \
self.SPE_test, self.alarms, self.alarmOutlier
self.SPE_test, \
self.alarms, self.alarmOutlier
if self.has_Y:
del self.Y_test, self.Y_test_orig, \
self.Y_test_pred, self.Y_test_pred_orig
else:
del self.X_test_pred, self.X_test_pred_orig
del self.X_test_pred, self.X_test_pred_orig, \
self.SPE_contrib_test,
if hasattr(self, 'alarmCount'): del self.alarmCount

###########################################################################
Expand Down Expand Up @@ -757,7 +811,55 @@ def plot_SPE (self, ax = None, train_or_test = 'train', logy = True,

if legends:
ax.legend(fontsize=12);


###########################################################################

def plot_SPE_contributions(self, ax = None, train_or_test = 'test'):
"""
Plotting the temporal evolution of SPE contributions on a heatmap.
Parameters:
----------
ax: matplotlib.axes._subplots.AxesSubplot
Axis where the graph will be plotted.
train_or_test: string
Indicates whether to plot the 'train' or 'test' graph.
"""

if self.has_Y:
print('This is not a reconstruction model!')
return

if ax is None:
fig, ax = plt.subplots(figsize=(20, 8))

if train_or_test == 'train':
SPE_contrib = self.SPE_contrib_train
elif train_or_test == 'test':
SPE_contrib = self.SPE_contrib_test

# Plot heatmap
sns.heatmap(SPE_contrib, ax=ax,
yticklabels=int(SPE_contrib.shape[0] / 10),
cmap="Blues")

yticks = ax.get_yticks()
if hasattr(SPE_contrib, 'index') \
and hasattr(SPE_contrib.index, 'to_list'):
# Convert tick positions to datetime using the DataFrame's index
datetime_labels = [SPE_contrib.index[int(tick)] for
tick in yticks if 0 <= int(tick) <
len(SPE_contrib.index)]
else:
# Fallback: Use integer positions as labels
datetime_labels = yticks

ax.set_yticks(yticks)
ax.set_yticklabels([d.strftime('%Y-%m-%d %H:%M:%S')
for d in datetime_labels], rotation=0)

ax.figure.autofmt_xdate()

###########################################################################

def plot_predictions (self, ax = None, train_or_test = 'train',
Expand Down
162 changes: 81 additions & 81 deletions docs/source/tutorial_real_process.ipynb

Large diffs are not rendered by default.

453 changes: 436 additions & 17 deletions docs/source/tutorial_tep.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
numpy>=2.1.0
scipy>=1.14.1
numpy>=1.26
scipy>=1.10
scikit-learn>=1.5.1
pandas>=2.2.2
matplotlib>=3.9.0
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
name='bibmon',
description='Library with routines for data-driven process monitoring.',
license='Apache 2.0',
version='1.1.6',
version='1.2.0',
long_description=README,
long_description_content_type='text/markdown',
url='https://github.com/petrobras/bibmon',
Expand All @@ -38,6 +38,6 @@
package_data={
'bibmon': ['real_process_data/*.csv','tennessee_eastman/*.dat'],
},
python_requires=">=3.12",
python_requires=">=3.10",
install_requires=install_requires
)
)

0 comments on commit d3953d6

Please sign in to comment.