{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " session_duration_min pages_viewed items_purchased\n", "count 600.000000 600.000000 600.000000\n", "mean 12.502000 8.590000 1.635000\n", "std 8.522518 5.041918 2.259857\n", "min 0.500000 1.000000 0.000000\n", "25% 3.500000 4.000000 0.000000\n", "50% 12.000000 8.000000 0.000000\n", "75% 19.325000 13.000000 3.000000\n", "max 34.700000 19.000000 7.000000\n", "\n", "Estimated bandwidth: 1.0144\n", "Clusters found: 4\n", "Cluster name map: {0: 'Casual', 1: 'Browser', 2: 'Buyer', 3: 'Power User'}\n", " session_duration_min pages_viewed items_purchased\n", "label \n", "Browser 12.2 13.4 0.5\n", "Buyer 22.2 9.6 2.9\n", "Casual 3.0 3.0 0.0\n", "Power User 22.8 9.0 6.0\n", "\n", "Sample (18 min, 10 pages, 3 purchases): cluster 2 → Buyer\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.12/dist-packages/sklearn/utils/validation.py:2732: UserWarning: X has feature names, but StandardScaler was fitted without feature names\n", " warnings.warn(\n" ] } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "from sklearn.cluster import MeanShift, estimate_bandwidth\n", "from sklearn.preprocessing import StandardScaler\n", "\n", "np.random.seed(42)\n", "\n", "# Casual users: short sessions, few pages, no purchases\n", "casual = pd.DataFrame({\n", " 'session_duration_min': np.random.normal(3, 1, 200).clip(0.5, 8),\n", " 'pages_viewed': np.random.randint(1, 6, 200).astype(float),\n", " 'items_purchased': np.zeros(200),\n", "})\n", "# Browsers: moderate sessions, many pages, rare purchases\n", "browsers = pd.DataFrame({\n", " 'session_duration_min': np.random.normal(12, 3, 200).clip(5, 22),\n", " 'pages_viewed': np.random.randint(8, 20, 200).astype(float),\n", " 'items_purchased': np.random.randint(0, 2, 200).astype(float),\n", "})\n", "# Buyers: long sessions, moderate pages, multiple purchases\n", "buyers = pd.DataFrame({\n", " 'session_duration_min': np.random.normal(22, 4, 200).clip(12, 35),\n", " 'pages_viewed': np.random.randint(5, 15, 200).astype(float),\n", " 'items_purchased': np.random.randint(2, 8, 200).astype(float),\n", "})\n", "\n", "df = pd.concat([casual, browsers, buyers], ignore_index=True).round(1)\n", "print(df.describe())\n", "\n", "X = df[['session_duration_min', 'pages_viewed', 'items_purchased']].values\n", "\n", "scaler = StandardScaler()\n", "X_scaled = scaler.fit_transform(X)\n", "\n", "bandwidth = estimate_bandwidth(X_scaled, quantile=0.2, random_state=42)\n", "print(f'\\nEstimated bandwidth: {bandwidth:.4f}')\n", "\n", "ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)\n", "ms.fit(X_scaled)\n", "\n", "labels = ms.labels_\n", "n_clusters = len(np.unique(labels))\n", "print(f'Clusters found: {n_clusters}')\n", "\n", "# Assign names by sorting cluster centers on session_duration (ascending)\n", "centers_original = scaler.inverse_transform(ms.cluster_centers_)\n", "duration_order = np.argsort(centers_original[:, 0])\n", "duration_rank = np.empty_like(duration_order)\n", "duration_rank[duration_order] = np.arange(n_clusters)\n", "base_names = ['Casual', 'Browser', 'Buyer', 'Power User']\n", "cluster_name_map = {int(cid): base_names[min(int(rank), len(base_names) - 1)]\n", " for cid, rank in enumerate(duration_rank)}\n", "print('Cluster name map:', cluster_name_map)\n", "\n", "df['cluster'] = labels\n", "df['label'] = df['cluster'].map(cluster_name_map)\n", "print(df.groupby('label')[['session_duration_min', 'pages_viewed', 'items_purchased']].mean().round(1))\n", "\n", "# Sample inference\n", "sample = pd.DataFrame([[18.0, 10, 3]], columns=['session_duration_min', 'pages_viewed', 'items_purchased'])\n", "pred_cluster = int(ms.predict(scaler.transform(sample))[0])\n", "print(f'\\nSample (18 min, 10 pages, 3 purchases): cluster {pred_cluster} \\u2192 {cluster_name_map[pred_cluster]}')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.12/dist-packages/huggingface_hub/utils/_auth.py:122: UserWarning: \n", "Error while fetching `HF_TOKEN` secret value from your vault: 'Requesting secret HF_TOKEN timed out. Secrets can only be fetched when running from the Colab UI.'.\n", "You are not authenticated with the Hugging Face Hub in this notebook.\n", "If the error persists, please let us know by opening an issue on GitHub (https://github.com/huggingface/huggingface_hub/issues/new).\n", " warnings.warn(\n" ] } ], "source": [ "from huggingface_hub import notebook_login\n", "notebook_login()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model saved locally: /content/ML_MeanShift_UserBehavior.joblib\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "542d3ec58080495ababf206b1f833698", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Processing Files (0 / 0) : | | 0.00B / 0.00B " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "327a4ad562d74b639b60874c4934a691", "version_major": 2, "version_minor": 0 }, "text/plain": [ "New Data Upload : | | 0.00B / 0.00B " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "f20ff4c06e8c466c9586532951b0ede6", "version_major": 2, "version_minor": 0 }, "text/plain": [ " ...Shift_UserBehavior.joblib: 100%|##########| 5.98kB / 5.98kB " ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Uploaded to https://huggingface.co/looh2/model\n" ] } ], "source": [ "import joblib\n", "from pathlib import Path\n", "from huggingface_hub import HfApi\n", "\n", "repo_id = \"looh2/model\"\n", "model_path = Path(\"ML_MeanShift_UserBehavior.joblib\")\n", "\n", "bundle = {'scaler': scaler, 'ms': ms, 'cluster_name_map': cluster_name_map}\n", "joblib.dump(bundle, model_path)\n", "print(f\"Model saved locally: {model_path.resolve()}\")\n", "\n", "api = HfApi()\n", "api.create_repo(repo_id=repo_id, repo_type=\"model\", exist_ok=True)\n", "api.upload_file(\n", " path_or_fileobj=str(model_path),\n", " path_in_repo=model_path.name,\n", " repo_id=repo_id,\n", " repo_type=\"model\",\n", ")\n", "print(f\"Uploaded to https://huggingface.co/{repo_id}\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "aa817602449f4cfeaa31c8867aeab907", "version_major": 2, "version_minor": 0 }, "text/plain": [ "ML_MeanShift_UserBehavior.joblib: 0%| | 0.00/5.98k [00:00