| """
|
| Data Preprocessing Module for AI4I 2020 Predictive Maintenance Dataset
|
| Handles missing values, encoding, scaling, and train-test splitting
|
| """
|
|
|
| import pandas as pd
|
| import numpy as np
|
| from sklearn.model_selection import train_test_split
|
| from sklearn.preprocessing import StandardScaler, LabelEncoder
|
| from sklearn.impute import SimpleImputer
|
|
|
| class DataPreprocessor:
|
| def __init__(self, data_path='ai4i2020.csv'):
|
| """Initialize the preprocessor"""
|
| self.df = pd.read_csv(data_path)
|
| self.scaler = StandardScaler()
|
| self.label_encoder = LabelEncoder()
|
| self.feature_columns = None
|
| self.is_fitted = False
|
|
|
| def create_features(self):
|
| """Create additional features"""
|
|
|
| self.df['Temperature difference [K]'] = (
|
| self.df['Process temperature [K]'] -
|
| self.df['Air temperature [K]']
|
| )
|
|
|
|
|
| self.df['Power [W]'] = (
|
| self.df['Rotational speed [rpm]'] *
|
| self.df['Torque [Nm]'] / 9.5488
|
| )
|
|
|
|
|
|
|
| self.df['Tool wear category'] = pd.cut(
|
| self.df['Tool wear [min]'],
|
| bins=[0, 50, 100, 150, 200, 300],
|
| labels=['Very Low', 'Low', 'Medium', 'High', 'Very High']
|
| )
|
|
|
| def handle_missing_values(self):
|
| """Handle missing values"""
|
|
|
| missing = self.df.isnull().sum()
|
|
|
| if missing.sum() > 0:
|
|
|
| numerical_cols = self.df.select_dtypes(include=[np.number]).columns
|
| imputer = SimpleImputer(strategy='mean')
|
| self.df[numerical_cols] = imputer.fit_transform(self.df[numerical_cols])
|
|
|
|
|
| categorical_cols = self.df.select_dtypes(include=['object']).columns
|
| for col in categorical_cols:
|
| if self.df[col].isnull().sum() > 0:
|
| mode_value = self.df[col].mode()[0]
|
| self.df[col].fillna(mode_value, inplace=True)
|
| else:
|
| print("No missing values found in the dataset.")
|
|
|
| def encode_categorical_variables(self):
|
| """Encode categorical variables"""
|
|
|
| self.df['Type_encoded'] = self.label_encoder.fit_transform(self.df['Type'])
|
|
|
|
|
| type_dummies = pd.get_dummies(self.df['Type'], prefix='Type')
|
| self.df = pd.concat([self.df, type_dummies], axis=1)
|
|
|
|
|
| if 'Tool wear category' in self.df.columns:
|
| self.df['Tool_wear_category_encoded'] = LabelEncoder().fit_transform(
|
| self.df['Tool wear category'].astype(str)
|
| )
|
|
|
| def select_features(self):
|
| """Select features for modeling"""
|
|
|
| columns_to_drop = [
|
| 'UDI', 'Product ID', 'Type', 'Tool wear category'
|
| ]
|
|
|
|
|
| feature_columns = [
|
| 'Air temperature [K]',
|
| 'Process temperature [K]',
|
| 'Rotational speed [rpm]',
|
| 'Torque [Nm]',
|
| 'Tool wear [min]',
|
| 'Temperature difference [K]',
|
| 'Power [W]',
|
| 'Type_encoded',
|
| 'Type_H',
|
| 'Type_L',
|
| 'Type_M'
|
| ]
|
|
|
|
|
| feature_columns = [col for col in feature_columns if col in self.df.columns]
|
|
|
| self.feature_columns = feature_columns
|
| return feature_columns
|
|
|
| def scale_features(self, X_train, X_test):
|
| """Scale numerical features"""
|
|
|
| X_train_scaled = self.scaler.fit_transform(X_train)
|
| X_test_scaled = self.scaler.transform(X_test)
|
|
|
|
|
| X_train_scaled = pd.DataFrame(
|
| X_train_scaled,
|
| columns=X_train.columns,
|
| index=X_train.index
|
| )
|
| X_test_scaled = pd.DataFrame(
|
| X_test_scaled,
|
| columns=X_test.columns,
|
| index=X_test.index
|
| )
|
|
|
| return X_train_scaled, X_test_scaled
|
|
|
| def prepare_data(self, target='Machine failure', test_size=0.2, random_state=42):
|
| """Complete preprocessing pipeline"""
|
| print("Starting data preprocessing...")
|
|
|
|
|
| print("1. Creating additional features...")
|
| self.create_features()
|
|
|
|
|
| print("2. Handling missing values...")
|
| self.handle_missing_values()
|
|
|
|
|
| print("3. Encoding categorical variables...")
|
| self.encode_categorical_variables()
|
|
|
|
|
| print("4. Selecting features...")
|
| feature_columns = self.select_features()
|
|
|
|
|
| X = self.df[feature_columns]
|
| y = self.df[target]
|
|
|
|
|
| print("5. Splitting data into train and test sets...")
|
| X_train, X_test, y_train, y_test = train_test_split(
|
| X, y, test_size=test_size, random_state=random_state, stratify=y
|
| )
|
|
|
|
|
| print("6. Scaling features...")
|
| X_train_scaled, X_test_scaled = self.scale_features(X_train, X_test)
|
|
|
| self.is_fitted = True
|
|
|
| print("Preprocessing complete!")
|
| print(f"Training set shape: {X_train_scaled.shape}")
|
| print(f"Test set shape: {X_test_scaled.shape}")
|
| print(f"Features: {feature_columns}")
|
|
|
| return X_train_scaled, X_test_scaled, y_train, y_test, feature_columns
|
|
|
| def preprocess_new_data(self, new_data):
|
| """Preprocess new data for prediction (using fitted scaler and encoders)"""
|
| if not self.is_fitted:
|
| raise ValueError("Preprocessor must be fitted first using prepare_data()")
|
|
|
|
|
| df_new = new_data.copy()
|
|
|
|
|
| df_new['Temperature difference [K]'] = (
|
| df_new['Process temperature [K]'] -
|
| df_new['Air temperature [K]']
|
| )
|
| df_new['Power [W]'] = (
|
| df_new['Rotational speed [rpm]'] *
|
| df_new['Torque [Nm]'] / 9.5488
|
| )
|
|
|
|
|
| df_new['Type_encoded'] = self.label_encoder.transform(df_new['Type'])
|
| type_dummies = pd.get_dummies(df_new['Type'], prefix='Type')
|
|
|
|
|
| for col in ['Type_H', 'Type_L', 'Type_M']:
|
| if col not in type_dummies.columns:
|
| type_dummies[col] = 0
|
|
|
| df_new = pd.concat([df_new, type_dummies[['Type_H', 'Type_L', 'Type_M']]], axis=1)
|
|
|
|
|
| X_new = df_new[self.feature_columns]
|
|
|
|
|
| X_new_scaled = self.scaler.transform(X_new)
|
| X_new_scaled = pd.DataFrame(X_new_scaled, columns=self.feature_columns)
|
|
|
| return X_new_scaled |