ahadalii commited on
Commit
0b794cc
·
verified ·
1 Parent(s): 5ce771e

Upload 8 files

Browse files

project/
├── ai4i2020.csv # Dataset file
├── analysis.py # EDA analysis script
├── preprocessing.py # Data preprocessing module
├── model.py # Machine learning model
├── train_model.py # Script to train and save model
├── app.py # Streamlit web application
└── README.md # Project documentation

Files changed (8) hide show
  1. README.md +215 -16
  2. ai4i2020.csv +0 -0
  3. analysis.py +431 -0
  4. app.py +989 -0
  5. model.py +235 -0
  6. preprocessing.py +205 -0
  7. requirements.txt +5 -3
  8. train_model.py +52 -0
README.md CHANGED
@@ -1,19 +1,218 @@
1
- ---
2
- title: Predictive Maintenance System
3
- emoji: 🚀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
- pinned: false
11
- short_description: Predicts when industrial machinery will need maintenance.
12
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Welcome to Streamlit!
 
 
 
 
 
 
15
 
16
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
19
- forums](https://discuss.streamlit.io).
 
1
+ # Predictive Maintenance System - AI4I 2020 Dataset
2
+
3
+ ## Project Overview
4
+
5
+ This project implements a comprehensive predictive maintenance system using machine learning to predict when industrial machinery needs maintenance. The system analyzes the AI4I 2020 Predictive Maintenance Dataset and provides interactive visualizations and real-time predictions through a Streamlit web application.
6
+
7
+ ## Features
8
+
9
+ - **Comprehensive EDA**: 15+ different exploratory data analyses
10
+ - **Machine Learning Model**: Random Forest Classifier for failure prediction
11
+ - **Real-time Predictions**: Interactive interface for runtime predictions
12
+ - **Maintenance Scheduling**: Estimates time to failure and maintenance urgency
13
+ - **Interactive Visualizations**: Dynamic charts and graphs using Plotly
14
+ - **Batch Processing**: Upload CSV files for batch predictions
15
+
16
+ ## Dataset
17
+
18
+ The AI4I 2020 Predictive Maintenance Dataset contains:
19
+ - **10,000 machine records**
20
+ - **14 features** including temperature, rotational speed, torque, and tool wear
21
+ - **Binary target**: Machine failure (0 = no failure, 1 = failure)
22
+ - **5 failure types**: TWF, HDF, PWF, OSF, RNF
23
+
24
+ ## Project Structure
25
+
26
+ ```
27
+ project/
28
+ ├── ai4i2020.csv # Dataset file
29
+ ├── analysis.py # EDA analysis script
30
+ ├── preprocessing.py # Data preprocessing module
31
+ ├── model.py # Machine learning model
32
+ ├── train_model.py # Script to train and save model
33
+ ├── app.py # Streamlit web application
34
+ └── README.md # Project documentation
35
+ ```
36
+
37
+ ## Installation
38
+
39
+ 1. **Clone or download the project**
40
+ 2. **Install dependencies** (requirements listed below):
41
+ ```bash
42
+ pip install pandas==2.1.4 numpy==1.26.2 matplotlib==3.8.2 seaborn==0.13.0 scikit-learn==1.4.0 streamlit==1.29.0 plotly==5.18.0
43
+ ```
44
+ 3. **Ensure the dataset file (`ai4i2020.csv`) is in the project directory**
45
+
46
+ ## Usage
47
+
48
+ ### Option 1: Run Streamlit App Directly (Recommended)
49
+
50
+ The app will train the model automatically on first run:
51
+
52
+ ```bash
53
+ streamlit run app.py
54
+ ```
55
+
56
+ ### Option 2: Train Model First, Then Run App
57
+
58
+ 1. **Train the model**:
59
+ ```bash
60
+ python train_model.py
61
+ ```
62
+
63
+ 2. **Run the Streamlit app**:
64
+ ```bash
65
+ streamlit run app.py
66
+ ```
67
+
68
+ ### Option 3: Run EDA Analysis Only
69
+
70
+ ```bash
71
+ python analysis.py
72
+ ```
73
+
74
+ ## Streamlit Application
75
+
76
+ The web application includes four main sections:
77
+
78
+ ### 1. Introduction
79
+ - Dataset overview and statistics
80
+ - Project goals and features
81
+ - Dataset preview
82
+
83
+ ### 2. Exploratory Data Analysis
84
+ Interactive visualizations including:
85
+ - Summary statistics
86
+ - Target distribution
87
+ - Feature distributions
88
+ - Correlation analysis
89
+ - Failure analysis by machine type
90
+ - Tool wear analysis
91
+ - Temperature analysis
92
+ - Outlier detection
93
+ - Pairwise relationships
94
+ - Failure type breakdown
95
 
96
+ ### 3. Model & Predictions
97
+ - Model performance metrics
98
+ - Feature importance visualization
99
+ - **Runtime prediction**: Enter machine parameters to predict maintenance needs
100
+ - **Batch prediction**: Upload CSV file for multiple predictions
101
+ - Maintenance urgency assessment
102
+ - Time-to-failure estimation
103
 
104
+ ### 4. Conclusion
105
+ - Key findings and takeaways
106
+ - Applications and future improvements
107
+
108
+ ## EDA Analyses Performed
109
+
110
+ 1. Summary statistics (mean, median, mode, etc.)
111
+ 2. Missing value analysis
112
+ 3. Data types and unique value counts
113
+ 4. Target variable distribution
114
+ 5. Correlation analysis
115
+ 6. Outlier detection (IQR method)
116
+ 7. Feature distribution analysis
117
+ 8. Failure analysis by machine type
118
+ 9. Tool wear analysis
119
+ 10. Temperature analysis
120
+ 11. Power and rotational speed analysis
121
+ 12. Pairwise feature relationships
122
+ 13. Detailed failure type analysis
123
+ 14. Time to failure estimation
124
+ 15. Grouped aggregations
125
+
126
+ ## Machine Learning Model
127
+
128
+ - **Algorithm**: Random Forest Classifier
129
+ - **Features**: 11 engineered features including:
130
+ - Air temperature, Process temperature
131
+ - Rotational speed, Torque
132
+ - Tool wear
133
+ - Temperature difference
134
+ - Power
135
+ - Machine type encoding
136
+
137
+ - **Evaluation Metrics**:
138
+ - Accuracy
139
+ - Precision
140
+ - Recall
141
+ - F1-Score
142
+ - ROC-AUC
143
+
144
+ ## Predictive Features
145
+
146
+ The model predicts:
147
+ 1. **Machine Failure**: Binary prediction (Yes/No)
148
+ 2. **Failure Probability**: Probability score (0-1)
149
+ 3. **Time to Failure**: Estimated minutes until maintenance needed
150
+ 4. **Maintenance Status**: Current maintenance requirement status
151
+ 5. **Maintenance Urgency**: CRITICAL, HIGH, MEDIUM, or LOW
152
+
153
+ ## Runtime Prediction
154
+
155
+ Users can input machine parameters:
156
+ - Machine Type (L, M, H)
157
+ - Air Temperature (K)
158
+ - Process Temperature (K)
159
+ - Rotational Speed (rpm)
160
+ - Torque (Nm)
161
+ - Tool Wear (minutes)
162
+
163
+ The system provides:
164
+ - Failure prediction
165
+ - Maintenance urgency level
166
+ - Estimated time to failure
167
+ - Detailed recommendations
168
+
169
+ ## Technical Stack
170
+
171
+ - **Python 3.8+**
172
+ - **Data Processing**: Pandas, NumPy
173
+ - **Visualization**: Matplotlib, Seaborn, Plotly
174
+ - **Machine Learning**: Scikit-learn
175
+ - **Web Framework**: Streamlit
176
+
177
+ ## Key Insights
178
+
179
+ 1. **Tool wear** is the most critical indicator of machine health
180
+ 2. **Temperature difference** between process and air temperature correlates with failures
181
+ 3. Machine **type affects failure rates** differently
182
+ 4. Early detection can **prevent costly downtime**
183
+ 5. Proactive maintenance scheduling can **optimize operations**
184
+
185
+ ## Applications
186
+
187
+ - Industrial manufacturing
188
+ - Equipment monitoring systems
189
+ - Preventive maintenance scheduling
190
+ - Cost reduction through failure prevention
191
+ - Production optimization
192
+
193
+ ## Future Enhancements
194
+
195
+ 1. Real-time data streaming integration
196
+ 2. IoT sensor integration
197
+ 3. Advanced ensemble methods
198
+ 4. Time-series analysis
199
+ 5. Automated alert system
200
+ 6. Historical maintenance record integration
201
+
202
+ ## Author
203
+
204
+ Developed as part of the Introduction to Data Science course project.
205
+
206
+ ## License
207
+
208
+ This project is for educational purposes.
209
+
210
+ ## Acknowledgments
211
+
212
+ - AI4I 2020 Predictive Maintenance Dataset
213
+ - Scikit-learn documentation
214
+ - Streamlit documentation
215
+
216
+ ---
217
 
218
+ **Note**: Make sure the `ai4i2020.csv` file is in the same directory as the scripts before running the application.
 
ai4i2020.csv ADDED
The diff for this file is too large to render. See raw diff
 
analysis.py ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Comprehensive Exploratory Data Analysis (EDA) for AI4I 2020 Predictive Maintenance Dataset
3
+ This script performs 10-15 different analyses as required for the project.
4
+ """
5
+
6
+ import pandas as pd
7
+ import numpy as np
8
+ import matplotlib.pyplot as plt
9
+ import seaborn as sns
10
+ import warnings
11
+ warnings.filterwarnings('ignore')
12
+
13
+ # Set style for better visualizations
14
+ sns.set_style("whitegrid")
15
+ plt.rcParams['figure.figsize'] = (12, 6)
16
+
17
+ class EDAAnalysis:
18
+ def __init__(self, data_path='ai4i2020.csv'):
19
+ """Initialize the EDA analysis class"""
20
+ self.df = pd.read_csv(data_path)
21
+ self.prepare_data()
22
+
23
+ def prepare_data(self):
24
+ """Prepare data for analysis"""
25
+ # Create a copy for analysis
26
+ self.df_clean = self.df.copy()
27
+
28
+ # Calculate temperature difference (important feature)
29
+ self.df_clean['Temperature difference [K]'] = (
30
+ self.df_clean['Process temperature [K]'] -
31
+ self.df_clean['Air temperature [K]']
32
+ )
33
+
34
+ # Calculate power (Rotational speed * Torque)
35
+ self.df_clean['Power [W]'] = (
36
+ self.df_clean['Rotational speed [rpm]'] *
37
+ self.df_clean['Torque [Nm]'] / 9.5488 # Conversion factor
38
+ )
39
+
40
+ # Failure type names
41
+ self.failure_types = {
42
+ 'TWF': 'Tool Wear Failure',
43
+ 'HDF': 'Heat Dissipation Failure',
44
+ 'PWF': 'Power Failure',
45
+ 'OSF': 'Overstrain Failure',
46
+ 'RNF': 'Random Failure'
47
+ }
48
+
49
+ def analysis_1_summary_statistics(self):
50
+ """Analysis 1: Summary statistics for numerical features"""
51
+ print("="*80)
52
+ print("ANALYSIS 1: SUMMARY STATISTICS")
53
+ print("="*80)
54
+
55
+ numerical_cols = [
56
+ 'Air temperature [K]', 'Process temperature [K]',
57
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]',
58
+ 'Temperature difference [K]', 'Power [W]'
59
+ ]
60
+
61
+ summary = self.df_clean[numerical_cols].describe()
62
+ print(summary)
63
+ print("\n")
64
+
65
+ return summary
66
+
67
+ def analysis_2_missing_values(self):
68
+ """Analysis 2: Missing value analysis"""
69
+ print("="*80)
70
+ print("ANALYSIS 2: MISSING VALUE ANALYSIS")
71
+ print("="*80)
72
+
73
+ missing = self.df_clean.isnull().sum()
74
+ missing_pct = (missing / len(self.df_clean)) * 100
75
+
76
+ missing_df = pd.DataFrame({
77
+ 'Missing Count': missing,
78
+ 'Missing Percentage': missing_pct
79
+ })
80
+ missing_df = missing_df[missing_df['Missing Count'] > 0]
81
+
82
+ if len(missing_df) == 0:
83
+ print("✓ No missing values found in the dataset!")
84
+ else:
85
+ print(missing_df)
86
+ print("\n")
87
+
88
+ return missing_df
89
+
90
+ def analysis_3_data_types(self):
91
+ """Analysis 3: Data types and unique value counts"""
92
+ print("="*80)
93
+ print("ANALYSIS 3: DATA TYPES AND UNIQUE VALUES")
94
+ print("="*80)
95
+
96
+ info_df = pd.DataFrame({
97
+ 'Column': self.df_clean.columns,
98
+ 'Data Type': self.df_clean.dtypes,
99
+ 'Unique Values': [self.df_clean[col].nunique() for col in self.df_clean.columns],
100
+ 'Non-Null Count': self.df_clean.count().values
101
+ })
102
+
103
+ print(info_df.to_string(index=False))
104
+ print("\n")
105
+
106
+ # Categorical value counts
107
+ print("Type Distribution:")
108
+ print(self.df_clean['Type'].value_counts())
109
+ print("\n")
110
+
111
+ return info_df
112
+
113
+ def analysis_4_target_distribution(self):
114
+ """Analysis 4: Target variable (Machine failure) distribution"""
115
+ print("="*80)
116
+ print("ANALYSIS 4: TARGET VARIABLE DISTRIBUTION")
117
+ print("="*80)
118
+
119
+ failure_counts = self.df_clean['Machine failure'].value_counts()
120
+ failure_pct = self.df_clean['Machine failure'].value_counts(normalize=True) * 100
121
+
122
+ print("Machine Failure Distribution:")
123
+ print(f" No Failure (0): {failure_counts[0]} ({failure_pct[0]:.2f}%)")
124
+ print(f" Failure (1): {failure_counts[1]} ({failure_pct[1]:.2f}%)")
125
+ print("\n")
126
+
127
+ # Failure types breakdown
128
+ print("Failure Types Breakdown:")
129
+ for ft in ['TWF', 'HDF', 'PWF', 'OSF', 'RNF']:
130
+ count = self.df_clean[ft].sum()
131
+ pct = (count / len(self.df_clean)) * 100
132
+ print(f" {self.failure_types[ft]}: {count} ({pct:.2f}%)")
133
+ print("\n")
134
+
135
+ return failure_counts
136
+
137
+ def analysis_5_correlation_analysis(self):
138
+ """Analysis 5: Correlation analysis"""
139
+ print("="*80)
140
+ print("ANALYSIS 5: CORRELATION ANALYSIS")
141
+ print("="*80)
142
+
143
+ numerical_cols = [
144
+ 'Air temperature [K]', 'Process temperature [K]',
145
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]',
146
+ 'Temperature difference [K]', 'Power [W]', 'Machine failure'
147
+ ]
148
+
149
+ corr_matrix = self.df_clean[numerical_cols].corr()
150
+
151
+ print("Correlation with Machine Failure:")
152
+ failure_corr = corr_matrix['Machine failure'].sort_values(ascending=False)
153
+ print(failure_corr)
154
+ print("\n")
155
+
156
+ return corr_matrix
157
+
158
+ def analysis_6_outlier_detection(self):
159
+ """Analysis 6: Outlier detection using IQR method"""
160
+ print("="*80)
161
+ print("ANALYSIS 6: OUTLIER DETECTION (IQR METHOD)")
162
+ print("="*80)
163
+
164
+ numerical_cols = [
165
+ 'Air temperature [K]', 'Process temperature [K]',
166
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]'
167
+ ]
168
+
169
+ outlier_summary = {}
170
+
171
+ for col in numerical_cols:
172
+ Q1 = self.df_clean[col].quantile(0.25)
173
+ Q3 = self.df_clean[col].quantile(0.75)
174
+ IQR = Q3 - Q1
175
+ lower_bound = Q1 - 1.5 * IQR
176
+ upper_bound = Q3 + 1.5 * IQR
177
+
178
+ outliers = self.df_clean[(self.df_clean[col] < lower_bound) |
179
+ (self.df_clean[col] > upper_bound)]
180
+ outlier_count = len(outliers)
181
+ outlier_pct = (outlier_count / len(self.df_clean)) * 100
182
+
183
+ outlier_summary[col] = {
184
+ 'Lower Bound': lower_bound,
185
+ 'Upper Bound': upper_bound,
186
+ 'Outlier Count': outlier_count,
187
+ 'Outlier Percentage': outlier_pct
188
+ }
189
+
190
+ print(f"{col}:")
191
+ print(f" Bounds: [{lower_bound:.2f}, {upper_bound:.2f}]")
192
+ print(f" Outliers: {outlier_count} ({outlier_pct:.2f}%)")
193
+
194
+ print("\n")
195
+ return outlier_summary
196
+
197
+ def analysis_7_feature_distributions(self):
198
+ """Analysis 7: Feature distribution analysis"""
199
+ print("="*80)
200
+ print("ANALYSIS 7: FEATURE DISTRIBUTION ANALYSIS")
201
+ print("="*80)
202
+
203
+ numerical_cols = [
204
+ 'Air temperature [K]', 'Process temperature [K]',
205
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]'
206
+ ]
207
+
208
+ dist_stats = {}
209
+ for col in numerical_cols:
210
+ dist_stats[col] = {
211
+ 'Mean': self.df_clean[col].mean(),
212
+ 'Median': self.df_clean[col].median(),
213
+ 'Mode': self.df_clean[col].mode()[0] if len(self.df_clean[col].mode()) > 0 else None,
214
+ 'Std': self.df_clean[col].std(),
215
+ 'Skewness': self.df_clean[col].skew(),
216
+ 'Kurtosis': self.df_clean[col].kurtosis()
217
+ }
218
+
219
+ print(f"{col}:")
220
+ print(f" Mean: {dist_stats[col]['Mean']:.2f}")
221
+ print(f" Median: {dist_stats[col]['Median']:.2f}")
222
+ print(f" Std Dev: {dist_stats[col]['Std']:.2f}")
223
+ print(f" Skewness: {dist_stats[col]['Skewness']:.2f}")
224
+ print(f" Kurtosis: {dist_stats[col]['Kurtosis']:.2f}")
225
+ print()
226
+
227
+ print("\n")
228
+ return dist_stats
229
+ def analysis_8_failure_by_type(self):
230
+ """Analysis 8: Failure analysis by machine type"""
231
+ print("="*80)
232
+ print("ANALYSIS 8: FAILURE ANALYSIS BY MACHINE TYPE")
233
+ print("="*80)
234
+
235
+ failure_by_type = self.df_clean.groupby('Type')['Machine failure'].agg([
236
+ 'count', 'sum', 'mean'
237
+ ]).round(4)
238
+ failure_by_type.columns = ['Total Machines', 'Failures', 'Failure Rate']
239
+ failure_by_type['Failure Rate'] = failure_by_type['Failure Rate'] * 100
240
+
241
+ print(failure_by_type)
242
+ print("\n")
243
+
244
+ return failure_by_type
245
+
246
+ def analysis_9_tool_wear_analysis(self):
247
+ """Analysis 9: Tool wear analysis and relationship with failures"""
248
+ print("="*80)
249
+ print("ANALYSIS 9: TOOL WEAR ANALYSIS")
250
+ print("="*80)
251
+
252
+ print("Tool Wear Statistics:")
253
+ print(f" Mean: {self.df_clean['Tool wear [min]'].mean():.2f} minutes")
254
+ print(f" Median: {self.df_clean['Tool wear [min]'].median():.2f} minutes")
255
+ print(f" Max: {self.df_clean['Tool wear [min]'].max():.2f} minutes")
256
+ print(f" Min: {self.df_clean['Tool wear [min]'].min():.2f} minutes")
257
+ print()
258
+
259
+ # Tool wear vs failure
260
+ tool_wear_failure = self.df_clean.groupby('Machine failure')['Tool wear [min]'].agg([
261
+ 'mean', 'median', 'std', 'min', 'max'
262
+ ])
263
+ print("Tool Wear by Failure Status:")
264
+ print(tool_wear_failure)
265
+ print("\n")
266
+
267
+ return tool_wear_failure
268
+
269
+ def analysis_10_temperature_analysis(self):
270
+ """Analysis 10: Temperature analysis"""
271
+ print("="*80)
272
+ print("ANALYSIS 10: TEMPERATURE ANALYSIS")
273
+ print("="*80)
274
+
275
+ temp_stats = self.df_clean.groupby('Machine failure')[
276
+ ['Air temperature [K]', 'Process temperature [K]', 'Temperature difference [K]']
277
+ ].agg(['mean', 'std'])
278
+
279
+ print("Temperature Statistics by Failure Status:")
280
+ print(temp_stats)
281
+ print("\n")
282
+
283
+ return temp_stats
284
+
285
+ def analysis_11_power_analysis(self):
286
+ """Analysis 11: Power and rotational speed analysis"""
287
+ print("="*80)
288
+ print("ANALYSIS 11: POWER AND ROTATIONAL SPEED ANALYSIS")
289
+ print("="*80)
290
+
291
+ power_stats = self.df_clean.groupby('Machine failure')[
292
+ ['Rotational speed [rpm]', 'Torque [Nm]', 'Power [W]']
293
+ ].agg(['mean', 'std', 'min', 'max'])
294
+
295
+ print("Power Statistics by Failure Status:")
296
+ print(power_stats)
297
+ print("\n")
298
+
299
+ return power_stats
300
+
301
+ def analysis_12_pairwise_relationships(self):
302
+ """Analysis 12: Pairwise feature relationships"""
303
+ print("="*80)
304
+ print("ANALYSIS 12: PAIRWISE FEATURE RELATIONSHIPS")
305
+ print("="*80)
306
+
307
+ key_features = [
308
+ 'Tool wear [min]', 'Temperature difference [K]',
309
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Machine failure'
310
+ ]
311
+
312
+ pairwise_corr = self.df_clean[key_features].corr()
313
+ print("Pairwise Correlations:")
314
+ print(pairwise_corr)
315
+ print("\n")
316
+
317
+ return pairwise_corr
318
+
319
+ def analysis_13_failure_type_analysis(self):
320
+ """Analysis 13: Detailed failure type analysis"""
321
+ print("="*80)
322
+ print("ANALYSIS 13: DETAILED FAILURE TYPE ANALYSIS")
323
+ print("="*80)
324
+
325
+ failure_types = ['TWF', 'HDF', 'PWF', 'OSF', 'RNF']
326
+
327
+ for ft in failure_types:
328
+ failed_machines = self.df_clean[self.df_clean[ft] == 1]
329
+ if len(failed_machines) > 0:
330
+ print(f"\n{self.failure_types[ft]} ({ft}):")
331
+ print(f" Count: {len(failed_machines)}")
332
+ print(f" Avg Tool Wear: {failed_machines['Tool wear [min]'].mean():.2f} min")
333
+ print(f" Avg Temp Diff: {failed_machines['Temperature difference [K]'].mean():.2f} K")
334
+ print(f" Avg Rotational Speed: {failed_machines['Rotational speed [rpm]'].mean():.2f} rpm")
335
+ print(f" Avg Torque: {failed_machines['Torque [Nm]'].mean():.2f} Nm")
336
+
337
+ print("\n")
338
+
339
+ def analysis_14_time_to_failure_estimation(self):
340
+ """Analysis 14: Time to failure estimation based on tool wear"""
341
+ print("="*80)
342
+ print("ANALYSIS 14: TIME TO FAILURE ESTIMATION")
343
+ print("="*80)
344
+
345
+ # Analyze tool wear progression for machines that failed
346
+ failed_machines = self.df_clean[self.df_clean['Machine failure'] == 1]
347
+
348
+ if len(failed_machines) > 0:
349
+ avg_tool_wear_at_failure = failed_machines['Tool wear [min]'].mean()
350
+ median_tool_wear_at_failure = failed_machines['Tool wear [min]'].median()
351
+
352
+ print(f"Average Tool Wear at Failure: {avg_tool_wear_at_failure:.2f} minutes")
353
+ print(f"Median Tool Wear at Failure: {median_tool_wear_at_failure:.2f} minutes")
354
+ print()
355
+
356
+ # Estimate time remaining for machines not yet failed
357
+ non_failed = self.df_clean[self.df_clean['Machine failure'] == 0]
358
+ if len(non_failed) > 0:
359
+ non_failed['Estimated Time to Failure'] = (
360
+ avg_tool_wear_at_failure - non_failed['Tool wear [min]']
361
+ )
362
+ non_failed['Estimated Time to Failure'] = non_failed['Estimated Time to Failure'].clip(lower=0)
363
+
364
+ print("Time to Failure Estimates (for non-failed machines):")
365
+ print(f" Machines needing immediate maintenance (< 10 min): "
366
+ f"{(non_failed['Estimated Time to Failure'] < 10).sum()}")
367
+ print(f" Machines needing maintenance soon (10-50 min): "
368
+ f"{((non_failed['Estimated Time to Failure'] >= 10) & (non_failed['Estimated Time to Failure'] < 50)).sum()}")
369
+ print(f" Machines with time remaining (> 50 min): "
370
+ f"{(non_failed['Estimated Time to Failure'] >= 50).sum()}")
371
+
372
+ print("\n")
373
+
374
+ def analysis_15_grouped_aggregations(self):
375
+ """Analysis 15: Grouped aggregations by type and failure status"""
376
+ print("="*80)
377
+ print("ANALYSIS 15: GROUPED AGGREGATIONS")
378
+ print("="*80)
379
+
380
+ grouped = self.df_clean.groupby(['Type', 'Machine failure']).agg({
381
+ 'Tool wear [min]': ['mean', 'std', 'max'],
382
+ 'Temperature difference [K]': ['mean', 'std'],
383
+ 'Rotational speed [rpm]': ['mean', 'std'],
384
+ 'Torque [Nm]': ['mean', 'std']
385
+ })
386
+
387
+ print("Grouped Statistics by Type and Failure Status:")
388
+ print(grouped)
389
+ print("\n")
390
+
391
+ return grouped
392
+
393
+ def run_all_analyses(self):
394
+ """Run all EDA analyses"""
395
+ print("\n" + "="*80)
396
+ print("COMPREHENSIVE EXPLORATORY DATA ANALYSIS")
397
+ print("AI4I 2020 Predictive Maintenance Dataset")
398
+ print("="*80 + "\n")
399
+
400
+ results = {}
401
+
402
+ results['summary_stats'] = self.analysis_1_summary_statistics()
403
+ results['missing_values'] = self.analysis_2_missing_values()
404
+ results['data_types'] = self.analysis_3_data_types()
405
+ results['target_distribution'] = self.analysis_4_target_distribution()
406
+ results['correlation'] = self.analysis_5_correlation_analysis()
407
+ results['outliers'] = self.analysis_6_outlier_detection()
408
+ results['distributions'] = self.analysis_7_feature_distributions()
409
+ results['failure_by_type'] = self.analysis_8_failure_by_type()
410
+ results['tool_wear'] = self.analysis_9_tool_wear_analysis()
411
+ results['temperature'] = self.analysis_10_temperature_analysis()
412
+ results['power'] = self.analysis_11_power_analysis()
413
+ results['pairwise'] = self.analysis_12_pairwise_relationships()
414
+ self.analysis_13_failure_type_analysis()
415
+ self.analysis_14_time_to_failure_estimation()
416
+ results['grouped'] = self.analysis_15_grouped_aggregations()
417
+
418
+ print("="*80)
419
+ print("EDA ANALYSIS COMPLETE!")
420
+ print("="*80)
421
+
422
+ return results
423
+
424
+ if __name__ == "__main__":
425
+ # Run EDA analysis
426
+ eda = EDAAnalysis('ai4i2020.csv')
427
+ results = eda.run_all_analyses()
428
+
429
+ # Save processed data for modeling
430
+ eda.df_clean.to_csv('processed_data.csv', index=False)
431
+ print("\nProcessed data saved to 'processed_data.csv'")
app.py ADDED
@@ -0,0 +1,989 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Streamlit Application for Predictive Maintenance Project
3
+ Interactive web app for EDA, model visualization, and runtime predictions
4
+ """
5
+
6
+ import streamlit as st
7
+ import pandas as pd
8
+ import numpy as np
9
+ import plotly.express as px
10
+ import plotly.graph_objects as go
11
+ from plotly.subplots import make_subplots
12
+ import pickle
13
+ import warnings
14
+ warnings.filterwarnings('ignore')
15
+
16
+ # Import custom modules
17
+ from preprocessing import DataPreprocessor
18
+ from model import PredictiveMaintenanceModel
19
+
20
+ # Page configuration
21
+ st.set_page_config(
22
+ page_title="Predictive Maintenance System",
23
+ page_icon="🔧",
24
+ layout="wide",
25
+ initial_sidebar_state="expanded"
26
+ )
27
+
28
+ # Custom CSS
29
+ st.markdown("""
30
+ <style>
31
+ .main-header {
32
+ font-size: 3rem;
33
+ font-weight: bold;
34
+ color: #1f77b4;
35
+ text-align: center;
36
+ margin-bottom: 2rem;
37
+ }
38
+ .sub-header {
39
+ font-size: 1.5rem;
40
+ color: #ff7f0e;
41
+ margin-top: 2rem;
42
+ margin-bottom: 1rem;
43
+ }
44
+ .metric-card {
45
+ background-color: #f0f2f6;
46
+ padding: 1rem;
47
+ border-radius: 0.5rem;
48
+ margin: 0.5rem 0;
49
+ }
50
+ </style>
51
+ """, unsafe_allow_html=True)
52
+
53
+ # Initialize session state
54
+ if 'model' not in st.session_state:
55
+ st.session_state.model = None
56
+ if 'preprocessor' not in st.session_state:
57
+ st.session_state.preprocessor = None
58
+ if 'data' not in st.session_state:
59
+ st.session_state.data = None
60
+
61
+ @st.cache_data
62
+ def load_data():
63
+ """Load and cache the dataset"""
64
+ df = pd.read_csv('ai4i2020.csv')
65
+ # Create additional features
66
+ df['Temperature difference [K]'] = (
67
+ df['Process temperature [K]'] - df['Air temperature [K]']
68
+ )
69
+ df['Power [W]'] = (
70
+ df['Rotational speed [rpm]'] * df['Torque [Nm]'] / 9.5488
71
+ )
72
+ return df
73
+
74
+ def train_model():
75
+ """Train the model and preprocessor"""
76
+ with st.spinner("Training model... This may take a moment."):
77
+ preprocessor = DataPreprocessor('ai4i2020.csv')
78
+ X_train, X_test, y_train, y_test, feature_columns = preprocessor.prepare_data()
79
+
80
+ model = PredictiveMaintenanceModel()
81
+ model.train(X_train, y_train)
82
+
83
+ # Evaluate model
84
+ results = model.evaluate(X_test, y_test)
85
+
86
+ st.session_state.model = model
87
+ st.session_state.preprocessor = preprocessor
88
+ st.session_state.feature_columns = feature_columns
89
+
90
+ return results
91
+
92
+ # Main App
93
+ def main():
94
+ # Header
95
+ st.markdown('<h1 class="main-header">🔧 Predictive Maintenance System</h1>', unsafe_allow_html=True)
96
+ st.markdown("---")
97
+
98
+ # Sidebar Navigation
99
+ st.sidebar.title("Navigation")
100
+ page = st.sidebar.radio(
101
+ "Select Page",
102
+ ["Introduction", "Exploratory Data Analysis", "Model & Predictions", "Conclusion"]
103
+ )
104
+
105
+ # Load data
106
+ df = load_data()
107
+ st.session_state.data = df
108
+
109
+ if page == "Introduction":
110
+ show_introduction(df)
111
+ elif page == "Exploratory Data Analysis":
112
+ show_eda(df)
113
+ elif page == "Model & Predictions":
114
+ show_model_predictions(df)
115
+ elif page == "Conclusion":
116
+ show_conclusion()
117
+
118
+ def show_introduction(df):
119
+ """Introduction page"""
120
+ st.markdown('<h2 class="sub-header">📋 Project Introduction</h2>', unsafe_allow_html=True)
121
+
122
+ col1, col2 = st.columns([2, 1])
123
+
124
+ with col1:
125
+ st.markdown("""
126
+ ### About the Dataset
127
+
128
+ This project uses the **AI4I 2020 Predictive Maintenance Dataset**, which contains
129
+ synthetic data simulating predictive maintenance scenarios for industrial machinery.
130
+
131
+ #### Dataset Overview:
132
+ - **Total Records**: 10,000 machines
133
+ - **Features**: 14 attributes including temperature, rotational speed, torque, and tool wear
134
+ - **Target**: Machine failure prediction (binary classification)
135
+ - **Failure Types**: Tool Wear Failure (TWF), Heat Dissipation Failure (HDF),
136
+ Power Failure (PWF), Overstrain Failure (OSF), and Random Failure (RNF)
137
+
138
+ #### Project Goals:
139
+ 1. **Exploratory Data Analysis**: Understand patterns and relationships in the data
140
+ 2. **Predictive Modeling**: Build a machine learning model to predict machine failures
141
+ 3. **Maintenance Scheduling**: Estimate when maintenance is needed and how urgent it is
142
+ 4. **Interactive Visualization**: Present findings through an interactive web application
143
+
144
+ #### Key Features:
145
+ - Comprehensive EDA with 15+ different analyses
146
+ - Random Forest Classifier for failure prediction
147
+ - Real-time predictions based on user input
148
+ - Maintenance urgency assessment
149
+ - Time-to-failure estimation
150
+ """)
151
+
152
+ with col2:
153
+ st.markdown("### Dataset Statistics")
154
+ st.metric("Total Machines", f"{len(df):,}")
155
+ st.metric("Features", len(df.columns))
156
+ st.metric("Machine Failures", f"{df['Machine failure'].sum():,}")
157
+ st.metric("Failure Rate", f"{(df['Machine failure'].mean()*100):.2f}%")
158
+
159
+ st.markdown("### Machine Types")
160
+ type_counts = df['Type'].value_counts()
161
+ type_meanings = {'L': 'Low Quality/Load', 'M': 'Medium Quality/Load', 'H': 'High Quality/Load'}
162
+ for machine_type, count in type_counts.items():
163
+ meaning = type_meanings.get(machine_type, '')
164
+ st.metric(f"Type {machine_type} ({meaning})", f"{count:,}")
165
+
166
+ st.markdown("---")
167
+ st.markdown("### Dataset Preview")
168
+ preview_mode = st.radio(
169
+ "Preview mode",
170
+ options=["First 20 rows", "Show all (10,000 rows)"],
171
+ index=0,
172
+ horizontal=True,
173
+ )
174
+ if preview_mode == "First 20 rows":
175
+ st.dataframe(df.head(20), use_container_width=True)
176
+ else:
177
+ st.dataframe(df, use_container_width=True)
178
+
179
+ st.markdown("### Dataset Information")
180
+ with st.expander("View Column Descriptions"):
181
+ st.markdown("""
182
+ - **UDI**: Unique identifier for each machine
183
+ - **Product ID**: Product identifier
184
+ - **Type**: Machine type (L = Low Quality/Load, M = Medium Quality/Load, H = High Quality/Load)
185
+ - **Air temperature [K]**: Air temperature in Kelvin
186
+ - **Process temperature [K]**: Process temperature in Kelvin
187
+ - **Rotational speed [rpm]**: Rotational speed in revolutions per minute
188
+ - **Torque [Nm]**: Torque in Newton meters
189
+ - **Tool wear [min]**: Tool wear in minutes
190
+ - **Machine failure**: Binary target (0 = no failure, 1 = failure)
191
+ - **TWF, HDF, PWF, OSF, RNF**: Different failure type indicators
192
+ """)
193
+
194
+ def show_eda(df):
195
+ """EDA page"""
196
+ st.markdown('<h2 class="sub-header">📊 Exploratory Data Analysis</h2>', unsafe_allow_html=True)
197
+
198
+ # Analysis selection
199
+ analysis_type = st.selectbox(
200
+ "Select Analysis Type",
201
+ [
202
+ "Summary Statistics",
203
+ "Data Types & Unique Values",
204
+ "Target Distribution",
205
+ "Feature Distributions",
206
+ "Correlation Analysis",
207
+ "Failure Analysis by Type",
208
+ "Tool Wear Analysis",
209
+ "Temperature Analysis",
210
+ "Power & Rotational Speed Analysis",
211
+ "Outlier Detection",
212
+ "Pairwise Relationships",
213
+ "Failure Type Breakdown",
214
+ "Time to Failure Estimation",
215
+ "Grouped Aggregations"
216
+ ]
217
+ )
218
+
219
+ st.markdown("---")
220
+
221
+ if analysis_type == "Summary Statistics":
222
+ st.subheader("Summary Statistics")
223
+ st.dataframe(df.describe(), use_container_width=True)
224
+
225
+ # Key metrics
226
+ col1, col2, col3, col4 = st.columns(4)
227
+ with col1:
228
+ st.metric("Mean Air Temp", f"{df['Air temperature [K]'].mean():.2f} K")
229
+ with col2:
230
+ st.metric("Mean Process Temp", f"{df['Process temperature [K]'].mean():.2f} K")
231
+ with col3:
232
+ st.metric("Mean Rotational Speed", f"{df['Rotational speed [rpm]'].mean():.0f} rpm")
233
+ with col4:
234
+ st.metric("Mean Torque", f"{df['Torque [Nm]'].mean():.2f} Nm")
235
+
236
+ elif analysis_type == "Data Types & Unique Values":
237
+ st.subheader("Data Types and Unique Values")
238
+
239
+ info_df = pd.DataFrame({
240
+ 'Column': df.columns,
241
+ 'Data Type': df.dtypes.astype(str),
242
+ 'Unique Values': [df[col].nunique() for col in df.columns],
243
+ 'Non-Null Count': df.count().values
244
+ })
245
+
246
+ st.dataframe(info_df, use_container_width=True)
247
+
248
+ st.subheader("Machine Type Distribution")
249
+ type_counts = df['Type'].value_counts()
250
+ type_meanings = {'L': 'Low Quality/Load', 'M': 'Medium Quality/Load', 'H': 'High Quality/Load'}
251
+
252
+ col1, col2 = st.columns(2)
253
+ with col1:
254
+ fig = px.bar(
255
+ x=type_counts.index,
256
+ y=type_counts.values,
257
+ title="Machine Type Distribution",
258
+ labels={'x': 'Machine Type', 'y': 'Count'},
259
+ color=type_counts.index,
260
+ color_discrete_sequence=['#e74c3c', '#3498db', '#2ecc71']
261
+ )
262
+ st.plotly_chart(fig, use_container_width=True)
263
+
264
+ with col2:
265
+ for machine_type, count in type_counts.items():
266
+ meaning = type_meanings.get(machine_type, '')
267
+ st.metric(f"Type {machine_type} ({meaning})", f"{count:,}")
268
+
269
+ elif analysis_type == "Target Distribution":
270
+ st.subheader("Machine Failure Distribution")
271
+
272
+ col1, col2 = st.columns(2)
273
+
274
+ with col1:
275
+ failure_counts = df['Machine failure'].value_counts()
276
+ fig = px.pie(
277
+ values=failure_counts.values,
278
+ names=['No Failure', 'Failure'],
279
+ title="Failure Distribution",
280
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
281
+ )
282
+ st.plotly_chart(fig, use_container_width=True)
283
+
284
+ with col2:
285
+ st.metric("No Failure", f"{failure_counts[0]:,} ({(failure_counts[0]/len(df)*100):.2f}%)")
286
+ st.metric("Failure", f"{failure_counts[1]:,} ({(failure_counts[1]/len(df)*100):.2f}%)")
287
+
288
+ # Failure types
289
+ st.subheader("Failure Types Breakdown")
290
+ failure_types = {
291
+ 'TWF': 'Tool Wear Failure',
292
+ 'HDF': 'Heat Dissipation Failure',
293
+ 'PWF': 'Power Failure',
294
+ 'OSF': 'Overstrain Failure',
295
+ 'RNF': 'Random Failure'
296
+ }
297
+ for ft_code, ft_name in failure_types.items():
298
+ count = df[ft_code].sum()
299
+ st.metric(ft_name, f"{count} ({(count/len(df)*100):.2f}%)")
300
+
301
+ elif analysis_type == "Feature Distributions":
302
+ st.subheader("Feature Distributions")
303
+
304
+ feature = st.selectbox(
305
+ "Select Feature",
306
+ ['Air temperature [K]', 'Process temperature [K]', 'Rotational speed [rpm]',
307
+ 'Torque [Nm]', 'Tool wear [min]', 'Temperature difference [K]', 'Power [W]']
308
+ )
309
+
310
+ col1, col2 = st.columns(2)
311
+
312
+ with col1:
313
+ fig = px.histogram(
314
+ df, x=feature, nbins=50,
315
+ title=f"Distribution of {feature}",
316
+ color_discrete_sequence=['#3498db']
317
+ )
318
+ st.plotly_chart(fig, use_container_width=True)
319
+
320
+ with col2:
321
+ fig = px.box(
322
+ df, y=feature,
323
+ title=f"Box Plot of {feature}",
324
+ color_discrete_sequence=['#9b59b6']
325
+ )
326
+ st.plotly_chart(fig, use_container_width=True)
327
+
328
+ # Statistics
329
+ st.subheader("Statistics")
330
+ col1, col2, col3, col4 = st.columns(4)
331
+ with col1:
332
+ st.metric("Mean", f"{df[feature].mean():.2f}")
333
+ with col2:
334
+ st.metric("Median", f"{df[feature].median():.2f}")
335
+ with col3:
336
+ st.metric("Std Dev", f"{df[feature].std():.2f}")
337
+ with col4:
338
+ st.metric("Skewness", f"{df[feature].skew():.2f}")
339
+
340
+ elif analysis_type == "Correlation Analysis":
341
+ st.subheader("Correlation Analysis")
342
+
343
+ numerical_cols = [
344
+ 'Air temperature [K]', 'Process temperature [K]',
345
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]',
346
+ 'Temperature difference [K]', 'Power [W]', 'Machine failure'
347
+ ]
348
+
349
+ corr_matrix = df[numerical_cols].corr()
350
+
351
+ fig = px.imshow(
352
+ corr_matrix,
353
+ text_auto=True,
354
+ aspect="auto",
355
+ title="Correlation Heatmap",
356
+ color_continuous_scale="RdBu"
357
+ )
358
+ st.plotly_chart(fig, use_container_width=True)
359
+
360
+ st.subheader("Correlation with Machine Failure")
361
+ failure_corr = corr_matrix['Machine failure'].sort_values(ascending=False)
362
+ fig = px.bar(
363
+ x=failure_corr.index,
364
+ y=failure_corr.values,
365
+ title="Feature Correlation with Machine Failure",
366
+ labels={'x': 'Feature', 'y': 'Correlation'},
367
+ color=failure_corr.values,
368
+ color_continuous_scale="RdYlGn"
369
+ )
370
+ st.plotly_chart(fig, use_container_width=True)
371
+
372
+ elif analysis_type == "Failure Analysis by Type":
373
+ st.subheader("Failure Analysis by Machine Type")
374
+ st.info("**Machine Type Meanings**: L = Low Quality/Load, M = Medium Quality/Load, H = High Quality/Load")
375
+
376
+ failure_by_type = df.groupby('Type')['Machine failure'].agg(['count', 'sum', 'mean']).reset_index()
377
+ failure_by_type.columns = ['Type', 'Total Machines', 'Failures', 'Failure Rate']
378
+ failure_by_type['Failure Rate'] = failure_by_type['Failure Rate'] * 100
379
+ failure_by_type['Type_Label'] = failure_by_type['Type'].map({
380
+ 'L': 'L (Low Quality/Load)',
381
+ 'M': 'M (Medium Quality/Load)',
382
+ 'H': 'H (High Quality/Load)'
383
+ })
384
+
385
+ st.dataframe(failure_by_type[['Type', 'Total Machines', 'Failures', 'Failure Rate']], use_container_width=True)
386
+
387
+ fig = px.bar(
388
+ failure_by_type,
389
+ x='Type_Label',
390
+ y='Failure Rate',
391
+ title="Failure Rate by Machine Type",
392
+ color='Type',
393
+ color_discrete_sequence=['#e74c3c', '#3498db', '#2ecc71'],
394
+ labels={'Type_Label': 'Machine Type'}
395
+ )
396
+ st.plotly_chart(fig, use_container_width=True)
397
+
398
+ elif analysis_type == "Tool Wear Analysis":
399
+ st.subheader("Tool Wear Analysis")
400
+
401
+ col1, col2 = st.columns(2)
402
+
403
+ with col1:
404
+ fig = px.scatter(
405
+ df,
406
+ x='Tool wear [min]',
407
+ y='Machine failure',
408
+ color='Machine failure',
409
+ title="Tool Wear vs Machine Failure",
410
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
411
+ )
412
+ st.plotly_chart(fig, use_container_width=True)
413
+
414
+ with col2:
415
+ tool_wear_by_failure = df.groupby('Machine failure')['Tool wear [min]'].agg(['mean', 'median', 'std'])
416
+ st.dataframe(tool_wear_by_failure, use_container_width=True)
417
+
418
+ # Tool wear distribution by failure status
419
+ fig = px.histogram(
420
+ df,
421
+ x='Tool wear [min]',
422
+ color='Machine failure',
423
+ nbins=50,
424
+ title="Tool Wear Distribution by Failure Status",
425
+ barmode='overlay',
426
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
427
+ )
428
+ st.plotly_chart(fig, use_container_width=True)
429
+
430
+ elif analysis_type == "Temperature Analysis":
431
+ st.subheader("Temperature Analysis")
432
+
433
+ col1, col2 = st.columns(2)
434
+
435
+ with col1:
436
+ fig = px.scatter(
437
+ df,
438
+ x='Air temperature [K]',
439
+ y='Process temperature [K]',
440
+ color='Machine failure',
441
+ title="Temperature Relationship",
442
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
443
+ )
444
+ st.plotly_chart(fig, use_container_width=True)
445
+
446
+ with col2:
447
+ temp_by_failure = df.groupby('Machine failure')[
448
+ ['Air temperature [K]', 'Process temperature [K]', 'Temperature difference [K]']
449
+ ].mean()
450
+ st.dataframe(temp_by_failure, use_container_width=True)
451
+
452
+ elif analysis_type == "Power & Rotational Speed Analysis":
453
+ st.subheader("Power and Rotational Speed Analysis")
454
+
455
+ power_stats = df.groupby('Machine failure')[
456
+ ['Rotational speed [rpm]', 'Torque [Nm]', 'Power [W]']
457
+ ].agg(['mean', 'std', 'min', 'max'])
458
+
459
+ st.dataframe(power_stats, use_container_width=True)
460
+
461
+ col1, col2 = st.columns(2)
462
+
463
+ with col1:
464
+ fig = px.box(
465
+ df,
466
+ x='Machine failure',
467
+ y='Rotational speed [rpm]',
468
+ title="Rotational Speed by Failure Status",
469
+ color='Machine failure',
470
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
471
+ )
472
+ st.plotly_chart(fig, use_container_width=True)
473
+
474
+ with col2:
475
+ fig = px.box(
476
+ df,
477
+ x='Machine failure',
478
+ y='Power [W]',
479
+ title="Power by Failure Status",
480
+ color='Machine failure',
481
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
482
+ )
483
+ st.plotly_chart(fig, use_container_width=True)
484
+
485
+ # Scatter plot: Power vs Rotational Speed
486
+ fig = px.scatter(
487
+ df,
488
+ x='Rotational speed [rpm]',
489
+ y='Power [W]',
490
+ color='Machine failure',
491
+ title="Power vs Rotational Speed",
492
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
493
+ )
494
+ st.plotly_chart(fig, use_container_width=True)
495
+
496
+ elif analysis_type == "Outlier Detection":
497
+ st.subheader("Outlier Detection")
498
+
499
+ feature = st.selectbox(
500
+ "Select Feature for Outlier Detection",
501
+ ['Air temperature [K]', 'Process temperature [K]', 'Rotational speed [rpm]',
502
+ 'Torque [Nm]', 'Tool wear [min]']
503
+ )
504
+
505
+ method = st.radio(
506
+ "Detection method",
507
+ options=["IQR (robust, default)", "Z-score"],
508
+ index=0,
509
+ horizontal=True,
510
+ help="IQR is robust to skew; Z-score highlights extreme standardized values."
511
+ )
512
+
513
+ if method == "IQR (robust, default)":
514
+ iqr_mult = st.slider("IQR multiplier", 0.5, 3.0, 1.5, 0.1,
515
+ help="Lower the multiplier to surface milder outliers.")
516
+ Q1 = df[feature].quantile(0.25)
517
+ Q3 = df[feature].quantile(0.75)
518
+ IQR = Q3 - Q1
519
+ lower_bound = Q1 - iqr_mult * IQR
520
+ upper_bound = Q3 + iqr_mult * IQR
521
+
522
+ outliers = df[(df[feature] < lower_bound) | (df[feature] > upper_bound)]
523
+
524
+ col1, col2 = st.columns(2)
525
+ with col1:
526
+ st.metric("Lower Bound", f"{lower_bound:.2f}")
527
+ st.metric("Upper Bound", f"{upper_bound:.2f}")
528
+ with col2:
529
+ st.metric("Outlier Count", len(outliers))
530
+ st.metric("Outlier Percentage", f"{(len(outliers)/len(df)*100):.2f}%")
531
+
532
+ fig = px.box(df, y=feature, title=f"Box Plot with Outliers - {feature}")
533
+ st.plotly_chart(fig, use_container_width=True)
534
+
535
+ else:
536
+ z_thresh = st.slider("Z-score threshold", 2.0, 5.0, 3.0, 0.1,
537
+ help="Lower threshold to surface more anomalies.")
538
+ mean = df[feature].mean()
539
+ std = df[feature].std()
540
+ if std == 0:
541
+ outliers = df.iloc[0:0]
542
+ zscores = pd.Series([0]*len(df), index=df.index)
543
+ else:
544
+ zscores = (df[feature] - mean) / std
545
+ outliers = df[zscores.abs() > z_thresh]
546
+
547
+ col1, col2 = st.columns(2)
548
+ with col1:
549
+ st.metric("Mean", f"{mean:.2f}")
550
+ st.metric("Std Dev", f"{std:.2f}")
551
+ with col2:
552
+ st.metric("Outlier Count", len(outliers))
553
+ st.metric("Outlier Percentage", f"{(len(outliers)/len(df)*100):.2f}%")
554
+
555
+ fig = px.histogram(df, x=feature, nbins=60, opacity=0.7,
556
+ title=f"{feature} with Z-score Threshold (>|{z_thresh}|)")
557
+ # Overlay threshold lines
558
+ fig.add_vline(x=mean + z_thresh*std, line_dash="dash", line_color="red")
559
+ fig.add_vline(x=mean - z_thresh*std, line_dash="dash", line_color="red")
560
+ st.plotly_chart(fig, use_container_width=True)
561
+
562
+ elif analysis_type == "Pairwise Relationships":
563
+ st.subheader("Pairwise Feature Relationships")
564
+
565
+ feature1 = st.selectbox("Select First Feature",
566
+ ['Tool wear [min]', 'Temperature difference [K]',
567
+ 'Rotational speed [rpm]', 'Torque [Nm]'])
568
+ feature2 = st.selectbox("Select Second Feature",
569
+ ['Tool wear [min]', 'Temperature difference [K]',
570
+ 'Rotational speed [rpm]', 'Torque [Nm]'])
571
+
572
+ if feature1 != feature2:
573
+ fig = px.scatter(
574
+ df,
575
+ x=feature1,
576
+ y=feature2,
577
+ color='Machine failure',
578
+ title=f"{feature1} vs {feature2}",
579
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
580
+ )
581
+ st.plotly_chart(fig, use_container_width=True)
582
+
583
+ correlation = df[feature1].corr(df[feature2])
584
+ st.metric("Correlation", f"{correlation:.4f}")
585
+
586
+ elif analysis_type == "Failure Type Breakdown":
587
+ st.subheader("Detailed Failure Type Analysis")
588
+
589
+ failure_types = {
590
+ 'TWF': 'Tool Wear Failure',
591
+ 'HDF': 'Heat Dissipation Failure',
592
+ 'PWF': 'Power Failure',
593
+ 'OSF': 'Overstrain Failure',
594
+ 'RNF': 'Random Failure'
595
+ }
596
+
597
+ for ft_code, ft_name in failure_types.items():
598
+ with st.expander(f"{ft_name} ({ft_code})"):
599
+ failed_machines = df[df[ft_code] == 1]
600
+ if len(failed_machines) > 0:
601
+ st.metric("Count", len(failed_machines))
602
+ col1, col2, col3 = st.columns(3)
603
+ with col1:
604
+ st.metric("Avg Tool Wear", f"{failed_machines['Tool wear [min]'].mean():.2f} min")
605
+ with col2:
606
+ st.metric("Avg Temp Diff", f"{failed_machines['Temperature difference [K]'].mean():.2f} K")
607
+ with col3:
608
+ st.metric("Avg Rotational Speed", f"{failed_machines['Rotational speed [rpm]'].mean():.0f} rpm")
609
+
610
+ elif analysis_type == "Time to Failure Estimation":
611
+ st.subheader("Time to Failure Estimation")
612
+
613
+ # Analyze tool wear progression for machines that failed
614
+ failed_machines = df[df['Machine failure'] == 1]
615
+
616
+ if len(failed_machines) > 0:
617
+ avg_tool_wear_at_failure = failed_machines['Tool wear [min]'].mean()
618
+ median_tool_wear_at_failure = failed_machines['Tool wear [min]'].median()
619
+
620
+ col1, col2 = st.columns(2)
621
+ with col1:
622
+ st.metric("Average Tool Wear at Failure", f"{avg_tool_wear_at_failure:.2f} minutes")
623
+ with col2:
624
+ st.metric("Median Tool Wear at Failure", f"{median_tool_wear_at_failure:.2f} minutes")
625
+
626
+ # Estimate time remaining for machines not yet failed
627
+ non_failed = df[df['Machine failure'] == 0].copy()
628
+ if len(non_failed) > 0:
629
+ non_failed['Estimated Time to Failure'] = (
630
+ avg_tool_wear_at_failure - non_failed['Tool wear [min]']
631
+ )
632
+ non_failed['Estimated Time to Failure'] = non_failed['Estimated Time to Failure'].clip(lower=0)
633
+
634
+ st.subheader("Time to Failure Estimates (for non-failed machines)")
635
+
636
+ immediate = (non_failed['Estimated Time to Failure'] < 10).sum()
637
+ soon = ((non_failed['Estimated Time to Failure'] >= 10) &
638
+ (non_failed['Estimated Time to Failure'] < 50)).sum()
639
+ remaining = (non_failed['Estimated Time to Failure'] >= 50).sum()
640
+
641
+ col1, col2, col3 = st.columns(3)
642
+ with col1:
643
+ st.metric("Immediate Maintenance (< 10 min)", f"{immediate:,}")
644
+ with col2:
645
+ st.metric("Maintenance Soon (10-50 min)", f"{soon:,}")
646
+ with col3:
647
+ st.metric("Time Remaining (> 50 min)", f"{remaining:,}")
648
+
649
+ # Distribution chart
650
+ fig = px.histogram(
651
+ non_failed,
652
+ x='Estimated Time to Failure',
653
+ nbins=50,
654
+ title="Distribution of Estimated Time to Failure",
655
+ color_discrete_sequence=['#3498db']
656
+ )
657
+ st.plotly_chart(fig, use_container_width=True)
658
+
659
+ elif analysis_type == "Grouped Aggregations":
660
+ st.subheader("Grouped Aggregations by Type and Failure Status")
661
+
662
+ grouped = df.groupby(['Type', 'Machine failure']).agg({
663
+ 'Tool wear [min]': ['mean', 'std', 'max'],
664
+ 'Temperature difference [K]': ['mean', 'std'],
665
+ 'Rotational speed [rpm]': ['mean', 'std'],
666
+ 'Torque [Nm]': ['mean', 'std']
667
+ })
668
+
669
+ st.dataframe(grouped, use_container_width=True)
670
+
671
+ # Visualizations
672
+ st.subheader("Tool Wear by Type and Failure Status")
673
+ fig = px.box(
674
+ df,
675
+ x='Type',
676
+ y='Tool wear [min]',
677
+ color='Machine failure',
678
+ title="Tool Wear Distribution by Type and Failure Status",
679
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
680
+ )
681
+ st.plotly_chart(fig, use_container_width=True)
682
+
683
+ st.subheader("Temperature Difference by Type and Failure Status")
684
+ fig = px.box(
685
+ df,
686
+ x='Type',
687
+ y='Temperature difference [K]',
688
+ color='Machine failure',
689
+ title="Temperature Difference by Type and Failure Status",
690
+ color_discrete_sequence=['#2ecc71', '#e74c3c']
691
+ )
692
+ st.plotly_chart(fig, use_container_width=True)
693
+
694
+ def show_model_predictions(df):
695
+ """Model and predictions page"""
696
+ st.markdown('<h2 class="sub-header">🤖 Machine Learning Model & Predictions</h2>', unsafe_allow_html=True)
697
+
698
+ # Train model section
699
+ if st.session_state.model is None:
700
+ st.info("⚠️ Model not trained yet. Click the button below to train the model.")
701
+ if st.button("Train Model", type="primary"):
702
+ results = train_model()
703
+ st.success("✅ Model trained successfully!")
704
+ st.session_state.model_results = results
705
+
706
+ if st.session_state.model is not None:
707
+ model = st.session_state.model
708
+ preprocessor = st.session_state.preprocessor
709
+
710
+ # Model performance section
711
+ st.subheader("Model Performance")
712
+
713
+ if 'model_results' in st.session_state:
714
+ results = st.session_state.model_results
715
+ col1, col2, col3, col4 = st.columns(4)
716
+ with col1:
717
+ st.metric("Accuracy", f"{results['accuracy']:.4f}")
718
+ with col2:
719
+ st.metric("Precision", f"{results['precision']:.4f}")
720
+ with col3:
721
+ st.metric("Recall", f"{results['recall']:.4f}")
722
+ with col4:
723
+ st.metric("F1-Score", f"{results['f1_score']:.4f}")
724
+
725
+ # Feature importance
726
+ st.subheader("Feature Importance")
727
+ feature_importance = model.get_feature_importance()
728
+ fig = px.bar(
729
+ feature_importance,
730
+ x='importance',
731
+ y='feature',
732
+ orientation='h',
733
+ title="Feature Importance",
734
+ color='importance',
735
+ color_continuous_scale="Viridis"
736
+ )
737
+ st.plotly_chart(fig, use_container_width=True)
738
+
739
+ st.markdown("---")
740
+
741
+ # Runtime prediction section
742
+ st.subheader("🔮 Runtime Prediction - Predict Maintenance Needs")
743
+ st.markdown("Enter machine parameters below to predict if maintenance is needed:")
744
+
745
+ col1, col2 = st.columns(2)
746
+
747
+ with col1:
748
+ machine_type = st.selectbox(
749
+ "Machine Type",
750
+ ['L', 'M', 'H'],
751
+ format_func=lambda x: f"{x} ({'Low Quality/Load' if x == 'L' else 'Medium Quality/Load' if x == 'M' else 'High Quality/Load'})"
752
+ )
753
+ air_temp = st.slider("Air Temperature (K)",
754
+ min_value=295.0, max_value=305.0, value=298.0, step=0.1)
755
+ process_temp = st.slider("Process Temperature (K)",
756
+ min_value=305.0, max_value=315.0, value=309.0, step=0.1)
757
+
758
+ with col2:
759
+ rotational_speed = st.slider("Rotational Speed (rpm)",
760
+ min_value=1000, max_value=3000, value=1500, step=10)
761
+ torque = st.slider("Torque (Nm)",
762
+ min_value=10.0, max_value=80.0, value=40.0, step=0.1)
763
+ tool_wear = st.slider("Tool Wear (minutes)",
764
+ min_value=0, max_value=300, value=50, step=1)
765
+
766
+ if st.button("Predict Maintenance Status", type="primary"):
767
+ # Create input data
768
+ input_data = pd.DataFrame({
769
+ 'Type': [machine_type],
770
+ 'Air temperature [K]': [air_temp],
771
+ 'Process temperature [K]': [process_temp],
772
+ 'Rotational speed [rpm]': [rotational_speed],
773
+ 'Torque [Nm]': [torque],
774
+ 'Tool wear [min]': [tool_wear]
775
+ })
776
+
777
+ # Preprocess
778
+ X_new = preprocessor.preprocess_new_data(input_data)
779
+
780
+ # Predict
781
+ maintenance_pred = model.predict_maintenance(X_new, tool_wear_values=[tool_wear])
782
+
783
+ # Display results
784
+ st.markdown("### Prediction Results")
785
+
786
+ failure_prob = maintenance_pred['Failure_Probability'].iloc[0]
787
+ time_to_failure = maintenance_pred['Time_to_Failure_Minutes'].iloc[0]
788
+ status = maintenance_pred['Maintenance_Status'].iloc[0]
789
+ urgency = maintenance_pred['Maintenance_Urgency'].iloc[0]
790
+
791
+ # Color coding
792
+ if urgency == "CRITICAL":
793
+ st.error(f"🚨 **{status}**")
794
+ elif urgency == "HIGH":
795
+ st.warning(f"⚠️ **{status}**")
796
+ elif urgency == "MEDIUM":
797
+ st.info(f"ℹ️ **{status}**")
798
+ else:
799
+ st.success(f"✅ **{status}**")
800
+
801
+ col1, col2, col3 = st.columns(3)
802
+ with col1:
803
+ st.metric("Failure Probability", f"{failure_prob:.2%}")
804
+ with col2:
805
+ st.metric("Estimated Time to Maintenance", f"{time_to_failure:.1f} minutes")
806
+ if time_to_failure > 0:
807
+ st.caption(f"Based on historical data: avg failure at 120 min tool wear")
808
+ else:
809
+ st.caption("Tool wear exceeded average failure threshold (120 min)")
810
+ with col3:
811
+ st.metric("Maintenance Urgency", urgency)
812
+
813
+ # Detailed information
814
+ with st.expander("View Detailed Information"):
815
+ st.markdown(f"""
816
+ **Machine Parameters:**
817
+ - Type: {machine_type} ({'Low Quality/Load' if machine_type == 'L' else 'Medium Quality/Load' if machine_type == 'M' else 'High Quality/Load'})
818
+ - Air Temperature: {air_temp} K
819
+ - Process Temperature: {process_temp} K
820
+ - Rotational Speed: {rotational_speed} rpm
821
+ - Torque: {torque} Nm
822
+ - Tool Wear: {tool_wear} minutes
823
+
824
+ **Prediction Details:**
825
+ - Failure Predicted: {'Yes' if maintenance_pred['Failure_Predicted'].iloc[0] == 1 else 'No'}
826
+ - Failure Probability: {failure_prob:.2%}
827
+ - Estimated Time to Maintenance: {time_to_failure:.1f} minutes
828
+ *Based on historical analysis: Machines in this dataset typically require maintenance when tool wear reaches approximately 120 minutes.
829
+ This estimate projects when your machine will reach that threshold based on current tool wear level.*
830
+ - Maintenance Status: {status}
831
+ - Urgency Level: {urgency}
832
+
833
+ **Recommendation:**
834
+ {get_maintenance_recommendation(urgency, time_to_failure, failure_prob)}
835
+ """)
836
+
837
+ st.markdown("---")
838
+
839
+ # Batch prediction section
840
+ st.subheader("Batch Prediction")
841
+ st.markdown("Upload a CSV file with machine data for batch predictions:")
842
+
843
+ uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
844
+
845
+ if uploaded_file is not None:
846
+ try:
847
+ batch_data = pd.read_csv(uploaded_file)
848
+ st.dataframe(batch_data.head(), use_container_width=True)
849
+
850
+ if st.button("Predict for Batch", type="primary"):
851
+ # Check required columns
852
+ required_cols = ['Type', 'Air temperature [K]', 'Process temperature [K]',
853
+ 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]']
854
+
855
+ if all(col in batch_data.columns for col in required_cols):
856
+ X_batch = preprocessor.preprocess_new_data(batch_data)
857
+ tool_wear_batch = batch_data['Tool wear [min]'].values
858
+ batch_predictions = model.predict_maintenance(X_batch, tool_wear_batch)
859
+
860
+ # Combine with original data
861
+ results_df = pd.concat([batch_data, batch_predictions], axis=1)
862
+
863
+ st.success("✅ Batch prediction complete!")
864
+ st.dataframe(results_df, use_container_width=True)
865
+
866
+ # Summary statistics
867
+ st.subheader("Batch Prediction Summary")
868
+ col1, col2, col3, col4 = st.columns(4)
869
+ with col1:
870
+ st.metric("Total Machines", len(batch_data))
871
+ with col2:
872
+ st.metric("Critical Maintenance",
873
+ (batch_predictions['Maintenance_Urgency'] == 'CRITICAL').sum())
874
+ with col3:
875
+ st.metric("High Priority",
876
+ (batch_predictions['Maintenance_Urgency'] == 'HIGH').sum())
877
+ with col4:
878
+ st.metric("Average Time to Failure",
879
+ f"{batch_predictions['Time_to_Failure_Minutes'].mean():.1f} min")
880
+ else:
881
+ st.error(f"CSV must contain these columns: {', '.join(required_cols)}")
882
+ except Exception as e:
883
+ st.error(f"Error processing file: {str(e)}")
884
+
885
+ def get_maintenance_recommendation(urgency, time_to_failure, failure_prob):
886
+ """Get maintenance recommendation based on prediction"""
887
+ if urgency == "CRITICAL":
888
+ return "**IMMEDIATE ACTION REQUIRED**: Stop the machine immediately and perform maintenance. Failure is imminent."
889
+ elif urgency == "HIGH":
890
+ if time_to_failure < 60:
891
+ return f"**URGENT**: Schedule maintenance within {int(time_to_failure/60)} hour(s) or immediately if possible. The machine shows high risk of failure."
892
+ else:
893
+ return f"**Schedule maintenance within {int(time_to_failure/60)} hours**. The machine shows high risk of failure."
894
+ elif urgency == "MEDIUM":
895
+ if time_to_failure < 20:
896
+ return f"**Schedule maintenance within the next 20-30 minutes**. Monitor the machine very closely. Estimated time to maintenance: {time_to_failure:.0f} minutes."
897
+ elif time_to_failure < 60:
898
+ return f"**Schedule maintenance within the next hour**. Monitor the machine closely. Estimated time to maintenance: {time_to_failure:.0f} minutes."
899
+ elif time_to_failure < 120:
900
+ return f"**Plan maintenance within the next 2 hours**. Monitor the machine closely. Estimated time to maintenance: {int(time_to_failure/60)} hours."
901
+ else:
902
+ return f"**Plan maintenance within the next few days**. Monitor the machine regularly. Estimated time to maintenance: {int(time_to_failure/60)} hours."
903
+ else:
904
+ if time_to_failure < 120:
905
+ return f"**Monitor regularly**. No immediate action needed, but plan maintenance soon. Estimated time to maintenance: {int(time_to_failure/60)} hours."
906
+ else:
907
+ return f"**No immediate action needed**. Continue regular monitoring. Estimated time to maintenance: {int(time_to_failure/60)} hours."
908
+
909
+ def show_conclusion():
910
+ """Conclusion page"""
911
+ st.markdown('<h2 class="sub-header">📝 Conclusion & Key Takeaways</h2>', unsafe_allow_html=True)
912
+
913
+ st.markdown("""
914
+ ### Project Summary
915
+
916
+ This predictive maintenance project successfully analyzed the AI4I 2020 dataset and built
917
+ a machine learning model to predict machine failures and estimate maintenance needs.
918
+
919
+ ### Key Findings
920
+
921
+ 1. **Dataset Characteristics**:
922
+ - The dataset contains 10,000 machine records with 14 features
923
+ - Machine failure rate is approximately 3.39% (imbalanced dataset)
924
+ - Five different failure types were identified: TWF, HDF, PWF, OSF, and RNF
925
+
926
+ 2. **Important Features**:
927
+ - Tool wear is a critical indicator of machine health
928
+ - Temperature difference between process and air temperature correlates with failures
929
+ - Machine type (L = Low Quality/Load, M = Medium Quality/Load, H = High Quality/Load) affects failure rates differently
930
+ - Rotational speed and torque relationships are important predictors
931
+
932
+ 3. **Model Performance**:
933
+ - Random Forest Classifier achieved good performance on the imbalanced dataset
934
+ - The model can effectively predict machine failures
935
+ - Feature importance analysis revealed tool wear and temperature as key predictors
936
+
937
+ 4. **Maintenance Insights**:
938
+ - Machines with tool wear > 100 minutes are at higher risk
939
+ - Temperature differences > 10K indicate potential heat dissipation issues
940
+ - Early detection can prevent costly downtime
941
+
942
+ ### Applications
943
+
944
+ This system can be used in real-world industrial settings to:
945
+ - **Prevent unexpected failures** by predicting maintenance needs
946
+ - **Optimize maintenance schedules** based on actual machine conditions
947
+ - **Reduce downtime** through proactive maintenance
948
+ - **Save costs** by avoiding catastrophic failures
949
+
950
+ ### Future Improvements
951
+
952
+ 1. **Model Enhancement**:
953
+ - Try ensemble methods (XGBoost, LightGBM)
954
+ - Implement time-series analysis for sequential data
955
+ - Add anomaly detection algorithms
956
+
957
+ 2. **Feature Engineering**:
958
+ - Create more domain-specific features
959
+ - Include historical maintenance records
960
+ - Add environmental factors
961
+
962
+ 3. **System Integration**:
963
+ - Real-time data streaming
964
+ - Integration with IoT sensors
965
+ - Automated alert system
966
+
967
+ ### Technical Stack
968
+
969
+ - **Data Analysis**: Pandas, NumPy
970
+ - **Visualization**: Matplotlib, Seaborn, Plotly
971
+ - **Machine Learning**: Scikit-learn (Random Forest)
972
+ - **Web Application**: Streamlit
973
+ - **Preprocessing**: StandardScaler, LabelEncoder
974
+
975
+ ### Acknowledgments
976
+
977
+ Dataset: AI4I 2020 Predictive Maintenance Dataset
978
+
979
+ ---
980
+
981
+ **Project completed for Introduction to Data Science Course (IDS F24)**
982
+ """)
983
+
984
+ st.markdown("---")
985
+ st.markdown("### Thank you for using the Predictive Maintenance System! 🔧")
986
+
987
+ if __name__ == "__main__":
988
+ main()
989
+
model.py ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Machine Learning Model for Predictive Maintenance
3
+ Uses Random Forest Classifier for failure prediction
4
+ """
5
+
6
+ import pandas as pd
7
+ import numpy as np
8
+ import pickle
9
+ from sklearn.ensemble import RandomForestClassifier
10
+ from sklearn.metrics import (
11
+ accuracy_score, precision_score, recall_score, f1_score,
12
+ confusion_matrix, classification_report, roc_auc_score
13
+ )
14
+ import warnings
15
+ warnings.filterwarnings('ignore')
16
+
17
+ class PredictiveMaintenanceModel:
18
+ def __init__(self):
19
+ """Initialize the model"""
20
+ self.model = RandomForestClassifier(
21
+ n_estimators=100,
22
+ max_depth=10,
23
+ min_samples_split=5,
24
+ min_samples_leaf=2,
25
+ random_state=42,
26
+ class_weight='balanced' # Handle class imbalance
27
+ )
28
+ self.is_trained = False
29
+ self.feature_importance_ = None
30
+
31
+ def train(self, X_train, y_train):
32
+ """Train the model"""
33
+ print("Training Random Forest Classifier...")
34
+ self.model.fit(X_train, y_train)
35
+ self.is_trained = True
36
+
37
+ # Get feature importance
38
+ self.feature_importance_ = pd.DataFrame({
39
+ 'feature': X_train.columns,
40
+ 'importance': self.model.feature_importances_
41
+ }).sort_values('importance', ascending=False)
42
+
43
+ print("Model training complete!")
44
+ return self
45
+
46
+ def predict(self, X):
47
+ """Make predictions"""
48
+ if not self.is_trained:
49
+ raise ValueError("Model must be trained first!")
50
+ return self.model.predict(X)
51
+
52
+ def predict_proba(self, X):
53
+ """Get prediction probabilities"""
54
+ if not self.is_trained:
55
+ raise ValueError("Model must be trained first!")
56
+ return self.model.predict_proba(X)
57
+
58
+ def evaluate(self, X_test, y_test):
59
+ """Evaluate the model"""
60
+ if not self.is_trained:
61
+ raise ValueError("Model must be trained first!")
62
+
63
+ # Predictions
64
+ y_pred = self.predict(X_test)
65
+ y_pred_proba = self.predict_proba(X_test)[:, 1]
66
+
67
+ # Metrics
68
+ accuracy = accuracy_score(y_test, y_pred)
69
+ precision = precision_score(y_test, y_pred, zero_division=0)
70
+ recall = recall_score(y_test, y_pred, zero_division=0)
71
+ f1 = f1_score(y_test, y_pred, zero_division=0)
72
+ roc_auc = roc_auc_score(y_test, y_pred_proba)
73
+
74
+ # Confusion matrix
75
+ cm = confusion_matrix(y_test, y_pred)
76
+
77
+ # Classification report
78
+ report = classification_report(y_test, y_pred, zero_division=0)
79
+
80
+ results = {
81
+ 'accuracy': accuracy,
82
+ 'precision': precision,
83
+ 'recall': recall,
84
+ 'f1_score': f1,
85
+ 'roc_auc': roc_auc,
86
+ 'confusion_matrix': cm,
87
+ 'classification_report': report,
88
+ 'y_pred': y_pred,
89
+ 'y_pred_proba': y_pred_proba
90
+ }
91
+
92
+ print("="*60)
93
+ print("MODEL EVALUATION RESULTS")
94
+ print("="*60)
95
+ print(f"Accuracy: {accuracy:.4f}")
96
+ print(f"Precision: {precision:.4f}")
97
+ print(f"Recall: {recall:.4f}")
98
+ print(f"F1-Score: {f1:.4f}")
99
+ print(f"ROC-AUC: {roc_auc:.4f}")
100
+ print("\nConfusion Matrix:")
101
+ print(cm)
102
+ print("\nClassification Report:")
103
+ print(report)
104
+
105
+ return results
106
+
107
+ def predict_maintenance(self, X, tool_wear_values=None):
108
+ """
109
+ Predict maintenance needs and time to failure
110
+
111
+ Args:
112
+ X: Feature matrix
113
+ tool_wear_values: Array of tool wear values (optional)
114
+
115
+ Returns:
116
+ DataFrame with predictions and maintenance recommendations
117
+ """
118
+ if not self.is_trained:
119
+ raise ValueError("Model must be trained first!")
120
+
121
+ # Get failure predictions
122
+ failure_pred = self.predict(X)
123
+ failure_proba = self.predict_proba(X)[:, 1]
124
+
125
+ # Estimate time to failure based on tool wear
126
+ # Average tool wear at failure is around 100-150 minutes based on EDA
127
+ avg_tool_wear_at_failure = 120 # minutes
128
+
129
+ if tool_wear_values is None:
130
+ # Try to get tool wear from features if available
131
+ if 'Tool wear [min]' in X.columns:
132
+ tool_wear_values = X['Tool wear [min]'].values
133
+ else:
134
+ tool_wear_values = np.zeros(len(X))
135
+ else:
136
+ # Convert to numpy array if it's a list or scalar
137
+ tool_wear_values = np.array(tool_wear_values)
138
+ # If it's a scalar, make it an array
139
+ if tool_wear_values.ndim == 0:
140
+ tool_wear_values = np.array([tool_wear_values])
141
+
142
+ # Ensure tool_wear_values is 1D array with same length as predictions
143
+ if len(tool_wear_values) != len(failure_pred):
144
+ # If single value provided, repeat it for all predictions
145
+ if len(tool_wear_values) == 1:
146
+ tool_wear_values = np.repeat(tool_wear_values, len(failure_pred))
147
+ else:
148
+ raise ValueError(f"tool_wear_values length ({len(tool_wear_values)}) doesn't match predictions length ({len(failure_pred)})")
149
+
150
+ # Calculate estimated time to failure
151
+ time_to_failure = np.maximum(0, avg_tool_wear_at_failure - tool_wear_values)
152
+
153
+ # Determine maintenance urgency
154
+ maintenance_status = []
155
+ maintenance_urgency = []
156
+
157
+ for i in range(len(failure_pred)):
158
+ # Check if tool wear has exceeded the average failure threshold
159
+ tool_wear_exceeded = tool_wear_values[i] >= avg_tool_wear_at_failure
160
+ tool_wear_severely_exceeded = tool_wear_values[i] >= (avg_tool_wear_at_failure * 1.5) # 150% of threshold (180 min)
161
+
162
+ # Combined risk assessment: consider both ML prediction AND tool wear
163
+ if failure_pred[i] == 1:
164
+ # Model predicts failure - highest priority
165
+ maintenance_status.append("IMMEDIATE MAINTENANCE REQUIRED")
166
+ maintenance_urgency.append("CRITICAL")
167
+ elif tool_wear_severely_exceeded and failure_proba[i] > 0.2:
168
+ # Severely exceeded tool wear AND significant failure probability
169
+ maintenance_status.append("IMMEDIATE MAINTENANCE REQUIRED - Tool wear severely exceeded threshold")
170
+ maintenance_urgency.append("CRITICAL")
171
+ elif tool_wear_severely_exceeded:
172
+ # Severely exceeded tool wear but low failure probability - still HIGH priority
173
+ maintenance_status.append("URGENT MAINTENANCE NEEDED - Tool wear severely exceeded (monitor closely)")
174
+ maintenance_urgency.append("HIGH")
175
+ elif tool_wear_exceeded and failure_proba[i] > 0.3:
176
+ # Tool wear exceeded AND moderate failure probability
177
+ maintenance_status.append("IMMEDIATE MAINTENANCE REQUIRED - Tool wear exceeded threshold")
178
+ maintenance_urgency.append("CRITICAL")
179
+ elif tool_wear_exceeded:
180
+ # Tool wear exceeded but low failure probability - HIGH priority
181
+ maintenance_status.append("URGENT MAINTENANCE NEEDED - Tool wear exceeded threshold")
182
+ maintenance_urgency.append("HIGH")
183
+ elif failure_proba[i] > 0.7:
184
+ # High failure probability regardless of tool wear
185
+ maintenance_status.append("Maintenance needed soon")
186
+ maintenance_urgency.append("HIGH")
187
+ elif failure_proba[i] > 0.4:
188
+ # Moderate failure probability
189
+ maintenance_status.append("Schedule maintenance")
190
+ maintenance_urgency.append("MEDIUM")
191
+ elif time_to_failure[i] < 20:
192
+ # Less than 20 minutes remaining
193
+ maintenance_status.append("Monitor closely - Maintenance needed within 20 minutes")
194
+ maintenance_urgency.append("MEDIUM")
195
+ elif time_to_failure[i] < 60:
196
+ # Less than 1 hour remaining
197
+ maintenance_status.append("Monitor closely - Maintenance needed within 1 hour")
198
+ maintenance_urgency.append("MEDIUM")
199
+ else:
200
+ # Low risk
201
+ maintenance_status.append("No immediate maintenance needed")
202
+ maintenance_urgency.append("LOW")
203
+
204
+ results_df = pd.DataFrame({
205
+ 'Failure_Predicted': failure_pred,
206
+ 'Failure_Probability': failure_proba,
207
+ 'Time_to_Failure_Minutes': time_to_failure,
208
+ 'Maintenance_Status': maintenance_status,
209
+ 'Maintenance_Urgency': maintenance_urgency
210
+ })
211
+
212
+ return results_df
213
+
214
+
215
+ def get_feature_importance(self):
216
+ """Get feature importance"""
217
+ if self.feature_importance_ is None:
218
+ raise ValueError("Model must be trained first!")
219
+ return self.feature_importance_
220
+
221
+ def save_model(self, filepath='predictive_maintenance_model.pkl'):
222
+ """Save the trained model"""
223
+ if not self.is_trained:
224
+ raise ValueError("Model must be trained first!")
225
+
226
+ with open(filepath, 'wb') as f:
227
+ pickle.dump(self.model, f)
228
+ print(f"Model saved to {filepath}")
229
+
230
+ def load_model(self, filepath='predictive_maintenance_model.pkl'):
231
+ """Load a trained model"""
232
+ with open(filepath, 'rb') as f:
233
+ self.model = pickle.load(f)
234
+ self.is_trained = True
235
+ print(f"Model loaded from {filepath}")
preprocessing.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data Preprocessing Module for AI4I 2020 Predictive Maintenance Dataset
3
+ Handles missing values, encoding, scaling, and train-test splitting
4
+ """
5
+
6
+ import pandas as pd
7
+ import numpy as np
8
+ from sklearn.model_selection import train_test_split
9
+ from sklearn.preprocessing import StandardScaler, LabelEncoder
10
+ from sklearn.impute import SimpleImputer
11
+
12
+ class DataPreprocessor:
13
+ def __init__(self, data_path='ai4i2020.csv'):
14
+ """Initialize the preprocessor"""
15
+ self.df = pd.read_csv(data_path)
16
+ self.scaler = StandardScaler()
17
+ self.label_encoder = LabelEncoder()
18
+ self.feature_columns = None
19
+ self.is_fitted = False
20
+
21
+ def create_features(self):
22
+ """Create additional features"""
23
+ # Temperature difference
24
+ self.df['Temperature difference [K]'] = (
25
+ self.df['Process temperature [K]'] -
26
+ self.df['Air temperature [K]']
27
+ )
28
+
29
+ # Power calculation
30
+ self.df['Power [W]'] = (
31
+ self.df['Rotational speed [rpm]'] *
32
+ self.df['Torque [Nm]'] / 9.5488
33
+ )
34
+
35
+ # Tool wear rate (if we had time data, but we'll use a proxy)
36
+ # We can create bins for tool wear
37
+ self.df['Tool wear category'] = pd.cut(
38
+ self.df['Tool wear [min]'],
39
+ bins=[0, 50, 100, 150, 200, 300],
40
+ labels=['Very Low', 'Low', 'Medium', 'High', 'Very High']
41
+ )
42
+
43
+ def handle_missing_values(self):
44
+ """Handle missing values"""
45
+ # Check for missing values
46
+ missing = self.df.isnull().sum()
47
+
48
+ if missing.sum() > 0:
49
+ # For numerical columns, use mean imputation
50
+ numerical_cols = self.df.select_dtypes(include=[np.number]).columns
51
+ imputer = SimpleImputer(strategy='mean')
52
+ self.df[numerical_cols] = imputer.fit_transform(self.df[numerical_cols])
53
+
54
+ # For categorical columns, use mode imputation
55
+ categorical_cols = self.df.select_dtypes(include=['object']).columns
56
+ for col in categorical_cols:
57
+ if self.df[col].isnull().sum() > 0:
58
+ mode_value = self.df[col].mode()[0]
59
+ self.df[col].fillna(mode_value, inplace=True)
60
+ else:
61
+ print("No missing values found in the dataset.")
62
+
63
+ def encode_categorical_variables(self):
64
+ """Encode categorical variables"""
65
+ # Encode Type column
66
+ self.df['Type_encoded'] = self.label_encoder.fit_transform(self.df['Type'])
67
+
68
+ # One-hot encode Type (alternative approach)
69
+ type_dummies = pd.get_dummies(self.df['Type'], prefix='Type')
70
+ self.df = pd.concat([self.df, type_dummies], axis=1)
71
+
72
+ # Encode Tool wear category if it exists
73
+ if 'Tool wear category' in self.df.columns:
74
+ self.df['Tool_wear_category_encoded'] = LabelEncoder().fit_transform(
75
+ self.df['Tool wear category'].astype(str)
76
+ )
77
+
78
+ def select_features(self):
79
+ """Select features for modeling"""
80
+ # Drop non-feature columns
81
+ columns_to_drop = [
82
+ 'UDI', 'Product ID', 'Type', 'Tool wear category'
83
+ ]
84
+
85
+ # Keep only relevant columns
86
+ feature_columns = [
87
+ 'Air temperature [K]',
88
+ 'Process temperature [K]',
89
+ 'Rotational speed [rpm]',
90
+ 'Torque [Nm]',
91
+ 'Tool wear [min]',
92
+ 'Temperature difference [K]',
93
+ 'Power [W]',
94
+ 'Type_encoded',
95
+ 'Type_H',
96
+ 'Type_L',
97
+ 'Type_M'
98
+ ]
99
+
100
+ # Remove columns that don't exist
101
+ feature_columns = [col for col in feature_columns if col in self.df.columns]
102
+
103
+ self.feature_columns = feature_columns
104
+ return feature_columns
105
+
106
+ def scale_features(self, X_train, X_test):
107
+ """Scale numerical features"""
108
+ # Scale training data
109
+ X_train_scaled = self.scaler.fit_transform(X_train)
110
+ X_test_scaled = self.scaler.transform(X_test)
111
+
112
+ # Convert back to DataFrame
113
+ X_train_scaled = pd.DataFrame(
114
+ X_train_scaled,
115
+ columns=X_train.columns,
116
+ index=X_train.index
117
+ )
118
+ X_test_scaled = pd.DataFrame(
119
+ X_test_scaled,
120
+ columns=X_test.columns,
121
+ index=X_test.index
122
+ )
123
+
124
+ return X_train_scaled, X_test_scaled
125
+
126
+ def prepare_data(self, target='Machine failure', test_size=0.2, random_state=42):
127
+ """Complete preprocessing pipeline"""
128
+ print("Starting data preprocessing...")
129
+
130
+ # Step 1: Create features
131
+ print("1. Creating additional features...")
132
+ self.create_features()
133
+
134
+ # Step 2: Handle missing values
135
+ print("2. Handling missing values...")
136
+ self.handle_missing_values()
137
+
138
+ # Step 3: Encode categorical variables
139
+ print("3. Encoding categorical variables...")
140
+ self.encode_categorical_variables()
141
+
142
+ # Step 4: Select features
143
+ print("4. Selecting features...")
144
+ feature_columns = self.select_features()
145
+
146
+ # Step 5: Prepare X and y
147
+ X = self.df[feature_columns]
148
+ y = self.df[target]
149
+
150
+ # Step 6: Split data
151
+ print("5. Splitting data into train and test sets...")
152
+ X_train, X_test, y_train, y_test = train_test_split(
153
+ X, y, test_size=test_size, random_state=random_state, stratify=y
154
+ )
155
+
156
+ # Step 7: Scale features
157
+ print("6. Scaling features...")
158
+ X_train_scaled, X_test_scaled = self.scale_features(X_train, X_test)
159
+
160
+ self.is_fitted = True
161
+
162
+ print("Preprocessing complete!")
163
+ print(f"Training set shape: {X_train_scaled.shape}")
164
+ print(f"Test set shape: {X_test_scaled.shape}")
165
+ print(f"Features: {feature_columns}")
166
+
167
+ return X_train_scaled, X_test_scaled, y_train, y_test, feature_columns
168
+
169
+ def preprocess_new_data(self, new_data):
170
+ """Preprocess new data for prediction (using fitted scaler and encoders)"""
171
+ if not self.is_fitted:
172
+ raise ValueError("Preprocessor must be fitted first using prepare_data()")
173
+
174
+ # Create a copy
175
+ df_new = new_data.copy()
176
+
177
+ # Create features
178
+ df_new['Temperature difference [K]'] = (
179
+ df_new['Process temperature [K]'] -
180
+ df_new['Air temperature [K]']
181
+ )
182
+ df_new['Power [W]'] = (
183
+ df_new['Rotational speed [rpm]'] *
184
+ df_new['Torque [Nm]'] / 9.5488
185
+ )
186
+
187
+ # Encode Type
188
+ df_new['Type_encoded'] = self.label_encoder.transform(df_new['Type'])
189
+ type_dummies = pd.get_dummies(df_new['Type'], prefix='Type')
190
+
191
+ # Ensure all Type columns exist
192
+ for col in ['Type_H', 'Type_L', 'Type_M']:
193
+ if col not in type_dummies.columns:
194
+ type_dummies[col] = 0
195
+
196
+ df_new = pd.concat([df_new, type_dummies[['Type_H', 'Type_L', 'Type_M']]], axis=1)
197
+
198
+ # Select features
199
+ X_new = df_new[self.feature_columns]
200
+
201
+ # Scale
202
+ X_new_scaled = self.scaler.transform(X_new)
203
+ X_new_scaled = pd.DataFrame(X_new_scaled, columns=self.feature_columns)
204
+
205
+ return X_new_scaled
requirements.txt CHANGED
@@ -1,3 +1,5 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
1
+ pandas==2.1.4
2
+ numpy==1.26.2
3
+ scikit-learn==1.4.0
4
+ streamlit==1.29.0
5
+ plotly==5.18.0
train_model.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Script to train and save the predictive maintenance model
3
+ Run this script before using the Streamlit app for the first time
4
+ """
5
+
6
+ from preprocessing import DataPreprocessor
7
+ from model import PredictiveMaintenanceModel
8
+ import pickle
9
+
10
+ def main():
11
+ print("="*60)
12
+ print("Training Predictive Maintenance Model")
13
+ print("="*60)
14
+
15
+ # Initialize preprocessor
16
+ print("\n1. Loading and preprocessing data...")
17
+ preprocessor = DataPreprocessor('ai4i2020.csv')
18
+
19
+ # Prepare data
20
+ X_train, X_test, y_train, y_test, feature_columns = preprocessor.prepare_data()
21
+
22
+ # Train model
23
+ print("\n2. Training model...")
24
+ model = PredictiveMaintenanceModel()
25
+ model.train(X_train, y_train)
26
+
27
+ # Evaluate model
28
+ print("\n3. Evaluating model...")
29
+ results = model.evaluate(X_test, y_test)
30
+
31
+ # Save model and preprocessor
32
+ print("\n4. Saving model and preprocessor...")
33
+ model.save_model('predictive_maintenance_model.pkl')
34
+
35
+ with open('preprocessor.pkl', 'wb') as f:
36
+ pickle.dump(preprocessor, f)
37
+
38
+ print("\n5. Saving feature columns...")
39
+ with open('feature_columns.pkl', 'wb') as f:
40
+ pickle.dump(feature_columns, f)
41
+
42
+ print("\n" + "="*60)
43
+ print("Model training complete!")
44
+ print("="*60)
45
+ print("\nModel files saved:")
46
+ print(" - predictive_maintenance_model.pkl")
47
+ print(" - preprocessor.pkl")
48
+ print(" - feature_columns.pkl")
49
+ print("\nYou can now run the Streamlit app: streamlit run app.py")
50
+
51
+ if __name__ == "__main__":
52
+ main()