{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"this notebook using python version 3.7\n\ncopy & edit through this link if you can't downgrade \n\nhttps://www.kaggle.com/code/sabilarrosad/predictive-maintenance-survival-analysis","metadata":{}},{"cell_type":"code","source":"!pip install lifelines\n!pip install lifetimes\n!pip install pysurvival","metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-06-09T13:24:01.138712Z","iopub.execute_input":"2023-06-09T13:24:01.139101Z","iopub.status.idle":"2023-06-09T13:26:28.278916Z","shell.execute_reply.started":"2023-06-09T13:24:01.138995Z","shell.execute_reply":"2023-06-09T13:26:28.277871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\n\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\nplt.style.use('fivethirtyeight')\nimport seaborn as sns\n\nimport warnings\nwarnings.simplefilter(action='ignore', category= FutureWarning)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-06-09T13:27:02.562782Z","iopub.execute_input":"2023-06-09T13:27:02.563104Z","iopub.status.idle":"2023-06-09T13:27:03.729635Z","shell.execute_reply.started":"2023-06-09T13:27:02.563071Z","shell.execute_reply":"2023-06-09T13:27:03.728708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/predictive-maintenance-dataset-ai4i-2020/ai4i2020.csv')\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T13:27:05.936538Z","iopub.execute_input":"2023-06-09T13:27:05.936823Z","iopub.status.idle":"2023-06-09T13:27:06.010406Z","shell.execute_reply.started":"2023-06-09T13:27:05.936792Z","shell.execute_reply":"2023-06-09T13:27:06.009534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df.drop(['UDI', 'Product ID'], axis = 1)\ndf.head(3)","metadata":{"execution":{"iopub.status.busy":"2023-06-09T13:28:00.774074Z","iopub.execute_input":"2023-06-09T13:28:00.774398Z","iopub.status.idle":"2023-06-09T13:28:00.801289Z","shell.execute_reply.started":"2023-06-09T13:28:00.774365Z","shell.execute_reply":"2023-06-09T13:28:00.800328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# label encoding\nmap = {'L' : 0, 'M' : 1, 'H' : 2}\ndf['type'] = df['Type'].replace(map)\ndf = df.drop('Type',axis=1)\n\n# kelvin to celcius\ndf['process_temperature_c'] = (df['Process temperature [K]'] - 273.15).round(2)\ndf = df.drop('Process temperature [K]',axis=1)\n\ndf['air_temperature_c'] = (df['Air temperature [K]'] - 273.15).round(2)\ndf = df.drop('Air temperature [K]',axis=1)\n\ndf.rename(columns = {'Rotational speed [rpm]':'rotational_speed_rpm',\n                     'Torque [Nm]':'torque_nm',\n                     'Tool wear [min]':'tool_wear_min',\n                     'Machine failure':'machine_failure'}, inplace = True)\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T13:33:02.332431Z","iopub.execute_input":"2023-06-09T13:33:02.332888Z","iopub.status.idle":"2023-06-09T13:33:02.382696Z","shell.execute_reply.started":"2023-06-09T13:33:02.332845Z","shell.execute_reply":"2023-06-09T13:33:02.381103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n## Survival function \n\nSurvival function is given by \n\n\\begin{equation}\nS(t) = P(T > t)\n\\end{equation}\n\nInterpretation: probability of the event of interest **not occurring** by time $t$.\n\n","metadata":{}},{"cell_type":"markdown","source":"# Kaplan Meier","metadata":{}},{"cell_type":"code","source":"from lifelines import KaplanMeierFitter\nfig, ax = plt.subplots(figsize=(15, 10))\nkmf = KaplanMeierFitter()\nkmf.fit(df['tool_wear_min'], event_observed=df['machine_failure'])\nmedian = kmf.median_survival_time_\nprint('median time of machine has broken: {}'.format(median))\nkmf.plot_survival_function(ax=ax)\nplt.title('Survival probability of machine');","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:14:01.127213Z","iopub.execute_input":"2023-06-09T15:14:01.128543Z","iopub.status.idle":"2023-06-09T15:14:01.509508Z","shell.execute_reply.started":"2023-06-09T15:14:01.128489Z","shell.execute_reply":"2023-06-09T15:14:01.508424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from lifelines.plotting import add_at_risk_counts\nfig, ax = plt.subplots(figsize=(15, 15))\n\ntwf = df.drop(['HDF', 'PWF', 'OSF', 'RNF'], axis = 1)\nhdf = df.drop(['TWF', 'PWF', 'OSF', 'RNF'], axis = 1)\npwf = df.drop(['TWF', 'HDF', 'OSF', 'RNF'], axis = 1)\nosf = df.drop(['TWF', 'HDF', 'PWF', 'RNF'], axis = 1)\nrnf = df.drop(['TWF', 'HDF', 'PWF', 'OSF'], axis = 1)\n\nkmf_twf = KaplanMeierFitter()\nkmf_twf.fit(twf['tool_wear_min'], event_observed=twf['TWF'], label=\"Tool Wear Failure\")\nkmf_twf.plot_survival_function(ax=ax)\n\nkmf_hdf = KaplanMeierFitter()\nkmf_hdf.fit(hdf['tool_wear_min'], event_observed=hdf['HDF'], label=\"Heat Dissipation Failure\")\nkmf_hdf.plot_survival_function(ax=ax)\n\nkmf_pwf = KaplanMeierFitter()\nkmf_pwf.fit(pwf['tool_wear_min'], event_observed=pwf['PWF'], label=\"Power Failure\")\nkmf_pwf.plot_survival_function(ax=ax)\n\nkmf_osf = KaplanMeierFitter()\nkmf_osf.fit(osf['tool_wear_min'], event_observed=osf['OSF'], label=\"Overstrain Failure\")\nkmf_osf.plot_survival_function(ax=ax)\n\nkmf_rnf = KaplanMeierFitter()\nkmf_rnf.fit(rnf['tool_wear_min'], event_observed=rnf['RNF'], label=\"Random Failure\")\nkmf_rnf.plot_survival_function(ax=ax)\n\nadd_at_risk_counts(kmf_twf, kmf_hdf, kmf_pwf, kmf_osf,kmf_rnf, ax=ax)\nplt.tight_layout()\nplt.title(\"Survival Probability of different type failure\");","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:14:34.259942Z","iopub.execute_input":"2023-06-09T15:14:34.260300Z","iopub.status.idle":"2023-06-09T15:14:35.403523Z","shell.execute_reply.started":"2023-06-09T15:14:34.260262Z","shell.execute_reply":"2023-06-09T15:14:35.402386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(15, 10))\n\nlow = df[df['type'] == 0]\nmed = df[df['type'] == 1]\nhigh = df[df['type'] == 2]\n\nkmf = KaplanMeierFitter()\nkmf.fit(low['tool_wear_min'], event_observed=low['machine_failure'], label=\"Type L\")\nkmf.plot_survival_function(ax=ax)\n\nkmf = KaplanMeierFitter()\nkmf.fit(med['tool_wear_min'], event_observed=med['machine_failure'], label=\"Type M\")\nkmf.plot_survival_function(ax=ax)\n\nkmf = KaplanMeierFitter()\nkmf.fit(high['tool_wear_min'], event_observed=high['machine_failure'], label=\"Type H\")\nkmf.plot_survival_function(ax=ax)\n\nplt.title(\"Survival Probability of different type machine\");","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:16:22.558211Z","iopub.execute_input":"2023-06-09T15:16:22.558924Z","iopub.status.idle":"2023-06-09T15:16:23.042615Z","shell.execute_reply.started":"2023-06-09T15:16:22.558842Z","shell.execute_reply":"2023-06-09T15:16:23.041169Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"mesin memiliki probabilitas kerusakan yang lebih tinggi setelah melebihi 233 menit, baik itu pada mesin bertipe L, M ataupun H. perusahaan dapat mengatur jadwal perawatan terjadwal untuk mesin-mesin tersebut. Dengan melakukan perawatan secara teratur sebelum mencapai waktu tersebut, perusahaan dapat mengurangi risiko kerusakan dan memperpanjang masa pakai mesin. Mesin yang mengalami kerusakan HDF, PWF dan RNF terlihat memiliki survival probability yang masih tinggi saat penggunaan melebihi 250 menit. Dengan kata lain, perusahaan harus lebih waspada ketika mesin teridentifikasi mengalami kerusakan TWF dan OSF.","metadata":{}},{"cell_type":"markdown","source":"# Manual Calculation","metadata":{}},{"cell_type":"code","source":"# Step 1: Sort the observed event times\nsorted_event_times = np.sort(df['tool_wear_min'].unique())\n\n# Step 2: Initialize survival probability\nsurvival_prob = np.array([1.0])\n\n# Step 3: Iterate through the sorted event times\nat_risk = []\nevents = []\n\nfor time in sorted_event_times:\n    # Number of individuals at risk\n    n_at_risk = len(df[df['tool_wear_min'] >= time])\n\n    # Number of observed events\n    n_events = df[(df['tool_wear_min'] == time) & (df['machine_failure'] == 1)]['machine_failure'].sum()\n\n    # Append values to lists\n    at_risk.append(n_at_risk)\n    events.append(n_events)\n\n    # Calculate survival probability\n    prob_not_event = (n_at_risk - n_events) / n_at_risk\n    survival_prob = np.append(survival_prob, survival_prob[-1] * prob_not_event)\n\n# Step 4: Create DataFrame with the results\ndf_survival = pd.DataFrame({\n    'Time': sorted_event_times,\n    'At Risk': at_risk,\n    'Events': events,\n    'Survival Probability': survival_prob[:-1]\n})\n\n# Reset index\ndf_survival.reset_index(drop=True, inplace=True)\ndf_survival.head(15)","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:22:22.367078Z","iopub.execute_input":"2023-06-09T15:22:22.367460Z","iopub.status.idle":"2023-06-09T15:22:23.021523Z","shell.execute_reply.started":"2023-06-09T15:22:22.367404Z","shell.execute_reply":"2023-06-09T15:22:23.020503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_survival[df_survival[\"Time\"] == 210]","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:23:28.943083Z","iopub.execute_input":"2023-06-09T15:23:28.943595Z","iopub.status.idle":"2023-06-09T15:23:28.957906Z","shell.execute_reply.started":"2023-06-09T15:23:28.943557Z","shell.execute_reply":"2023-06-09T15:23:28.956870Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Line chart\nplt.figure(figsize=(15, 10))\nplt.plot(df_survival['Time'], df_survival['Survival Probability'], marker='o', linestyle='-')\n\n# Axes labels\nplt.xlabel('Time')\nplt.ylabel('Survival Probability')\n\n# Title\nplt.title('Survival Probability Manual Calculation')\n\n# Display the plot\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:23:58.554472Z","iopub.execute_input":"2023-06-09T15:23:58.554791Z","iopub.status.idle":"2023-06-09T15:23:58.854683Z","shell.execute_reply.started":"2023-06-09T15:23:58.554755Z","shell.execute_reply":"2023-06-09T15:23:58.853422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_resampled[df_resampled['tool_wear_min'] > 233].describe()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:24:39.313068Z","iopub.execute_input":"2023-06-09T15:24:39.313364Z","iopub.status.idle":"2023-06-09T15:24:39.354195Z","shell.execute_reply.started":"2023-06-09T15:24:39.313332Z","shell.execute_reply":"2023-06-09T15:24:39.353456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_resampled[df_resampled['tool_wear_min'] <= 233].describe()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:06.292758Z","iopub.execute_input":"2023-06-09T15:25:06.293075Z","iopub.status.idle":"2023-06-09T15:25:06.340995Z","shell.execute_reply.started":"2023-06-09T15:25:06.293042Z","shell.execute_reply":"2023-06-09T15:25:06.339974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Random Survival Forest**","metadata":{}},{"cell_type":"code","source":"x = df[['type', 'air_temperature_c','process_temperature_c','rotational_speed_rpm','torque_nm','tool_wear_min']]\ny = df.machine_failure","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:39.393729Z","iopub.execute_input":"2023-06-09T15:25:39.394210Z","iopub.status.idle":"2023-06-09T15:25:39.402572Z","shell.execute_reply.started":"2023-06-09T15:25:39.394151Z","shell.execute_reply":"2023-06-09T15:25:39.400966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from imblearn.over_sampling import SMOTE","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:40.260578Z","iopub.execute_input":"2023-06-09T15:25:40.260893Z","iopub.status.idle":"2023-06-09T15:25:40.267467Z","shell.execute_reply.started":"2023-06-09T15:25:40.260861Z","shell.execute_reply":"2023-06-09T15:25:40.266685Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Membuat objek SMOTE\nsmote = SMOTE(sampling_strategy=0.8, random_state=50)\n\n# Melakukan resampling menggunakan SMOTE\nx_resampled, y_resampled = smote.fit_resample(x, y)\n\n# Mencetak jumlah sampel baru setelah resampling\nprint(\"Jumlah sampel baru setelah resampling: \", len(x_resampled))\nprint(y_resampled.value_counts())","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:41.543006Z","iopub.execute_input":"2023-06-09T15:25:41.543571Z","iopub.status.idle":"2023-06-09T15:25:41.564218Z","shell.execute_reply.started":"2023-06-09T15:25:41.543509Z","shell.execute_reply":"2023-06-09T15:25:41.563337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Mengganti nama kolom\ncols = list(x.columns)\ndf_resampled = pd.DataFrame(x_resampled, columns=cols)\ndf_resampled['target'] = y_resampled\n\n# Mencetak dataframe hasil gabungan\ndf_resampled.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:45.519710Z","iopub.execute_input":"2023-06-09T15:25:45.520607Z","iopub.status.idle":"2023-06-09T15:25:45.539659Z","shell.execute_reply.started":"2023-06-09T15:25:45.520563Z","shell.execute_reply":"2023-06-09T15:25:45.538527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X = df_resampled.drop(['tool_wear_min', 'target'], axis = 1)\nE = df_resampled['target']\nT = df_resampled['tool_wear_min']","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:46.733656Z","iopub.execute_input":"2023-06-09T15:25:46.733993Z","iopub.status.idle":"2023-06-09T15:25:46.746302Z","shell.execute_reply.started":"2023-06-09T15:25:46.733955Z","shell.execute_reply":"2023-06-09T15:25:46.745007Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Perform train-test split\nX_train, X_test, E_train, E_test, T_train, T_test = train_test_split(X, E, T, test_size=0.2, random_state=42)\n\n# Verify the shapes of the train and test sets\nprint(\"X_train shape:\", X_train.shape)\nprint(\"X_test shape:\", X_test.shape)\nprint(\"E_train shape:\", E_train.shape)\nprint(\"E_test shape:\", E_test.shape)\nprint(\"T_train shape:\", T_train.shape)\nprint(\"T_test shape:\", T_test.shape)","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:47.692082Z","iopub.execute_input":"2023-06-09T15:25:47.692400Z","iopub.status.idle":"2023-06-09T15:25:47.709970Z","shell.execute_reply.started":"2023-06-09T15:25:47.692364Z","shell.execute_reply":"2023-06-09T15:25:47.708465Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pysurvival.models.survival_forest import RandomSurvivalForestModel\nfrom pysurvival.utils.display import compare_to_actual\nfrom pysurvival.utils.metrics import concordance_index\nfrom pysurvival.utils.display import integrated_brier_score","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:25:56.772984Z","iopub.execute_input":"2023-06-09T15:25:56.773890Z","iopub.status.idle":"2023-06-09T15:25:58.095140Z","shell.execute_reply.started":"2023-06-09T15:25:56.773828Z","shell.execute_reply":"2023-06-09T15:25:58.094190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"rsf = RandomSurvivalForestModel(num_trees=100)\nrsf.fit(X_train, T_train, E_train,\n        max_features=\"sqrt\", max_depth=5, min_node_size=20)\n\n\n#### Cross Validation / Model Performances\nc_index = concordance_index(rsf, X_test, T_test, E_test)\nprint('C-index: {:.2f}'.format(c_index))\n\nibs = integrated_brier_score(rsf, X_test, T_test, E_test, t_max=30,\n            figure_size=(16, 6.5) )\nprint('IBS: {:.2f}'.format(ibs))","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:26:29.261025Z","iopub.execute_input":"2023-06-09T15:26:29.261343Z","iopub.status.idle":"2023-06-09T15:28:37.966925Z","shell.execute_reply.started":"2023-06-09T15:26:29.261309Z","shell.execute_reply":"2023-06-09T15:28:37.965915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dengan C-index meleebihi 0.80 dan IBS sebesar 0.02, dapat disimpulkan bahwa random survival model memiliki kemampuan yang baik dalam memprediksi risiko terjadinya kerusakan mesin dengan tingkat ketepatan yang tinggi dalam memperkirakan probabilitas risikonya pada waktu tertentu.","metadata":{}},{"cell_type":"code","source":"results = compare_to_actual(rsf, X_test, T_test, E_test,\n                            is_at_risk = True,  figure_size=(16, 10),\n                            metrics = ['rmse', 'mean', 'median'])","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:33:02.833395Z","iopub.execute_input":"2023-06-09T15:33:02.833787Z","iopub.status.idle":"2023-06-09T15:33:15.423945Z","shell.execute_reply.started":"2023-06-09T15:33:02.833746Z","shell.execute_reply":"2023-06-09T15:33:15.422496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5 sample testing","metadata":{}},{"cell_type":"code","source":"np.random.seed(36)\nfiltered_indices = np.where(E_test == 1)[0]\nrandom_indices = np.random.choice(filtered_indices, size=5, replace=False)\nrandom_samples = X_test.iloc[random_indices]\ntime_samples = T_test.iloc[random_indices]\nevent_samples = E_test.iloc[random_indices]\n\nsamples = pd.concat([random_samples, time_samples, event_samples], axis=1)\nsamples","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:38:09.183000Z","iopub.execute_input":"2023-06-09T15:38:09.183341Z","iopub.status.idle":"2023-06-09T15:38:09.205974Z","shell.execute_reply.started":"2023-06-09T15:38:09.183304Z","shell.execute_reply":"2023-06-09T15:38:09.204658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_survival_curve(testing_data, title):\n    fig, ax = plt.subplots(figsize=(15, 8))\n    survival = rsf.predict_survival(testing_data).flatten()\n    risk = rsf.predict_risk(testing_data)\n    hazard = rsf.predict_hazard(testing_data)\n    print(f'   Risk: {risk}')\n\n    times = np.arange(len(survival))\n\n    plt.plot(times, survival, lw=2, label='Survival Curve')\n\n    thresholds = [0.6, 0.4, 0.2]\n    colors = ['green', 'orange', 'red']\n    labels = ['Low Risk Fail', 'Medium Risk Fail', 'High Risk Fail']\n\n    for threshold, color, label in zip(thresholds, colors, labels):\n        exceeds_threshold = np.where(survival > threshold)[0]\n        if len(exceeds_threshold) > 0:\n            max_exceeds_threshold = exceeds_threshold[-1]\n            plt.axvline(x=max_exceeds_threshold, color=color, linestyle='--', label=label)\n            annotate_time = times[max_exceeds_threshold]\n            plt.annotate(f'   T: {annotate_time}', xy=(annotate_time, threshold),\n                         xytext=(annotate_time, threshold - 0.1),\n                         arrowprops=dict(facecolor='black', arrowstyle='->'))\n\n    plt.legend(fontsize=12)\n    plt.title(title, fontsize=15)  # Set the provided title\n    plt.ylim(0, 1.05)\n    plt.show()\n    threshold_times = []\n    for threshold in thresholds:\n        exceeds_threshold = np.where(survival > threshold)[0]\n        if len(exceeds_threshold) > 0:\n            max_exceeds_threshold = exceeds_threshold[-1]\n            annotate_time = times[max_exceeds_threshold]\n            threshold_times.append(annotate_time)\n        else:\n            threshold_times.append(None)\n\n    return threshold_times\n\n# Create a list to collect the annotated times for each row\nannotated_times = []\n\n# Iterate over the rows in random_samples\nfor i, (_, row) in enumerate(random_samples.iterrows()):\n    testing_data = np.array([row])  # Convert the row to a 2D array for prediction\n    title = f\"Sample {i+1}\"  # Set the title for each sample plot\n    threshold_times = plot_survival_curve(testing_data, title)\n    annotated_times.append(threshold_times)\n\n# Create the DataFrame from the collected annotated times\ndf_annotated_times = pd.DataFrame(annotated_times, columns=['Low Risk', 'Medium Risk', 'High Risk'])\ndf_annotated_times = df_annotated_times.set_index(samples.index)  # Set the index to match 'samples'\n\n# Merge the 'samples' DataFrame and 'df_annotated_times' DataFrame based on their indices\nsamples_predicted = pd.merge(samples, df_annotated_times, left_index=True, right_index=True)\nsamples_predicted","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:38:17.756990Z","iopub.execute_input":"2023-06-09T15:38:17.757326Z","iopub.status.idle":"2023-06-09T15:38:20.661028Z","shell.execute_reply.started":"2023-06-09T15:38:17.757288Z","shell.execute_reply":"2023-06-09T15:38:20.659907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Calculate the absolute differences between 'tool_wear_min' and risk columns\ndiff_low = abs(samples_predicted['tool_wear_min'] - samples_predicted['Low Risk'])\ndiff_medium = abs(samples_predicted['tool_wear_min'] - samples_predicted['Medium Risk'])\ndiff_high = abs(samples_predicted['tool_wear_min'] - samples_predicted['High Risk'])\n\n# Create a new column 'Risk Type' based on the smallest difference\nsamples_predicted['Risk Type'] = ''\nsamples_predicted.loc[diff_low <= diff_medium, 'Risk Type'] += 'Low'\nsamples_predicted.loc[(diff_medium < diff_low) & (diff_medium < diff_high), 'Risk Type'] += 'Medium'\nsamples_predicted.loc[(diff_high < diff_low) & (diff_high < diff_medium), 'Risk Type'] += 'High'\n\n# Handle cases with the same or smaller differences between risk types\nsamples_predicted.loc[(diff_low == diff_medium) | ((diff_low <= 10) & (diff_medium <= 10)), 'Risk Type'] = 'Low-Medium'\nsamples_predicted.loc[(diff_high == diff_medium) | ((diff_high <= 10) & (diff_medium <= 10)), 'Risk Type'] = 'Medium-High'\nsamples_predicted.loc[(diff_low == diff_high) | ((diff_low <= 10) & (diff_high <= 10)), 'Risk Type'] = 'Low-High'\n\nsamples_predicted['Risk Type'] += ' Risk'\n\nsamples_predicted","metadata":{"execution":{"iopub.status.busy":"2023-06-09T15:40:00.923897Z","iopub.execute_input":"2023-06-09T15:40:00.924269Z","iopub.status.idle":"2023-06-09T15:40:00.961784Z","shell.execute_reply.started":"2023-06-09T15:40:00.924230Z","shell.execute_reply":"2023-06-09T15:40:00.960418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"low risk ketika survival probability nya mulai menyentuh di bawah 0.6\n\nmedium risk ketika survival probability nya mulai menyentuh di bawah 0.4\n\nhigh risk ketika survival probability nya mulai menyentuh di bawah 0.2","metadata":{}}]}