AtthalaricNero
feat(anomaly): add functions for preparing sensor data and calculating risk scores
0da1da8 | from datetime import datetime | |
| import numpy as np | |
| import pandas as pd | |
| def generate_cyclical_features(timestamp): | |
| if isinstance(timestamp, str): | |
| timestamp = datetime.fromisoformat(timestamp.replace("Z", "+00:00")) | |
| hour = timestamp.hour | |
| dayofweek = timestamp.weekday() | |
| dayofyear = timestamp.timetuple().tm_yday | |
| month = timestamp.month | |
| features = { | |
| "hour_sin": np.sin(2 * np.pi * hour / 24), | |
| "hour_cos": np.cos(2 * np.pi * hour / 24), | |
| "dayofweek_sin": np.sin(2 * np.pi * dayofweek / 7), | |
| "dayofweek_cos": np.cos(2 * np.pi * dayofweek / 7), | |
| "dayofyear_sin": np.sin(2 * np.pi * dayofyear / 365), | |
| "dayofyear_cos": np.cos(2 * np.pi * dayofyear / 365), | |
| "month_sin": np.sin(2 * np.pi * month / 12), | |
| "month_cos": np.cos(2 * np.pi * month / 12), | |
| } | |
| return features | |
| def convert_cyclical_to_original( | |
| hour_sin, | |
| hour_cos, | |
| dayofweek_sin, | |
| dayofweek_cos, | |
| dayofyear_sin, | |
| dayofyear_cos, | |
| month_sin, | |
| month_cos, | |
| ): | |
| hour = np.arctan2(hour_sin, hour_cos) * 24 / (2 * np.pi) | |
| hour = int(np.round(hour % 24)) | |
| dayofweek = np.arctan2(dayofweek_sin, dayofweek_cos) * 7 / (2 * np.pi) | |
| dayofweek = int(np.round(dayofweek % 7)) | |
| dayofyear = np.arctan2(dayofyear_sin, dayofyear_cos) * 365 / (2 * np.pi) | |
| dayofyear = int(np.round(dayofyear % 365)) | |
| dayofyear = max(1, dayofyear) | |
| month = np.arctan2(month_sin, month_cos) * 12 / (2 * np.pi) | |
| month = int(np.round(month % 12)) | |
| month = 12 if month == 0 else month | |
| return { | |
| "hour": hour, | |
| "dayofweek": dayofweek, | |
| "dayofyear": dayofyear, | |
| "month": month, | |
| } | |
| def create_sequences(data, window_size=32): | |
| sequences = [] | |
| for i in range(len(data) - window_size + 1): | |
| sequences.append(data[i : i + window_size]) | |
| return np.array(sequences) | |
| def prepare_prediction_data(sensor_data, timestamp, scaler_x, window_size=32): | |
| try: | |
| sensor_features = { | |
| "Air temperature [K]": sensor_data.get("air_temp", 0), | |
| "Process temperature [K]": sensor_data.get("process_temp", 0), | |
| "Rotational speed [rpm]": sensor_data.get("rotational_speed", 0), | |
| "Torque [Nm]": sensor_data.get("torque", 0), | |
| "Tool wear [min]": sensor_data.get("tool_wear", 0), | |
| } | |
| cyclical_features = generate_cyclical_features(timestamp) | |
| all_features = {**sensor_features, **cyclical_features} | |
| column_order = [ | |
| "Air temperature [K]", | |
| "Process temperature [K]", | |
| "Rotational speed [rpm]", | |
| "Torque [Nm]", | |
| "Tool wear [min]", | |
| "hour_sin", | |
| "hour_cos", | |
| "dayofweek_sin", | |
| "dayofweek_cos", | |
| "dayofyear_sin", | |
| "dayofyear_cos", | |
| "month_sin", | |
| "month_cos", | |
| ] | |
| X_new = pd.DataFrame([all_features]) | |
| X_new = X_new[column_order] | |
| if scaler_x is None: | |
| raise ValueError("scaler_X not loaded.") | |
| X_scaled = scaler_x.transform(X_new) | |
| X_sequence = np.repeat(X_scaled, window_size, axis=0).reshape( | |
| 1, window_size, -1 | |
| ) | |
| return X_sequence | |
| except Exception as e: | |
| print(f"Error preparing prediction data: {str(e)}") | |
| import traceback | |
| traceback.print_exc() | |
| return None | |
| def create_timestamp_from_predictions(predictions, sensor_timestamp=None): | |
| try: | |
| hour = predictions.get("hour", 0) | |
| dayofyear = predictions.get("dayofyear", 1) | |
| if sensor_timestamp: | |
| if isinstance(sensor_timestamp, str): | |
| input_dt = datetime.fromisoformat( | |
| sensor_timestamp.replace("Z", "+00:00") | |
| ) | |
| else: | |
| input_dt = sensor_timestamp | |
| year = input_dt.year | |
| else: | |
| year = datetime.now().year | |
| predicted_dt = datetime.strptime(f"{year}-{dayofyear}", "%Y-%j") | |
| predicted_dt = predicted_dt.replace(hour=hour, minute=0, second=0) | |
| return predicted_dt.isoformat() | |
| except Exception as e: | |
| print(f"Error creating timestamp: {str(e)}") | |
| return None | |
| def prepare_sensor_data_for_anomaly(sensor_data, preprocessor): | |
| try: | |
| sensor_features = { | |
| "Air temperature [K]": sensor_data.get("air_temp"), | |
| "Process temperature [K]": sensor_data.get("process_temp"), | |
| "Rotational speed [rpm]": sensor_data.get("rotational_speed"), | |
| "Torque [Nm]": sensor_data.get("torque"), | |
| "Tool wear [min]": sensor_data.get("tool_wear"), | |
| } | |
| if None in sensor_features.values(): | |
| print("Error: Missing sensor data!") | |
| return None | |
| X = pd.DataFrame([sensor_features]) | |
| if preprocessor is None: | |
| raise ValueError("preprocessor not loaded.") | |
| X_transormed = preprocessor.transform(X) | |
| return X_transormed | |
| except Exception as e: | |
| print(f"Error preparing prediction data: {str(e)}") | |
| import traceback | |
| traceback.print_exc() | |
| return None | |
| def calculate_risk_score(confidence, severity): | |
| severity_weight = severity / 5.0 | |
| risk_score = confidence * severity_weight * 100 | |
| return risk_score | |
| def get_risk_level(risk_score): | |
| if risk_score >= 80: | |
| return "Critical" | |
| elif risk_score >= 60: | |
| return "High" | |
| elif risk_score >= 40: | |
| return "Medium" | |
| elif risk_score >= 20: | |
| return "Low" | |
| else: | |
| return "Very Low" | |
| def get_failure_severity(prediction_index): | |
| severity_mapping = { | |
| 0: 4, # Heat Dissipation Failure - High | |
| 1: 4, # Overstrain Failure - High | |
| 2: 5, # Power Failure - Critical | |
| 3: 2, # Random Failures - Low-Medium | |
| 4: 3, # Tool Wear Failure - Medium | |
| } | |
| return severity_mapping.get(prediction_index, 3) | |
| def get_failure_type_name(prediction_index): | |
| failure_types = { | |
| 0: "Heat Dissipation Failure", | |
| 1: "Overstrain Failure", | |
| 2: "Power Failure", | |
| 3: "Random Failures", | |
| 4: "Tool Wear Failure", | |
| } | |
| return failure_types.get(prediction_index, "Unknown Failure") | |