{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":96320,"databundleVersionId":11446019,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Task 1. Clustering","metadata":{}},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"%%capture\n!pip install umap-learn","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:33:50.723044Z","iopub.execute_input":"2025-10-12T09:33:50.724056Z","iopub.status.idle":"2025-10-12T09:33:56.743136Z","shell.execute_reply.started":"2025-10-12T09:33:50.724020Z","shell.execute_reply":"2025-10-12T09:33:56.741677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom sklearn.mixture import GaussianMixture\nfrom sklearn.cluster import KMeans\nfrom sklearn.cluster import AgglomerativeClustering\nfrom sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score\nfrom sklearn.decomposition import PCA\nimport umap\nimport matplotlib.pyplot as plt\nimport time","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:33:56.745684Z","iopub.execute_input":"2025-10-12T09:33:56.746339Z","iopub.status.idle":"2025-10-12T09:34:45.950312Z","shell.execute_reply.started":"2025-10-12T09:33:56.746297Z","shell.execute_reply":"2025-10-12T09:34:45.949120Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"markdown","source":"\nThe dataset contains 54 columns per row, containing the following data:\n\n- Timestamp (sec)\n- Heart rate (bpm)\n- 3-19 columns --- arm sensor data\n- 20-36 columns ---  chest sensor data\n- 37-53 columns ---  ankle sensor data\n \nThe goal is to cluster data on different types of physical activity (running, walking, ridering, etc.)","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/clustering-physical-activity-data/Physical_Activity_Monitoring_unlabeled.csv\")\n\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:45.951580Z","iopub.execute_input":"2025-10-12T09:34:45.952124Z","iopub.status.idle":"2025-10-12T09:34:55.011544Z","shell.execute_reply.started":"2025-10-12T09:34:45.952099Z","shell.execute_reply":"2025-10-12T09:34:55.010493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:55.013858Z","iopub.execute_input":"2025-10-12T09:34:55.014178Z","iopub.status.idle":"2025-10-12T09:34:56.792482Z","shell.execute_reply.started":"2025-10-12T09:34:55.014154Z","shell.execute_reply":"2025-10-12T09:34:56.791592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:56.793431Z","iopub.execute_input":"2025-10-12T09:34:56.793761Z","iopub.status.idle":"2025-10-12T09:34:56.909027Z","shell.execute_reply.started":"2025-10-12T09:34:56.793733Z","shell.execute_reply":"2025-10-12T09:34:56.908009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x = df.copy().dropna()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:56.910068Z","iopub.execute_input":"2025-10-12T09:34:56.910421Z","iopub.status.idle":"2025-10-12T09:34:57.159575Z","shell.execute_reply.started":"2025-10-12T09:34:56.910398Z","shell.execute_reply":"2025-10-12T09:34:57.158501Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Models\n\nModels considered:\n  * KMeans\n  * AgglomerativeClustering\n  * GaussianMixture","metadata":{}},{"cell_type":"code","source":"models_to_evaluate = {\n    \"GaussianMixture\": GaussianMixture(n_components=8, n_init=3),\n    \"KMeans\": KMeans(n_init=10),\n    # \"AgglomerativeClustering\": AgglomerativeClustering(),  # Need to much memory, will be trained separately\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:57.160629Z","iopub.execute_input":"2025-10-12T09:34:57.161002Z","iopub.status.idle":"2025-10-12T09:34:57.166611Z","shell.execute_reply.started":"2025-10-12T09:34:57.160974Z","shell.execute_reply":"2025-10-12T09:34:57.165493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"total_train_time = time.perf_counter()\nfor model_name, model in models_to_evaluate.items():\n    print(f\"Start to train model {model_name}\")\n    train_time = time.perf_counter()\n    model.fit(x)\n    print(f\"Train took {(time.perf_counter() - train_time):.4f} (s)\")\nprint(f\"Total train time {(time.perf_counter() - total_train_time):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:34:57.167823Z","iopub.execute_input":"2025-10-12T09:34:57.168302Z","iopub.status.idle":"2025-10-12T09:49:49.820452Z","shell.execute_reply.started":"2025-10-12T09:34:57.168238Z","shell.execute_reply":"2025-10-12T09:49:49.819535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NUM_SAMPLES = 50_000\n\nx_cut = x[:NUM_SAMPLES]\n\ny_pred_ac = AgglomerativeClustering().fit_predict(x_cut)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:49:49.821525Z","iopub.execute_input":"2025-10-12T09:49:49.821881Z","iopub.status.idle":"2025-10-12T09:54:08.320205Z","shell.execute_reply.started":"2025-10-12T09:49:49.821852Z","shell.execute_reply":"2025-10-12T09:54:08.319167Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Models score","metadata":{}},{"cell_type":"markdown","source":"Let evaluate models using metrics --- `Silhouette Score`, `Calinski-Harabasz Score` and `Davies-Bouldin Score`","metadata":{}},{"cell_type":"code","source":"silhouette = silhouette_score(x_cut, y_pred_ac)\ncalinski = calinski_harabasz_score(x_cut, y_pred_ac)\ndavies = davies_bouldin_score(x_cut, y_pred_ac)\n\nprint(f\"Metrics for AgglomerativeClustering:\")\nprint(f\"silhouette_score: {silhouette:.4f}\")\nprint(f\"calinski_harabasz_score: {calinski:.4f}\")\nprint(f\"davies_bouldin_score: {davies:.4f}\")\nprint(\"\\n*******\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:54:08.322935Z","iopub.execute_input":"2025-10-12T09:54:08.323609Z","iopub.status.idle":"2025-10-12T09:54:40.432223Z","shell.execute_reply.started":"2025-10-12T09:54:08.323580Z","shell.execute_reply":"2025-10-12T09:54:40.431125Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for model_name, model in models_to_evaluate.items():\n    y_pred = model.predict(x_cut)\n    silhouette = silhouette_score(x_cut, y_pred)\n    calinski = calinski_harabasz_score(x_cut, y_pred)\n    davies = davies_bouldin_score(x_cut, y_pred)\n        \n    print(f\"Metrics for {model_name}:\")\n    print(f\"silhouette_score: {silhouette:.4f}\")\n    print(f\"calinski_harabasz_score: {calinski:.4f}\")\n    print(f\"davies_bouldin_score: {davies:.4f}\")\n    print(\"\\n*******\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:54:40.433443Z","iopub.execute_input":"2025-10-12T09:54:40.433822Z","iopub.status.idle":"2025-10-12T09:55:36.894096Z","shell.execute_reply.started":"2025-10-12T09:54:40.433798Z","shell.execute_reply":"2025-10-12T09:55:36.892990Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"And visualize the resulting clusters","metadata":{}},{"cell_type":"code","source":"pca_2d = PCA(n_components=2)\npca_x_2d = pca_2d.fit_transform(x_cut)\n\nmanifold = umap.UMAP(n_jobs=-1).fit(x_cut)\numap_x_2d = manifold.transform(x_cut)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T09:55:36.895196Z","iopub.execute_input":"2025-10-12T09:55:36.895604Z","iopub.status.idle":"2025-10-12T09:57:18.291184Z","shell.execute_reply.started":"2025-10-12T09:55:36.895564Z","shell.execute_reply":"2025-10-12T09:57:18.289973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, axes = plt.subplots(nrows=len(models_to_evaluate) + 1, ncols=2, figsize=(25,30))\n\npca_2d_data = pd.DataFrame(pca_x_2d,columns=['PC1','PC2'])\numap_2d_data = pd.DataFrame(umap_x_2d,columns=['UMAP1','UMAP2']) \n\npca_2d_data['cluster'] = pd.Categorical(y_pred_ac)\numap_2d_data['cluster'] = pd.Categorical(y_pred_ac)\naxes[0, 0].scatter(pca_2d_data['PC1'], pca_2d_data['PC2'], c=pca_2d_data['cluster'], cmap=plt.colormaps[\"nipy_spectral\"].resampled(20), edgecolors=\"none\",\n                       alpha=0.4)\naxes[0, 0].title.set_text(f\"PCA {model_name}\")\naxes[0, 0].grid(None)\n\naxes[0, 1].scatter(umap_2d_data['UMAP1'], umap_2d_data['UMAP2'], c=umap_2d_data['cluster'], cmap=plt.colormaps[\"nipy_spectral\"].resampled(20), edgecolors=\"none\",\n                   alpha=0.4)\naxes[0, 1].title.set_text(f\"UMAP {model_name}\")\naxes[0, 1].grid(None)\n\nfor i, (model_name, model) in enumerate(models_to_evaluate.items()):\n    pred = model.predict(x_cut)\n    pca_2d_data['cluster'] = pd.Categorical(pred)\n    umap_2d_data['cluster'] = pd.Categorical(pred)\n\n    axes[i + 1, 0].scatter(pca_2d_data['PC1'], pca_2d_data['PC2'], c=pca_2d_data['cluster'], cmap=plt.colormaps[\"nipy_spectral\"].resampled(20), edgecolors=\"none\",\n                       alpha=0.4)\n    axes[i + 1, 0].title.set_text(f\"PCA {model_name}\")\n    axes[i + 1, 0].grid(None)\n    \n    axes[i + 1, 1].scatter(umap_2d_data['UMAP1'], umap_2d_data['UMAP2'], c=umap_2d_data['cluster'], cmap=plt.colormaps[\"nipy_spectral\"].resampled(20), edgecolors=\"none\",\n                       alpha=0.4)\n    axes[i + 1, 1].title.set_text(f\"UMAP {model_name}\")\n    axes[i + 1, 1].grid(None)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T10:02:42.581763Z","iopub.execute_input":"2025-10-12T10:02:42.582103Z","iopub.status.idle":"2025-10-12T10:02:46.495486Z","shell.execute_reply.started":"2025-10-12T10:02:42.582079Z","shell.execute_reply":"2025-10-12T10:02:46.493981Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Conclusion","metadata":{}},{"cell_type":"markdown","source":"AgglomerativeClustering with 2 clusters demonstrates the best clustering quality according to metrics. Calinski-Harabasz Score tends to overestimate when the number of clusters is large, thats why KMeans has better Calinski-Harabasz Score.","metadata":{}}]}