3611 lines
4.3 MiB
Plaintext
3611 lines
4.3 MiB
Plaintext
|
|
{
|
|||
|
|
"cells": [
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "13c6141d",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"# Behavioral Clustering of Carmignac Investors\n",
|
|||
|
|
"\n",
|
|||
|
|
"This notebook implements two complementary clustering analyses:\n",
|
|||
|
|
"\n",
|
|||
|
|
"| | Scope | Approach |\n",
|
|||
|
|
"|---|---|---|\n",
|
|||
|
|
"| **Part 1** | All active accounts (~7,000) | Global behavioral clustering |\n",
|
|||
|
|
"| **Part 2** | Top 400 accounts (AUM > €5M) | High-conviction clustering with performance reactivity features |\n",
|
|||
|
|
"\n",
|
|||
|
|
"Both analyses share the same preprocessing pipeline (RobustScaler, MAD winsorization) and visualization conventions (robust z-score heatmaps).\n",
|
|||
|
|
"\n",
|
|||
|
|
"---\n",
|
|||
|
|
"**Structure:**\n",
|
|||
|
|
"1. Imports & Configuration\n",
|
|||
|
|
"2. Data Loading\n",
|
|||
|
|
"3. Monthly Panel Construction\n",
|
|||
|
|
"4. Feature Engineering\n",
|
|||
|
|
"5. **Part 1** — Global Clustering (all accounts)\n",
|
|||
|
|
" - 5a. Feature selection & preprocessing\n",
|
|||
|
|
" - 5b. K-selection & clustering\n",
|
|||
|
|
" - 5c. Cluster profiles (behavioral + allocation)\n",
|
|||
|
|
" - 5d. Asset-type sub-clustering & cross-analysis\n",
|
|||
|
|
"6. **Part 2** — Top 400 Accounts Clustering\n",
|
|||
|
|
" - 6a. Account selection & feature engineering\n",
|
|||
|
|
" - 6b. K-selection & clustering\n",
|
|||
|
|
" - 6c. Cluster profiles & churn analysis\n",
|
|||
|
|
"7. Cross-Analysis: Global vs Top 400\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "28e588fe",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 1. Imports & Configuration\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 1,
|
|||
|
|
"id": "3bc1ffe0",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": [
|
|||
|
|
"import os\n",
|
|||
|
|
"import s3fs\n",
|
|||
|
|
"import warnings\n",
|
|||
|
|
"warnings.filterwarnings(\"ignore\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"os.environ[\"AWS_ACCESS_KEY_ID\"] = 'UMMV3Z72A70MCCSRV17O'\n",
|
|||
|
|
"os.environ[\"AWS_SECRET_ACCESS_KEY\"] = 'wBFxaez78UPNW3BtchZOf4f238ZNXKnCexeGufaa'\n",
|
|||
|
|
"os.environ[\"AWS_SESSION_TOKEN\"] = 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJVTU1WM1o3MkE3ME1DQ1NSVjE3TyIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJhdWQiOlsibWluaW8iLCJhY2NvdW50Il0sImF1dGhfdGltZSI6MTc3NTEzNTA4NiwiYXpwIjoib255eGlhLW1pbmlvIiwiZW1haWwiOiJzYXJhaC50aG91bXlyZUBlbnNhZS5mciIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE3NzYzNDQ3NDksImZhbWlseV9uYW1lIjoiVEhPVU1ZUkUiLCJnaXZlbl9uYW1lIjoiU2FyYWgiLCJncm91cHMiOlsiYmRjLWRhdGEiLCJiZGMtY2FybWlnbmFjLWczIl0sImlhdCI6MTc3NTEzNTE0OCwiaXNzIjoiaHR0cHM6Ly9hdXRoLmdyb3VwZS1nZW5lcy5mci9yZWFsbXMvZ2VuZXMiLCJqdGkiOiJlZGY1ZDQ1OC1hYzkxLTQ5NTAtYmI5Ny0zNjMwNWY1MTQwYTIiLCJuYW1lIjoiU2FyYWggVEhPVU1ZUkUiLCJwb2xpY3kiOiJzdHNvbmx5IiwicHJlZmVycmVkX3VzZXJuYW1lIjoic3Rob3VteXJlLWVuc2FlIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy1nZW5lcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsInNpZCI6IjMzMjg4YjJjLTlhMjAtNDNhOS1iMDlhLTdlMjc1OWQ1NjIxNiIsInN1YiI6ImVhYWVkN2QyLWM4MjYtNGIxNC05MzczLTYwYjNhODhlMWFiNiIsInR5cCI6IkJlYXJlciJ9.rffoTJijRiGK2DCDhXj5y8R31DRH1LWkTwuH_1lvU9qN_xJSTmBIM4uGR_zp7XpMnq_ePwVhlkoWN15cNUgjMA'\n",
|
|||
|
|
"os.environ[\"AWS_DEFAULT_REGION\"] = 'us-east-1'\n",
|
|||
|
|
"\n",
|
|||
|
|
"fs = s3fs.S3FileSystem(\n",
|
|||
|
|
" client_kwargs={'endpoint_url': 'https://'+'minio-simple.lab.groupe-genes.fr'},\n",
|
|||
|
|
" key = os.environ[\"AWS_ACCESS_KEY_ID\"], \n",
|
|||
|
|
" secret = os.environ[\"AWS_SECRET_ACCESS_KEY\"], \n",
|
|||
|
|
" token = os.environ[\"AWS_SESSION_TOKEN\"])\n",
|
|||
|
|
"\n",
|
|||
|
|
"import numpy as np\n",
|
|||
|
|
"import pandas as pd\n",
|
|||
|
|
"import matplotlib.pyplot as plt\n",
|
|||
|
|
"import seaborn as sns\n",
|
|||
|
|
"\n",
|
|||
|
|
"from sklearn.preprocessing import RobustScaler\n",
|
|||
|
|
"from sklearn.cluster import KMeans\n",
|
|||
|
|
"from sklearn.metrics import (\n",
|
|||
|
|
" silhouette_score, davies_bouldin_score,\n",
|
|||
|
|
" pairwise_distances, adjusted_rand_score\n",
|
|||
|
|
")\n",
|
|||
|
|
"from sklearn.linear_model import LinearRegression\n",
|
|||
|
|
"\n",
|
|||
|
|
"sns.set_style(\"whitegrid\")\n",
|
|||
|
|
"pd.set_option(\"display.max_columns\", 200)\n",
|
|||
|
|
"pd.set_option(\"display.max_rows\", 200)\n",
|
|||
|
|
"\n",
|
|||
|
|
"EPS = 1e-9\n",
|
|||
|
|
"RANDOM_STATE = 42"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 2,
|
|||
|
|
"id": "69d2dc25",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": [
|
|||
|
|
"# Column names\n",
|
|||
|
|
"ID_COL = \"Registrar Account - ID\"\n",
|
|||
|
|
"ISIN_COL = \"Product - Isin\"\n",
|
|||
|
|
"FUND_COL = \"Product - Fund\"\n",
|
|||
|
|
"ASSET_COL = \"Product - Asset Type\"\n",
|
|||
|
|
"FLOW_DATE_COL = \"Centralisation Date\"\n",
|
|||
|
|
"AUM_DATE_COL = \"Centralisation Date\"\n",
|
|||
|
|
"FLOW_QTY_COL = \"Quantity - NetFlows\"\n",
|
|||
|
|
"FLOW_SUB_COL = \"Quantity - Subscription\"\n",
|
|||
|
|
"FLOW_RED_COL = \"Quantity - Redemption\"\n",
|
|||
|
|
"AUM_QTY_COL = \"Quantity - AUM\"\n",
|
|||
|
|
"AUM_VAL_COL = \"Value - AUM €\"\n",
|
|||
|
|
"REGION_COL = \"Registrar Account - Region\"\n",
|
|||
|
|
"COUNTRY_COL = \"RegistrarAccount - Country\"\n",
|
|||
|
|
"NAV_DATE_COL = \"Dat\"\n",
|
|||
|
|
"NAV_ISIN_COL = \"Isin\"\n",
|
|||
|
|
"NAV_PRICE_COL = \"Price (TF PartPrice)\"\n",
|
|||
|
|
"NAV_BENCH_COL = \"PriceBench\"\n",
|
|||
|
|
"RATE_DATE_COL = \"Date\"\n",
|
|||
|
|
"RATE_VAL_COL = \"Yld to Maturity\"\n",
|
|||
|
|
"\n",
|
|||
|
|
"PATH_NAV = \"s3://projet-bdc-data/carmignac/Data Modélisation/Nav/NAV_Bench_data.csv\"\n",
|
|||
|
|
"PATH_RATES = \"s3://projet-bdc-data/carmignac/Data Modélisation/market data/esterRates.csv\""
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 3,
|
|||
|
|
"id": "bf5b7a0a",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": [
|
|||
|
|
"# SHARED UTILITIES\n",
|
|||
|
|
"def robust_zscore(s):\n",
|
|||
|
|
" med = np.nanmedian(s)\n",
|
|||
|
|
" mad = np.nanmedian(np.abs(s - med))\n",
|
|||
|
|
" if mad == 0 or np.isnan(mad):\n",
|
|||
|
|
" return np.zeros(len(s))\n",
|
|||
|
|
" return (s - med) / (1.4826 * mad)\n",
|
|||
|
|
"\n",
|
|||
|
|
"def plot_heatmap(dfc, profile_vars, cluster_col, title, figsize=(16, 4)):\n",
|
|||
|
|
" \"\"\"Cluster signature heatmap using robust z-scores, capped at ±3 for readability.\"\"\"\n",
|
|||
|
|
" dfc_viz = dfc[profile_vars + [cluster_col]].copy()\n",
|
|||
|
|
" for col in profile_vars:\n",
|
|||
|
|
" vals = pd.to_numeric(dfc_viz[col], errors=\"coerce\").to_numpy(dtype=float)\n",
|
|||
|
|
" lo = np.nanpercentile(vals, 2)\n",
|
|||
|
|
" hi = np.nanpercentile(vals, 98)\n",
|
|||
|
|
" dfc_viz[col] = np.clip(vals, lo, hi)\n",
|
|||
|
|
" prof = dfc_viz.groupby(cluster_col)[profile_vars].median()\n",
|
|||
|
|
" prof_z = prof.apply(lambda col: robust_zscore(col.values), axis=0)\n",
|
|||
|
|
" prof_z = prof_z.clip(-3, 3) # cap for readability\n",
|
|||
|
|
" plt.figure(figsize=figsize)\n",
|
|||
|
|
" sns.heatmap(prof_z, cmap=\"RdBu_r\", center=0, annot=True, fmt=\".2f\",\n",
|
|||
|
|
" xticklabels=profile_vars,\n",
|
|||
|
|
" yticklabels=[f\"Cluster {i}\" for i in range(len(prof))])\n",
|
|||
|
|
" plt.title(title)\n",
|
|||
|
|
" plt.xticks(rotation=45, ha=\"right\")\n",
|
|||
|
|
" plt.tight_layout()\n",
|
|||
|
|
" plt.show()\n",
|
|||
|
|
" return prof\n",
|
|||
|
|
"\n",
|
|||
|
|
"def winsorize_mad(series, n_sigma=3):\n",
|
|||
|
|
" \"\"\"Winsorize using MAD n-sigma rule. Falls back to p95 clip when MAD~0.\"\"\"\n",
|
|||
|
|
" vals = pd.to_numeric(series, errors=\"coerce\").to_numpy(dtype=float)\n",
|
|||
|
|
" med = np.nanmedian(vals)\n",
|
|||
|
|
" mad = np.nanmedian(np.abs(vals - med)) * 1.4826\n",
|
|||
|
|
" if mad > 0:\n",
|
|||
|
|
" return np.clip(vals, med - n_sigma * mad, med + n_sigma * mad)\n",
|
|||
|
|
" else:\n",
|
|||
|
|
" return np.clip(vals, 0, np.nanpercentile(vals, 95))\n",
|
|||
|
|
"\n",
|
|||
|
|
"def add_months_since_last_tx(dfc, df_month, id_col, suffix=\"\"):\n",
|
|||
|
|
" \"\"\"Adds months_since_last_tx[suffix] to dfc.\"\"\"\n",
|
|||
|
|
" col_name = f\"months_since_last_tx{suffix}\"\n",
|
|||
|
|
" reference_date = df_month[\"month\"].max()\n",
|
|||
|
|
" last_active = (\n",
|
|||
|
|
" df_month[df_month[\"active_month\"] == 1]\n",
|
|||
|
|
" .groupby(id_col)[\"month\"]\n",
|
|||
|
|
" .max()\n",
|
|||
|
|
" .reset_index(name=\"last_active_month\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
" last_active[col_name] = (\n",
|
|||
|
|
" (reference_date.to_period(\"M\") -\n",
|
|||
|
|
" last_active[\"last_active_month\"].dt.to_period(\"M\"))\n",
|
|||
|
|
" .apply(lambda x: x.n)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" dfc = dfc.merge(last_active[[id_col, col_name]], on=id_col, how=\"left\")\n",
|
|||
|
|
" max_months = dfc[col_name].max()\n",
|
|||
|
|
" dfc[col_name] = dfc[col_name].fillna(max_months + 1)\n",
|
|||
|
|
" return dfc"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "312153e6",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 2. Data Loading\n",
|
|||
|
|
"\n",
|
|||
|
|
"Three data sources are used:\n",
|
|||
|
|
"- **AUM** (repaired): monthly share quantities per account and ISIN\n",
|
|||
|
|
"- **Flows**: daily net transactions, aggregated to monthly\n",
|
|||
|
|
"- **NAV / Rates**: fund performance and interest rate data for enrichment\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 4,
|
|||
|
|
"id": "011958df",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"flows: (2574461, 26)\n",
|
|||
|
|
"aum: (4824814, 19)\n",
|
|||
|
|
"nav: (623914, 6)\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"df_flows = pd.read_csv(\"flows.csv\", low_memory=False)\n",
|
|||
|
|
"df_aum = pd.read_csv(\n",
|
|||
|
|
" \"s3://projet-bdc-carmignac-g3/paco/AUM_repaired.csv\", low_memory=False\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_nav = pd.read_csv(PATH_NAV, sep=\";\")\n",
|
|||
|
|
"df_rates = pd.read_csv(PATH_RATES, sep=\";\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Date parsing\n",
|
|||
|
|
"for df, col in [\n",
|
|||
|
|
" (df_flows, FLOW_DATE_COL), (df_aum, AUM_DATE_COL),\n",
|
|||
|
|
" (df_nav, NAV_DATE_COL), (df_rates, RATE_DATE_COL)\n",
|
|||
|
|
"]:\n",
|
|||
|
|
" df[col] = pd.to_datetime(df[col], errors=\"coerce\")\n",
|
|||
|
|
" df[\"month\"] = df[col].dt.to_period(\"M\").dt.to_timestamp()\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [FLOW_QTY_COL, FLOW_SUB_COL, FLOW_RED_COL]:\n",
|
|||
|
|
" df_flows[col] = pd.to_numeric(df_flows[col], errors=\"coerce\")\n",
|
|||
|
|
"for col in [AUM_QTY_COL, AUM_VAL_COL]:\n",
|
|||
|
|
" df_aum[col] = pd.to_numeric(df_aum[col], errors=\"coerce\")\n",
|
|||
|
|
"for col in [NAV_PRICE_COL, NAV_BENCH_COL]:\n",
|
|||
|
|
" df_nav[col] = pd.to_numeric(df_nav[col], errors=\"coerce\")\n",
|
|||
|
|
"df_rates[RATE_VAL_COL] = pd.to_numeric(df_rates[RATE_VAL_COL], errors=\"coerce\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"for df in [df_flows, df_aum]:\n",
|
|||
|
|
" df[ISIN_COL] = df[ISIN_COL].astype(str).str.strip()\n",
|
|||
|
|
"df_nav[NAV_ISIN_COL] = df_nav[NAV_ISIN_COL].astype(str).str.strip()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Remove technical accounts (not investable)\n",
|
|||
|
|
"df_flows = df_flows[~df_flows[ID_COL].isin(\n",
|
|||
|
|
" [\"Off Distribution\", \"Private Clients\", \"Private Client\"]\n",
|
|||
|
|
")]\n",
|
|||
|
|
"df_aum = df_aum[~df_aum[ID_COL].isin(\n",
|
|||
|
|
" [\"Off Distribution\", \"Private Clients\", \"Private Client\"]\n",
|
|||
|
|
")]\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"flows:\", df_flows.shape)\n",
|
|||
|
|
"print(\"aum: \", df_aum.shape)\n",
|
|||
|
|
"print(\"nav: \", df_nav.shape)"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "d34f5ecf",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 3. Monthly Panel Construction\n",
|
|||
|
|
"\n",
|
|||
|
|
"A full outer join of AUM and flows at `(account, ISIN, month)` granularity, enriched with NAV returns and interest rate changes.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 5,
|
|||
|
|
"id": "25f3dce4",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Panel shape: (4754355, 24)\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"df_flows_m = (\n",
|
|||
|
|
" df_flows\n",
|
|||
|
|
" .dropna(subset=[ID_COL, ISIN_COL, \"month\"])\n",
|
|||
|
|
" .assign(\n",
|
|||
|
|
" gross_flow_qty = lambda x: x[FLOW_QTY_COL].abs(),\n",
|
|||
|
|
" sub_qty = lambda x: x[FLOW_SUB_COL].fillna(0),\n",
|
|||
|
|
" red_qty = lambda x: x[FLOW_RED_COL].fillna(0)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .groupby([ID_COL, ISIN_COL, \"month\"], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" net_flow_qty = (FLOW_QTY_COL, \"sum\"),\n",
|
|||
|
|
" gross_flow_qty = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" sub_qty = (\"sub_qty\", \"sum\"),\n",
|
|||
|
|
" red_qty = (\"red_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx = (FLOW_QTY_COL, \"size\"),\n",
|
|||
|
|
" region = (REGION_COL, \"last\"),\n",
|
|||
|
|
" country = (COUNTRY_COL, \"last\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_aum_m = (\n",
|
|||
|
|
" df_aum\n",
|
|||
|
|
" .dropna(subset=[ID_COL, ISIN_COL, \"month\"])\n",
|
|||
|
|
" .groupby([ID_COL, ISIN_COL, \"month\"], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" aum_qty = (AUM_QTY_COL, \"sum\"),\n",
|
|||
|
|
" aum_val = (AUM_VAL_COL, \"sum\"),\n",
|
|||
|
|
" fund = (FUND_COL, \"last\"),\n",
|
|||
|
|
" asset_type = (ASSET_COL, \"last\"),\n",
|
|||
|
|
" region = (REGION_COL, \"last\"),\n",
|
|||
|
|
" country = (COUNTRY_COL, \"last\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"keys = pd.concat([\n",
|
|||
|
|
" df_flows_m[[ID_COL, ISIN_COL, \"month\"]],\n",
|
|||
|
|
" df_aum_m[[ID_COL, ISIN_COL, \"month\"]]\n",
|
|||
|
|
"]).drop_duplicates()\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rel_m = (\n",
|
|||
|
|
" keys\n",
|
|||
|
|
" .merge(df_aum_m, on=[ID_COL, ISIN_COL, \"month\"], how=\"left\")\n",
|
|||
|
|
" .merge(df_flows_m, on=[ID_COL, ISIN_COL, \"month\"], how=\"left\",\n",
|
|||
|
|
" suffixes=(\"\", \"_flow\"))\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"for c in [\"aum_qty\",\"aum_val\",\"net_flow_qty\",\"gross_flow_qty\",\n",
|
|||
|
|
" \"sub_qty\",\"red_qty\",\"n_tx\"]:\n",
|
|||
|
|
" df_rel_m[c] = df_rel_m[c].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rel_m[\"region\"] = df_rel_m[\"region\"].fillna(df_rel_m.get(\"region_flow\"))\n",
|
|||
|
|
"df_rel_m[\"country\"] = df_rel_m[\"country\"].fillna(df_rel_m.get(\"country_flow\"))\n",
|
|||
|
|
"df_rel_m[\"active_rel_month\"] = (df_rel_m[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"df_rel_m[\"holding_rel_month\"] = (df_rel_m[\"aum_qty\"] > 0).astype(int)\n",
|
|||
|
|
"df_rel_m[\"flow_to_aum_rel\"] = df_rel_m[\"net_flow_qty\"] / (df_rel_m[\"aum_qty\"].abs() + EPS)\n",
|
|||
|
|
"df_rel_m[\"turnover_rel\"] = df_rel_m[\"gross_flow_qty\"] / (df_rel_m[\"aum_qty\"].abs() + EPS)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# --- NAV returns & interest rates ---\n",
|
|||
|
|
"df_nav_m = (\n",
|
|||
|
|
" df_nav\n",
|
|||
|
|
" .dropna(subset=[NAV_ISIN_COL, \"month\", NAV_PRICE_COL])\n",
|
|||
|
|
" .sort_values([NAV_ISIN_COL, \"month\"])\n",
|
|||
|
|
" .groupby([NAV_ISIN_COL, \"month\"], as_index=False).tail(1).copy()\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_nav_m[\"ret_fund_m\"] = df_nav_m.groupby(NAV_ISIN_COL)[NAV_PRICE_COL].pct_change()\n",
|
|||
|
|
"df_nav_m[\"ret_bench_m\"] = df_nav_m.groupby(NAV_ISIN_COL)[NAV_BENCH_COL].pct_change()\n",
|
|||
|
|
"df_nav_m[\"active_return_m\"] = df_nav_m[\"ret_fund_m\"] - df_nav_m[\"ret_bench_m\"]\n",
|
|||
|
|
"df_nav_m = df_nav_m.rename(columns={NAV_ISIN_COL: ISIN_COL})[\n",
|
|||
|
|
" [ISIN_COL, \"month\", \"ret_fund_m\", \"ret_bench_m\", \"active_return_m\"]\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rates_m = (\n",
|
|||
|
|
" df_rates.dropna(subset=[\"month\", RATE_VAL_COL])\n",
|
|||
|
|
" .sort_values(RATE_DATE_COL)\n",
|
|||
|
|
" .groupby(\"month\", as_index=False).tail(1).copy()\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_rates_m[\"delta_rate_m\"] = df_rates_m[RATE_VAL_COL].diff()\n",
|
|||
|
|
"df_rates_m = df_rates_m[[\"month\", RATE_VAL_COL, \"delta_rate_m\"]]\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rel_m = df_rel_m.merge(df_nav_m, on=[ISIN_COL, \"month\"], how=\"left\")\n",
|
|||
|
|
"df_rel_m = df_rel_m.merge(\n",
|
|||
|
|
" df_rates_m[[\"month\", \"delta_rate_m\"]], on=\"month\", how=\"left\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"for c in [\"ret_fund_m\",\"ret_bench_m\",\"active_return_m\",\"delta_rate_m\"]:\n",
|
|||
|
|
" df_rel_m[c] = df_rel_m[c].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"Panel shape:\", df_rel_m.shape)"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "9121da21",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 4. Feature Engineering\n",
|
|||
|
|
"\n",
|
|||
|
|
"Features are built at three levels of granularity:\n",
|
|||
|
|
"- **Account × month**: activity flags, turnover, drawdown\n",
|
|||
|
|
"- **Account × ISIN**: entry/exit events, holding duration, performance reactivity\n",
|
|||
|
|
"- **Account (static)**: aggregated behavioral summary used for clustering\n",
|
|||
|
|
"\n",
|
|||
|
|
"Asset type and fund composition shares are computed separately and used as **descriptive** post-clustering variables only.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 6,
|
|||
|
|
"id": "d4a01bcc",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Monthly panel shape: (931089, 22)\n",
|
|||
|
|
"ISIN-level client features: (12582, 12)\n",
|
|||
|
|
"Asset shares: (7473, 6)\n",
|
|||
|
|
"Fund shares: (6591, 11)\n",
|
|||
|
|
"df_client_base shape: (12582, 47)\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# 4a. Monthly account-level panel\n",
|
|||
|
|
"tmp = df_rel_m.copy()\n",
|
|||
|
|
"tmp[\"isin_held_flag\"] = (tmp[\"aum_qty\"] > 0).astype(int)\n",
|
|||
|
|
"tmp[\"isin_active_flag\"] = (tmp[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month = (\n",
|
|||
|
|
" tmp.groupby([ID_COL, \"month\"], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" aum_qty = (\"aum_qty\", \"sum\"),\n",
|
|||
|
|
" aum_val = (\"aum_val\", \"sum\"),\n",
|
|||
|
|
" net_flow_qty = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" gross_flow_qty = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" sub_qty = (\"sub_qty\", \"sum\"),\n",
|
|||
|
|
" red_qty = (\"red_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" n_isin_held = (\"isin_held_flag\", \"sum\"),\n",
|
|||
|
|
" n_isin_active = (\"isin_active_flag\", \"sum\"),\n",
|
|||
|
|
" delta_rate_m = (\"delta_rate_m\", \"first\"),\n",
|
|||
|
|
" ret_fund_m = (\"ret_fund_m\", \"mean\"),\n",
|
|||
|
|
" region = (\"region\", \"first\"),\n",
|
|||
|
|
" country = (\"country\", \"first\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .sort_values([ID_COL, \"month\"])\n",
|
|||
|
|
" .reset_index(drop=True)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month[\"active_month\"] = (df_month[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"df_month[\"flow_to_aum_m\"] = np.where(\n",
|
|||
|
|
" df_month[\"aum_qty\"].abs() > 0,\n",
|
|||
|
|
" df_month[\"net_flow_qty\"] / df_month[\"aum_qty\"].abs(), np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month[\"turnover_m\"] = np.where(\n",
|
|||
|
|
" df_month[\"aum_qty\"].abs() > 0,\n",
|
|||
|
|
" df_month[\"gross_flow_qty\"] / df_month[\"aum_qty\"].abs(), np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month[\"sub_share_m\"] = np.where(\n",
|
|||
|
|
" df_month[\"gross_flow_qty\"] > 0,\n",
|
|||
|
|
" df_month[\"sub_qty\"] / df_month[\"gross_flow_qty\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month[\"red_share_m\"] = np.where(\n",
|
|||
|
|
" df_month[\"gross_flow_qty\"] > 0,\n",
|
|||
|
|
" df_month[\"red_qty\"] / df_month[\"gross_flow_qty\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month[\"aum_peak_to_date\"] = df_month.groupby(ID_COL)[\"aum_qty\"].cummax()\n",
|
|||
|
|
"df_month[\"aum_drawdown\"] = np.where(\n",
|
|||
|
|
" df_month[\"aum_peak_to_date\"] > 0,\n",
|
|||
|
|
" 1 - df_month[\"aum_qty\"] / df_month[\"aum_peak_to_date\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"Monthly panel shape:\", df_month.shape)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 4b. ISIN-level features (entry/exit, performance reactivity)\n",
|
|||
|
|
"tmp = df_rel_m.sort_values([ID_COL, ISIN_COL, \"month\"]).copy()\n",
|
|||
|
|
"tmp[\"prev_aum\"] = tmp.groupby([ID_COL, ISIN_COL])[\"aum_qty\"].shift(1)\n",
|
|||
|
|
"tmp[\"entry_event\"] = ((tmp[\"prev_aum\"].fillna(0) <= 0) & (tmp[\"aum_qty\"] > 0)).astype(int)\n",
|
|||
|
|
"tmp[\"full_exit_event\"] = ((tmp[\"prev_aum\"] > 0) & (tmp[\"aum_qty\"] <= 0)).astype(int)\n",
|
|||
|
|
"tmp[\"ret_fund_m_lag1\"] = tmp.groupby([ID_COL, ISIN_COL])[\"ret_fund_m\"].shift(1)\n",
|
|||
|
|
"tmp[\"buy_on_perf\"] = ((tmp[\"net_flow_qty\"] > 0) & (tmp[\"ret_fund_m_lag1\"] > 0)).astype(int)\n",
|
|||
|
|
"tmp[\"sell_on_perf\"] = ((tmp[\"net_flow_qty\"] < 0) & (tmp[\"ret_fund_m_lag1\"] < 0)).astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rel_feat = (\n",
|
|||
|
|
" tmp.groupby([ID_COL, ISIN_COL], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" rel_n_months = (\"month\", \"nunique\"),\n",
|
|||
|
|
" rel_active_months = (\"active_rel_month\", \"sum\"),\n",
|
|||
|
|
" rel_holding_months = (\"holding_rel_month\", \"sum\"),\n",
|
|||
|
|
" rel_aum_mean = (\"aum_qty\", \"mean\"),\n",
|
|||
|
|
" rel_turnover_mean = (\"turnover_rel\", \"mean\"),\n",
|
|||
|
|
" rel_turnover_vol = (\"turnover_rel\", \"std\"),\n",
|
|||
|
|
" rel_flow_to_aum_vol = (\"flow_to_aum_rel\", \"std\"),\n",
|
|||
|
|
" rel_n_tx = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" rel_full_exit_count = (\"full_exit_event\", \"sum\"),\n",
|
|||
|
|
" rel_entry_count = (\"entry_event\", \"sum\"),\n",
|
|||
|
|
" buy_on_perf_rate = (\"buy_on_perf\", \"mean\"),\n",
|
|||
|
|
" sell_on_perf_rate = (\"sell_on_perf\", \"mean\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"isin_aum = df_rel_feat.groupby(ID_COL)[\"rel_aum_mean\"].transform(\"sum\")\n",
|
|||
|
|
"df_rel_feat[\"isin_weight\"] = np.where(\n",
|
|||
|
|
" isin_aum > 0, df_rel_feat[\"rel_aum_mean\"] / isin_aum, np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"hhi_isin = (\n",
|
|||
|
|
" df_rel_feat.groupby(ID_COL)[\"isin_weight\"]\n",
|
|||
|
|
" .apply(lambda w: np.sum(w**2))\n",
|
|||
|
|
" .reset_index(name=\"hhi_isin\")\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_rel_client = (\n",
|
|||
|
|
" df_rel_feat.groupby(ID_COL, as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_isin_total = (ISIN_COL, \"nunique\"),\n",
|
|||
|
|
" rel_turnover_mean_avg = (\"rel_turnover_mean\", \"mean\"),\n",
|
|||
|
|
" rel_turnover_vol_avg = (\"rel_turnover_vol\", \"mean\"),\n",
|
|||
|
|
" rel_flow_to_aum_vol_avg = (\"rel_flow_to_aum_vol\", \"mean\"), \n",
|
|||
|
|
" full_exit_count = (\"rel_full_exit_count\", \"sum\"),\n",
|
|||
|
|
" entry_count = (\"rel_entry_count\", \"sum\"),\n",
|
|||
|
|
" avg_holding_months_per_isin = (\"rel_holding_months\", \"mean\"),\n",
|
|||
|
|
" max_holding_months_per_isin = (\"rel_holding_months\", \"max\"),\n",
|
|||
|
|
" buy_on_perf_rate_avg = (\"buy_on_perf_rate\", \"mean\"),\n",
|
|||
|
|
" sell_on_perf_rate_avg = (\"sell_on_perf_rate\", \"mean\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .merge(hhi_isin, on=ID_COL, how=\"left\")\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"ISIN-level client features:\", df_rel_client.shape)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 4c. Asset type & fund composition shares\n",
|
|||
|
|
"aum_by_asset = (\n",
|
|||
|
|
" df_aum.dropna(subset=[ID_COL, ASSET_COL])\n",
|
|||
|
|
" .groupby([ID_COL, ASSET_COL], as_index=False)[AUM_VAL_COL].sum()\n",
|
|||
|
|
")\n",
|
|||
|
|
"total_aum_acc = aum_by_asset.groupby(ID_COL)[AUM_VAL_COL].sum().rename(\"total_aum\")\n",
|
|||
|
|
"aum_by_asset = aum_by_asset.merge(total_aum_acc, on=ID_COL)\n",
|
|||
|
|
"aum_by_asset[\"share\"] = np.where(\n",
|
|||
|
|
" aum_by_asset[\"total_aum\"] > 0,\n",
|
|||
|
|
" aum_by_asset[AUM_VAL_COL] / aum_by_asset[\"total_aum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"asset_shares = (\n",
|
|||
|
|
" aum_by_asset\n",
|
|||
|
|
" .pivot_table(index=ID_COL, columns=ASSET_COL, values=\"share\", aggfunc=\"mean\")\n",
|
|||
|
|
" .fillna(0).reset_index()\n",
|
|||
|
|
")\n",
|
|||
|
|
"asset_shares.columns = [ID_COL] + [\n",
|
|||
|
|
" f\"share_asset_{c.lower().replace(' ','_')}\" for c in asset_shares.columns[1:]\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"aum_by_fund = (\n",
|
|||
|
|
" df_aum.dropna(subset=[ID_COL, FUND_COL])\n",
|
|||
|
|
" .groupby([ID_COL, FUND_COL], as_index=False)[AUM_VAL_COL].sum()\n",
|
|||
|
|
")\n",
|
|||
|
|
"aum_by_fund = aum_by_fund.merge(total_aum_acc, on=ID_COL)\n",
|
|||
|
|
"aum_by_fund[\"share\"] = np.where(\n",
|
|||
|
|
" aum_by_fund[\"total_aum\"] > 0,\n",
|
|||
|
|
" aum_by_fund[AUM_VAL_COL] / aum_by_fund[\"total_aum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"top_funds = aum_by_fund.groupby(FUND_COL)[AUM_VAL_COL].sum().nlargest(10).index\n",
|
|||
|
|
"fund_shares = (\n",
|
|||
|
|
" aum_by_fund[aum_by_fund[FUND_COL].isin(top_funds)]\n",
|
|||
|
|
" .pivot_table(index=ID_COL, columns=FUND_COL, values=\"share\", aggfunc=\"mean\")\n",
|
|||
|
|
" .fillna(0).reset_index()\n",
|
|||
|
|
")\n",
|
|||
|
|
"fund_shares.columns = [ID_COL] + [\n",
|
|||
|
|
" f\"share_fund_{c.lower().replace(' ','_')[:30]}\" for c in fund_shares.columns[1:]\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"Asset shares:\", asset_shares.shape)\n",
|
|||
|
|
"print(\"Fund shares: \", fund_shares.shape)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 4d. Static client-level features\n",
|
|||
|
|
"df_client_base = (\n",
|
|||
|
|
" df_month.groupby(ID_COL, as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_months = (\"month\", \"nunique\"),\n",
|
|||
|
|
" n_active_months = (\"active_month\", \"sum\"),\n",
|
|||
|
|
" flow_freq = (\"active_month\", \"mean\"),\n",
|
|||
|
|
" aum_qty_mean = (\"aum_qty\", \"mean\"),\n",
|
|||
|
|
" aum_qty_median = (\"aum_qty\", \"median\"),\n",
|
|||
|
|
" aum_qty_max = (\"aum_qty\", \"max\"),\n",
|
|||
|
|
" aum_qty_last = (\"aum_qty\", \"last\"),\n",
|
|||
|
|
" net_flow_qty_sum = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" gross_flow_qty_sum = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" gross_flow_qty_mean= (\"gross_flow_qty\", \"mean\"),\n",
|
|||
|
|
" sub_qty_sum = (\"sub_qty\", \"sum\"),\n",
|
|||
|
|
" red_qty_sum = (\"red_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx_total = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" avg_n_isin_held = (\"n_isin_held\", \"mean\"),\n",
|
|||
|
|
" max_n_isin_held = (\"n_isin_held\", \"max\"),\n",
|
|||
|
|
" net_flow_qty_vol = (\"net_flow_qty\", \"std\"),\n",
|
|||
|
|
" aum_drawdown_last = (\"aum_drawdown\", \"last\"),\n",
|
|||
|
|
" aum_drawdown_max = (\"aum_drawdown\", \"max\"),\n",
|
|||
|
|
" region = (\"region\", \"last\"),\n",
|
|||
|
|
" country = (\"country\", \"last\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_base[\"net_flow_qty_vol\"] = df_client_base[\"net_flow_qty_vol\"].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_client_base = (\n",
|
|||
|
|
" df_client_base\n",
|
|||
|
|
" .merge(df_rel_client, on=ID_COL, how=\"left\")\n",
|
|||
|
|
" .merge(asset_shares, on=ID_COL, how=\"left\")\n",
|
|||
|
|
" .merge(fund_shares, on=ID_COL, how=\"left\")\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"df_client_base shape:\", df_client_base.shape)"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "c383042d",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 5. Part 1 — Global Clustering (All Accounts)\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Objective\n",
|
|||
|
|
"Segment the full client base into behavioral profiles using 8 carefully selected features. The analysis covers ~7,000 accounts with at least 6 months of history.\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Feature set\n",
|
|||
|
|
"| Feature | Description |\n",
|
|||
|
|
"|---|---|\n",
|
|||
|
|
"| `flow_freq` | Proportion of months with at least one transaction |\n",
|
|||
|
|
"| `gross_flow_to_aum` | Total gross flows relative to mean AUM (clipped p90, log-transformed) |\n",
|
|||
|
|
"| `n_isin_total` | Total number of distinct ISINs held over the period |\n",
|
|||
|
|
"| `avg_holding_months_per_isin` | Average holding duration per ISIN |\n",
|
|||
|
|
"| `exit_rate_per_isin` | Average number of full exits per ISIN |\n",
|
|||
|
|
"| `flow_direction_balance` | Ratio of net to gross flows (buyer vs seller signal) |\n",
|
|||
|
|
"| `log_aum_qty_mean` | Log mean AUM — only size variable retained |\n",
|
|||
|
|
"| `months_since_last_tx` | Months since last transaction (recency signal, most discriminant feature) |\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Preprocessing\n",
|
|||
|
|
"- MAD winsorization (3σ) for long-tailed distributions\n",
|
|||
|
|
"- Clip p90 + log-transform for `gross_flow_to_aum` and `flow_freq`\n",
|
|||
|
|
"- RobustScaler before K-means\n",
|
|||
|
|
"- Geographic and allocation variables excluded from clustering (used post-hoc as descriptors)\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": null,
|
|||
|
|
"id": "0d8b7276-8213-4667-979c-d97b3729162a",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": [
|
|||
|
|
"# 2f. Engineered ratios\n",
|
|||
|
|
"dfc = df_client_base.copy()\n",
|
|||
|
|
"dfc[\"log_aum_qty_mean\"] = np.log1p(dfc[\"aum_qty_mean\"].clip(lower=0))\n",
|
|||
|
|
"dfc[\"gross_flow_to_aum\"] = np.where(\n",
|
|||
|
|
" dfc[\"aum_qty_mean\"] > 1,\n",
|
|||
|
|
" dfc[\"gross_flow_qty_sum\"] / dfc[\"aum_qty_mean\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"flow_direction_balance\"] = np.where(\n",
|
|||
|
|
" dfc[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" dfc[\"net_flow_qty_sum\"] / dfc[\"gross_flow_qty_sum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"sub_share_mean\"] = np.where(\n",
|
|||
|
|
" dfc[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" dfc[\"sub_qty_sum\"] / dfc[\"gross_flow_qty_sum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"redemption_bias\"] = np.where(\n",
|
|||
|
|
" dfc[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" (dfc[\"red_qty_sum\"] - dfc[\"sub_qty_sum\"]) / dfc[\"gross_flow_qty_sum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"exit_rate_per_isin\"] = np.where(\n",
|
|||
|
|
" dfc[\"n_isin_total\"] > 0,\n",
|
|||
|
|
" dfc[\"full_exit_count\"] / dfc[\"n_isin_total\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"aum_final_to_peak\"] = np.where(\n",
|
|||
|
|
" dfc[\"aum_qty_max\"] > 0,\n",
|
|||
|
|
" dfc[\"aum_qty_last\"] / dfc[\"aum_qty_max\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc[\"aum_drawdown_last\"] = dfc[\"aum_drawdown_last\"].clip(0, 1)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"aum_qty_mean\", \"gross_flow_qty_sum\", \"n_tx_total\"]:\n",
|
|||
|
|
" dfc[f\"log_{col}\"] = np.log1p(dfc[col].clip(lower=0))\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Filtres qualité\n",
|
|||
|
|
"dfc = dfc[(dfc[\"n_months\"] >= 6) & (dfc[\"aum_qty_mean\"] > 0)].copy()\n",
|
|||
|
|
"for col in [\"aum_qty_mean\", \"gross_flow_qty_sum\", \"n_tx_total\"]:\n",
|
|||
|
|
" cap = dfc[col].quantile(0.99)\n",
|
|||
|
|
" dfc = dfc[dfc[col] <= cap].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Géographie\n",
|
|||
|
|
"top_countries = dfc[\"country\"].fillna(\"Unknown\").value_counts().head(10).index\n",
|
|||
|
|
"top_regions = dfc[\"region\"].fillna(\"Unknown\").value_counts().head(10).index\n",
|
|||
|
|
"dfc[\"country_grp\"] = np.where(dfc[\"country\"].isin(top_countries), dfc[\"country\"], \"Other\")\n",
|
|||
|
|
"dfc[\"region_grp\"] = np.where(dfc[\"region\"].isin(top_regions), dfc[\"region\"], \"Other\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# months_since_last_tx\n",
|
|||
|
|
"dfc = add_months_since_last_tx(dfc, df_month, ID_COL)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(f\"Accounts after quality filters: {len(dfc)}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 5a. Feature selection & preprocessing\n",
|
|||
|
|
"base_features_global = [\n",
|
|||
|
|
" \"flow_freq\",\n",
|
|||
|
|
" \"gross_flow_to_aum\",\n",
|
|||
|
|
" \"n_isin_total\",\n",
|
|||
|
|
" \"avg_holding_months_per_isin\",\n",
|
|||
|
|
" \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"flow_direction_balance\",\n",
|
|||
|
|
" \"log_aum_qty_mean\",\n",
|
|||
|
|
" \"months_since_last_tx\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"all_features_global = [c for c in base_features_global if c in dfc.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
"dfc_clean = dfc.copy() # working copy for preprocessing\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"flow_direction_balance\", \"months_since_last_tx\"]:\n",
|
|||
|
|
" if col in dfc_clean.columns:\n",
|
|||
|
|
" dfc_clean[col] = dfc_clean[col].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"n_isin_total\", \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"avg_holding_months_per_isin\", \"months_since_last_tx\"]:\n",
|
|||
|
|
" if col in dfc_clean.columns:\n",
|
|||
|
|
" dfc_clean[col] = winsorize_mad(dfc_clean[col], n_sigma=3)\n",
|
|||
|
|
"\n",
|
|||
|
|
"col = \"gross_flow_to_aum\"\n",
|
|||
|
|
"if col in dfc_clean.columns:\n",
|
|||
|
|
" vals = dfc_clean[col].to_numpy(dtype=float)\n",
|
|||
|
|
" vals = np.clip(vals, 0, np.nanpercentile(vals, 90))\n",
|
|||
|
|
" dfc_clean[col] = np.log1p(vals)\n",
|
|||
|
|
"\n",
|
|||
|
|
"col = \"flow_freq\"\n",
|
|||
|
|
"if col in dfc_clean.columns:\n",
|
|||
|
|
" vals = dfc_clean[col].to_numpy(dtype=float)\n",
|
|||
|
|
" dfc_clean[col] = np.log1p(np.clip(vals, 0, None))\n",
|
|||
|
|
"\n",
|
|||
|
|
"col = \"log_aum_qty_mean\"\n",
|
|||
|
|
"if col in dfc_clean.columns:\n",
|
|||
|
|
" dfc_clean[col] = winsorize_mad(dfc_clean[col], n_sigma=3)\n",
|
|||
|
|
"\n",
|
|||
|
|
"X_global = dfc_clean[all_features_global].copy()\n",
|
|||
|
|
"X_global = X_global.loc[:, ~X_global.columns.duplicated()]\n",
|
|||
|
|
"X_global = X_global.fillna(X_global.median())\n",
|
|||
|
|
"\n",
|
|||
|
|
"scaler_global = RobustScaler()\n",
|
|||
|
|
"X_global_scaled = scaler_global.fit_transform(X_global)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Diagnostic\n",
|
|||
|
|
"X_df = pd.DataFrame(X_global_scaled, columns=X_global.columns)\n",
|
|||
|
|
"extreme = (X_df.abs() > 5).any(axis=1).sum()\n",
|
|||
|
|
"print(f\"Accounts: {X_global.shape[0]} | Features: {X_global.shape[1]}\")\n",
|
|||
|
|
"print(f\"Points > 5 std after scaling: {extreme} ({extreme/len(X_df):.1%})\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 5b. K-selection\n",
|
|||
|
|
"rows = []\n",
|
|||
|
|
"for k in range(2, 8):\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=50, random_state=RANDOM_STATE)\n",
|
|||
|
|
" labels = km.fit_predict(X_global_scaled)\n",
|
|||
|
|
" rows.append({\n",
|
|||
|
|
" \"k\": k, \"inertia\": km.inertia_,\n",
|
|||
|
|
" \"silhouette\": silhouette_score(X_global_scaled, labels),\n",
|
|||
|
|
" \"davies_bouldin\": davies_bouldin_score(X_global_scaled, labels),\n",
|
|||
|
|
" })\n",
|
|||
|
|
"df_kdiag_global = pd.DataFrame(rows)\n",
|
|||
|
|
"print(df_kdiag_global.to_string(index=False))\n",
|
|||
|
|
"\n",
|
|||
|
|
"fig, axes = plt.subplots(1, 3, figsize=(15, 4))\n",
|
|||
|
|
"for ax, col, title in zip(axes,\n",
|
|||
|
|
" [\"inertia\", \"silhouette\", \"davies_bouldin\"],\n",
|
|||
|
|
" [\"Elbow / Inertia\", \"Silhouette (higher=better)\", \"Davies-Bouldin (lower=better)\"]):\n",
|
|||
|
|
" ax.plot(df_kdiag_global[\"k\"], df_kdiag_global[col], marker=\"o\")\n",
|
|||
|
|
" ax.set_title(title); ax.set_xlabel(\"K\")\n",
|
|||
|
|
"plt.suptitle(\"K-selection — Global Clustering\")\n",
|
|||
|
|
"plt.tight_layout(); plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 5c. Final clustering K=4\n",
|
|||
|
|
"RESULTS_GLOBAL = {}\n",
|
|||
|
|
"for k in [4]:\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=50, random_state=RANDOM_STATE)\n",
|
|||
|
|
" dfc[f\"cluster_k{k}\"] = km.fit_predict(X_global_scaled)\n",
|
|||
|
|
" RESULTS_GLOBAL[k] = {\n",
|
|||
|
|
" \"model\": km,\n",
|
|||
|
|
" \"silhouette\": silhouette_score(X_global_scaled, dfc[f\"cluster_k{k}\"]),\n",
|
|||
|
|
" \"davies_bouldin\": davies_bouldin_score(X_global_scaled, dfc[f\"cluster_k{k}\"]),\n",
|
|||
|
|
" }\n",
|
|||
|
|
" print(f\"K={k} | sil={RESULTS_GLOBAL[k]['silhouette']:.4f} \"\n",
|
|||
|
|
" f\"| db={RESULTS_GLOBAL[k]['davies_bouldin']:.4f}\")\n",
|
|||
|
|
" counts = dfc[f\"cluster_k{k}\"].value_counts().sort_index()\n",
|
|||
|
|
" props = counts / counts.sum() * 100\n",
|
|||
|
|
" print(pd.DataFrame({\"n_comptes\": counts, \"pct\": props.round(1)}))"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 13,
|
|||
|
|
"id": "1c0ea35a",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Median behavioral features — K=4 ===\n",
|
|||
|
|
" gross_flow_to_aum flow_freq flow_direction_balance n_isin_total avg_holding_months_per_isin exit_rate_per_isin log_aum_qty_mean months_since_last_tx\n",
|
|||
|
|
"cluster_k4 \n",
|
|||
|
|
"0 1.159 0.043 -1.000 3.0 60.000 0.400 5.167 27.0\n",
|
|||
|
|
"1 1.476 0.012 -1.000 3.0 12.000 0.714 3.408 127.0\n",
|
|||
|
|
"2 5.351 0.617 -0.006 12.0 28.897 0.667 8.763 3.0\n",
|
|||
|
|
"3 7.889 0.071 0.000 1.0 11.333 1.000 5.280 69.0\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1200x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Median allocation — K=4 ===\n",
|
|||
|
|
" share_asset_fixed_income share_asset_diversified share_asset_equity share_fund_carmignac_patrimoine share_fund_carmignac_investissement share_fund_carmignac_sécurité share_fund_carmignac_emergents\n",
|
|||
|
|
"cluster_k4 \n",
|
|||
|
|
"0 0.000 0.373 0.227 0.260 0.000 0.000 0.000\n",
|
|||
|
|
"1 0.000 0.326 0.099 0.156 0.000 0.000 0.000\n",
|
|||
|
|
"2 0.284 0.207 0.154 0.149 0.011 0.017 0.002\n",
|
|||
|
|
"3 0.768 0.000 0.000 0.000 0.000 0.000 0.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"=== Distribution géographique per cluster ===\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABrcAAAGGCAYAAADRitpgAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XVcFOkfB/APICBISAsIiAUqILaYZyt2d3fn2YeF2I2JqIjtIZ6tZ3d3/mwMpJVmgd3fH5yLK+GCLLvLft738vW6nZ0ZvsMOM/PZ55ln1EQikQhERERERERERERERERESkBd3gUQERERERERERERERERSYuNW0RERERERERERERERKQ02LhFRERERERERERERERESoONW0RERERERERERERERKQ02LhFRERERERERERERERESoONW0RERERERERERERERKQ02LhFRERERERERERERERESoONW0RERERERERERERERKQ02LhFRERERERERERERERESoONW0RE/zlw4AAcHBzw8eNH8bTevXujd+/e+fLzHRwcsGbNGvHrNWvWwMHBAZGRkfny8xs2bIipU6fmy8/KjI+PD5o3bw6hUCi3GhTFx48f4eDggAMHDsi7lFwZP348xo4dK+8yiIiIiCgH8jP7SIP5jPlMUTCfEREpJjZuERVwQUFB8PDwQKNGjeDs7IzKlSujW7du8PPzQ2JiorzLAwDs3LlTaS8SM3P37l2sWbMG0dHR8i4lA0WtLTY2Fps3b8bgwYOhrp52ahKJRPD29kbdunXh5uaG+fPnQyAQSCwXFxeHunXr4vDhw/IoW2m9evUKa9askfiiIC8NHjwYp06dwvPnz2WyfiIiIqKC6Htjzvd/5cuXR926dTF16lSEhITIuzylpagZCFDc2pjP8hfzGRFR7hSSdwFEJDvnz5/H2LFjoaWlhbZt26Js2bJITk7GnTt3sGTJErx69Qrz5s2Td5nYvXs3jIyM0KFDB3mXkoGvr2+Ol7l37x68vb3Rvn17GBgYSL3cw4cPoaGhkeOflxPZ1XbixAmoqanJ9Odn5e+//0ZKSgpatWolnnbo0CFs2LABgwcPho6ODjZs2ABTU1MMHTpUPM+GDRtgbW2N1q1by6NspfXq1St4e3ujevXqKF68eJ6vv3z58nBycsKWLVuwePHiPF8/ERERUUE2ZswYFC9e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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1800x400 with 4 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# 5d. Cluster profiles\n",
|
|||
|
|
"profile_vars_behavior = [\n",
|
|||
|
|
" \"gross_flow_to_aum\", \"flow_freq\", \"flow_direction_balance\",\n",
|
|||
|
|
" \"n_isin_total\", \"avg_holding_months_per_isin\", \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"log_aum_qty_mean\", \"months_since_last_tx\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"profile_vars_behavior = [c for c in profile_vars_behavior if c in dfc.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
"prof_behavior = plot_heatmap(\n",
|
|||
|
|
" dfc, profile_vars_behavior, \"cluster_k4\",\n",
|
|||
|
|
" title=\"Cluster Signatures — Behavioral Features (K=4, robust z-score)\",\n",
|
|||
|
|
" figsize=(14, 4)\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(\"\\n=== Median behavioral features — K=4 ===\")\n",
|
|||
|
|
"print(prof_behavior.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"profile_vars_allocation = [\n",
|
|||
|
|
" c for c in [\n",
|
|||
|
|
" \"share_asset_fixed_income\", \"share_asset_diversified\",\n",
|
|||
|
|
" \"share_asset_equity\", \"share_fund_carmignac_patrimoine\",\n",
|
|||
|
|
" \"share_fund_carmignac_investissement\", \"share_fund_carmignac_sécurité\",\n",
|
|||
|
|
" \"share_fund_carmignac_emergents\",\n",
|
|||
|
|
" ] if c in dfc.columns\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"prof_allocation = plot_heatmap(\n",
|
|||
|
|
" dfc, profile_vars_allocation, \"cluster_k4\",\n",
|
|||
|
|
" title=\"Cluster signatures — Allocation produits (K=4, descriptif)\",\n",
|
|||
|
|
" figsize=(12, 4)\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(\"\\n=== Median allocation — K=4 ===\")\n",
|
|||
|
|
"print(prof_allocation.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 5e. Geographic description (post-clustering)\n",
|
|||
|
|
"print(\"\\n=== Distribution géographique per cluster ===\")\n",
|
|||
|
|
"geo_country = pd.crosstab(\n",
|
|||
|
|
" dfc[\"cluster_k4\"], dfc[\"country_grp\"].fillna(\"Unknown\"),\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
").round(3) * 100\n",
|
|||
|
|
"geo_region = pd.crosstab(\n",
|
|||
|
|
" dfc[\"cluster_k4\"], dfc[\"region_grp\"].fillna(\"Unknown\"),\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
").round(3) * 100\n",
|
|||
|
|
"\n",
|
|||
|
|
"fig, axes = plt.subplots(1, 2, figsize=(18, 4))\n",
|
|||
|
|
"sns.heatmap(geo_country, cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
|
|||
|
|
" ax=axes[0], cbar_kws={\"label\": \"%\"})\n",
|
|||
|
|
"axes[0].set_title(\"Country distribution (% per cluster)\")\n",
|
|||
|
|
"sns.heatmap(geo_region, cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
|
|||
|
|
" ax=axes[1], cbar_kws={\"label\": \"%\"})\n",
|
|||
|
|
"axes[1].set_title(\"Region distribution (% per cluster)\")\n",
|
|||
|
|
"plt.tight_layout(); plt.show()"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 19,
|
|||
|
|
"id": "bea76665-7a28-44ac-80a3-e32c595ff630",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"=== Asset types available ===\n",
|
|||
|
|
"Product - Asset Type\n",
|
|||
|
|
"Equity 2002728\n",
|
|||
|
|
"Diversified 1365811\n",
|
|||
|
|
"Fixed Income 933096\n",
|
|||
|
|
"Alternative 210440\n",
|
|||
|
|
"Private Assets 118\n",
|
|||
|
|
"Name: count, dtype: int64\n",
|
|||
|
|
"\n",
|
|||
|
|
"Accounts per asset type:\n",
|
|||
|
|
"Product - Asset Type\n",
|
|||
|
|
"Diversified 4159\n",
|
|||
|
|
"Fixed Income 3932\n",
|
|||
|
|
"Equity 3899\n",
|
|||
|
|
"Alternative 1317\n",
|
|||
|
|
"Private Assets 11\n",
|
|||
|
|
"Name: Registrar Account - ID, dtype: int64\n",
|
|||
|
|
"\n",
|
|||
|
|
"Retained asset types (>= 50 accounts): ['Alternative', 'Diversified', 'Equity', 'Fixed Income']\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"ASSET TYPE: Alternative\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin\n",
|
|||
|
|
" 2 0.4577 0.9931\n",
|
|||
|
|
" 3 0.3432 1.1315\n",
|
|||
|
|
" 4 0.2579 1.3841\n",
|
|||
|
|
" 5 0.2823 1.2409\n",
|
|||
|
|
" 6 0.2644 1.3500\n",
|
|||
|
|
"→ Retained K: 2 (silhouette=0.4577)\n",
|
|||
|
|
" n_accounts pct\n",
|
|||
|
|
"cluster_alternative \n",
|
|||
|
|
"0 310 23.5\n",
|
|||
|
|
"1 1007 76.5\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Alternative:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_asset aum_final_to_peak aum_drawdown_last\n",
|
|||
|
|
"cluster_alternative \n",
|
|||
|
|
"0 0.085 1.039 1.000 0.104 5.776 12.0 0.915 0.085\n",
|
|||
|
|
"1 0.069 4.730 0.512 -0.072 5.063 66.0 0.000 1.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"ASSET TYPE: Diversified\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin\n",
|
|||
|
|
" 2 0.6037 0.6502\n",
|
|||
|
|
" 3 0.5111 0.8181\n",
|
|||
|
|
" 4 0.4851 0.9788\n",
|
|||
|
|
" 5 0.4695 0.8712\n",
|
|||
|
|
" 6 0.3429 1.1031\n",
|
|||
|
|
"→ Retained K: 2 (silhouette=0.6037)\n",
|
|||
|
|
" n_accounts pct\n",
|
|||
|
|
"cluster_diversified \n",
|
|||
|
|
"0 3369 81.0\n",
|
|||
|
|
"1 790 19.0\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Diversified:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_asset aum_final_to_peak aum_drawdown_last\n",
|
|||
|
|
"cluster_diversified \n",
|
|||
|
|
"0 0.044 3.042 0.625 -0.578 5.063 80.0 0.000 1.000\n",
|
|||
|
|
"1 0.085 0.217 1.000 -0.675 5.150 12.0 0.907 0.093\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"ASSET TYPE: Equity\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin\n",
|
|||
|
|
" 2 0.3706 1.3811\n",
|
|||
|
|
" 3 0.4255 0.9469\n",
|
|||
|
|
" 4 0.2870 1.3650\n",
|
|||
|
|
" 5 0.2594 1.4419\n",
|
|||
|
|
" 6 0.2784 1.3111\n",
|
|||
|
|
"→ Retained K: 3 (silhouette=0.4255)\n",
|
|||
|
|
" n_accounts pct\n",
|
|||
|
|
"cluster_equity \n",
|
|||
|
|
"0 767 19.7\n",
|
|||
|
|
"1 748 19.2\n",
|
|||
|
|
"2 2384 61.1\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Equity:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_asset aum_final_to_peak aum_drawdown_last\n",
|
|||
|
|
"cluster_equity \n",
|
|||
|
|
"0 0.071 0.067 1.046 -0.935 4.552 12.0 0.975 0.025\n",
|
|||
|
|
"1 0.646 3.610 3.588 -0.099 8.474 0.0 0.154 0.846\n",
|
|||
|
|
"2 0.025 3.296 0.576 -0.835 3.976 90.0 0.000 1.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"ASSET TYPE: Fixed Income\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin\n",
|
|||
|
|
" 2 0.6775 0.5104\n",
|
|||
|
|
" 3 0.4227 0.8458\n",
|
|||
|
|
" 4 0.4350 0.9964\n",
|
|||
|
|
" 5 0.4607 0.9170\n",
|
|||
|
|
" 6 0.4388 0.9468\n",
|
|||
|
|
"→ Retained K: 2 (silhouette=0.6775)\n",
|
|||
|
|
" n_accounts pct\n",
|
|||
|
|
"cluster_fixed_income \n",
|
|||
|
|
"0 3140 79.9\n",
|
|||
|
|
"1 792 20.1\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Fixed Income:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_asset aum_final_to_peak aum_drawdown_last\n",
|
|||
|
|
"cluster_fixed_income \n",
|
|||
|
|
"0 0.060 6.239 0.48 0.000 5.146 69.0 0.000 1.000\n",
|
|||
|
|
"1 0.182 2.310 1.50 0.471 7.273 2.0 0.998 0.002\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"SUMMARY — Asset-type clustering\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" Alternative : K=2, sil=0.4577, n=1317\n",
|
|||
|
|
" Diversified : K=2, sil=0.6037, n=4159\n",
|
|||
|
|
" Equity : K=3, sil=0.4255, n=3899\n",
|
|||
|
|
" Fixed Income : K=2, sil=0.6775, n=3932\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# ============================================================\n",
|
|||
|
|
"# ASSET-TYPE SUB-CLUSTERING\n",
|
|||
|
|
"# ============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"=== Asset types available ===\")\n",
|
|||
|
|
"print(df_aum[ASSET_COL].value_counts())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Build account × asset type monthly panel\n",
|
|||
|
|
"df_rel_m_asset = df_rel_m.copy()\n",
|
|||
|
|
"df_rel_m_asset = df_rel_m_asset.merge(\n",
|
|||
|
|
" df_aum[[ID_COL, ISIN_COL, \"month\", ASSET_COL]].drop_duplicates(),\n",
|
|||
|
|
" on=[ID_COL, ISIN_COL, \"month\"], how=\"left\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"tmp_asset = df_rel_m_asset.copy()\n",
|
|||
|
|
"tmp_asset[\"isin_held_flag\"] = (tmp_asset[\"aum_qty\"] > 0).astype(int)\n",
|
|||
|
|
"tmp_asset[\"isin_active_flag\"] = (tmp_asset[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month_asset = (\n",
|
|||
|
|
" tmp_asset.dropna(subset=[ASSET_COL])\n",
|
|||
|
|
" .groupby([ID_COL, ASSET_COL, \"month\"], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" aum_qty = (\"aum_qty\", \"sum\"),\n",
|
|||
|
|
" net_flow_qty = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" gross_flow_qty = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" sub_qty = (\"sub_qty\", \"sum\"),\n",
|
|||
|
|
" red_qty = (\"red_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" n_isin_held = (\"isin_held_flag\", \"sum\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .sort_values([ID_COL, ASSET_COL, \"month\"])\n",
|
|||
|
|
" .reset_index(drop=True)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month_asset[\"active_month\"] = (df_month_asset[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"df_month_asset[\"flow_direction\"] = np.where(\n",
|
|||
|
|
" df_month_asset[\"gross_flow_qty\"] > 0,\n",
|
|||
|
|
" df_month_asset[\"net_flow_qty\"] / df_month_asset[\"gross_flow_qty\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month_asset[\"aum_peak\"] = df_month_asset.groupby(\n",
|
|||
|
|
" [ID_COL, ASSET_COL])[\"aum_qty\"].cummax()\n",
|
|||
|
|
"df_month_asset[\"aum_drawdown\"] = np.where(\n",
|
|||
|
|
" df_month_asset[\"aum_peak\"] > 0,\n",
|
|||
|
|
" 1 - df_month_asset[\"aum_qty\"] / df_month_asset[\"aum_peak\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Feature engineering per account × asset type\n",
|
|||
|
|
"reference_date = df_month_asset[\"month\"].max()\n",
|
|||
|
|
"last_active_asset = (\n",
|
|||
|
|
" df_month_asset[df_month_asset[\"active_month\"] == 1]\n",
|
|||
|
|
" .groupby([ID_COL, ASSET_COL])[\"month\"].max()\n",
|
|||
|
|
" .reset_index(name=\"last_active_month\")\n",
|
|||
|
|
")\n",
|
|||
|
|
"last_active_asset[\"months_since_last_tx_asset\"] = (\n",
|
|||
|
|
" (reference_date.to_period(\"M\") -\n",
|
|||
|
|
" last_active_asset[\"last_active_month\"].dt.to_period(\"M\"))\n",
|
|||
|
|
" .apply(lambda x: x.n)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_client_asset = (\n",
|
|||
|
|
" df_month_asset.groupby([ID_COL, ASSET_COL], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_months = (\"month\", \"nunique\"),\n",
|
|||
|
|
" n_active_months = (\"active_month\", \"sum\"),\n",
|
|||
|
|
" flow_freq = (\"active_month\", \"mean\"),\n",
|
|||
|
|
" aum_qty_mean = (\"aum_qty\", \"mean\"),\n",
|
|||
|
|
" aum_qty_max = (\"aum_qty\", \"max\"),\n",
|
|||
|
|
" aum_qty_last = (\"aum_qty\", \"last\"),\n",
|
|||
|
|
" gross_flow_qty_sum = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" net_flow_qty_sum = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx_total = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" avg_n_isin_held = (\"n_isin_held\", \"mean\"),\n",
|
|||
|
|
" aum_drawdown_last = (\"aum_drawdown\", \"last\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_client_asset = df_client_asset.merge(\n",
|
|||
|
|
" last_active_asset[[ID_COL, ASSET_COL, \"months_since_last_tx_asset\"]],\n",
|
|||
|
|
" on=[ID_COL, ASSET_COL], how=\"left\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset[\"months_since_last_tx_asset\"] = (\n",
|
|||
|
|
" df_client_asset[\"months_since_last_tx_asset\"]\n",
|
|||
|
|
" .fillna(df_client_asset[\"months_since_last_tx_asset\"].max() + 1)\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset[\"gross_flow_to_aum\"] = np.where(\n",
|
|||
|
|
" df_client_asset[\"aum_qty_mean\"] > 1,\n",
|
|||
|
|
" df_client_asset[\"gross_flow_qty_sum\"] / df_client_asset[\"aum_qty_mean\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset[\"flow_direction_balance\"] = np.where(\n",
|
|||
|
|
" df_client_asset[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" df_client_asset[\"net_flow_qty_sum\"] / df_client_asset[\"gross_flow_qty_sum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset[\"aum_final_to_peak\"] = np.where(\n",
|
|||
|
|
" df_client_asset[\"aum_qty_max\"] > 0,\n",
|
|||
|
|
" np.clip(df_client_asset[\"aum_qty_last\"] / df_client_asset[\"aum_qty_max\"], 0, 1), np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset[\"log_aum_qty_mean\"] = np.log1p(\n",
|
|||
|
|
" df_client_asset[\"aum_qty_mean\"].clip(lower=0)\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_asset = df_client_asset[\n",
|
|||
|
|
" (df_client_asset[\"n_months\"] >= 6) &\n",
|
|||
|
|
" (df_client_asset[\"aum_qty_mean\"] > 0)\n",
|
|||
|
|
"].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\nAccounts per asset type:\")\n",
|
|||
|
|
"print(df_client_asset.groupby(ASSET_COL)[ID_COL].nunique().sort_values(ascending=False))\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Select asset types with enough accounts\n",
|
|||
|
|
"min_accounts = 50\n",
|
|||
|
|
"asset_counts = df_client_asset.groupby(ASSET_COL)[ID_COL].nunique()\n",
|
|||
|
|
"valid_assets = asset_counts[asset_counts >= min_accounts].index.tolist()\n",
|
|||
|
|
"print(f\"\\nRetained asset types (>= {min_accounts} accounts): {valid_assets}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Feature set\n",
|
|||
|
|
"asset_features = [\n",
|
|||
|
|
" \"flow_freq\", \"gross_flow_to_aum\", \"avg_n_isin_held\",\n",
|
|||
|
|
" \"flow_direction_balance\", \"log_aum_qty_mean\",\n",
|
|||
|
|
" \"months_since_last_tx_asset\", \"aum_final_to_peak\", \"aum_drawdown_last\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Clustering loop\n",
|
|||
|
|
"ASSET_RESULTS = {}\n",
|
|||
|
|
"\n",
|
|||
|
|
"for asset in valid_assets:\n",
|
|||
|
|
" print(f\"\\n{'='*60}\")\n",
|
|||
|
|
" print(f\"ASSET TYPE: {asset}\")\n",
|
|||
|
|
" print(f\"{'='*60}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" df_a = df_client_asset[df_client_asset[ASSET_COL] == asset].copy()\n",
|
|||
|
|
" feats = [c for c in asset_features if c in df_a.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
" d = df_a.copy()\n",
|
|||
|
|
" d[\"flow_direction_balance\"] = d[\"flow_direction_balance\"].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"avg_n_isin_held\", \"months_since_last_tx_asset\",\n",
|
|||
|
|
" \"aum_drawdown_last\", \"aum_final_to_peak\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" d[col] = winsorize_mad(d[col], n_sigma=3)\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"gross_flow_to_aum\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" vals = d[col].to_numpy(dtype=float)\n",
|
|||
|
|
" d[col] = np.log1p(np.clip(vals, 0, np.nanpercentile(vals, 90)))\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"flow_freq\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" vals = d[col].to_numpy(dtype=float)\n",
|
|||
|
|
" d[col] = np.log1p(np.clip(vals, 0, None))\n",
|
|||
|
|
"\n",
|
|||
|
|
" X_a = d[feats].fillna(d[feats].median()).to_numpy()\n",
|
|||
|
|
" X_a_scaled = RobustScaler().fit_transform(X_a)\n",
|
|||
|
|
"\n",
|
|||
|
|
" best_k, best_sil = 2, -1\n",
|
|||
|
|
" rows_k = []\n",
|
|||
|
|
" max_k = min(6, len(df_a) // 50)\n",
|
|||
|
|
"\n",
|
|||
|
|
" for k in range(2, max_k + 1):\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=30, random_state=RANDOM_STATE)\n",
|
|||
|
|
" labels = km.fit_predict(X_a_scaled)\n",
|
|||
|
|
" sil = silhouette_score(X_a_scaled, labels)\n",
|
|||
|
|
" db = davies_bouldin_score(X_a_scaled, labels)\n",
|
|||
|
|
" rows_k.append({\"k\": k, \"silhouette\": round(sil, 4), \"davies_bouldin\": round(db, 4)})\n",
|
|||
|
|
" if sil > best_sil:\n",
|
|||
|
|
" best_sil, best_k = sil, k\n",
|
|||
|
|
"\n",
|
|||
|
|
" print(pd.DataFrame(rows_k).to_string(index=False))\n",
|
|||
|
|
" print(f\"→ Retained K: {best_k} (silhouette={best_sil:.4f})\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" km_final = KMeans(n_clusters=best_k, n_init=50, random_state=RANDOM_STATE)\n",
|
|||
|
|
" cluster_col = f\"cluster_{asset.lower().replace(' ','_')}\"\n",
|
|||
|
|
" df_a[cluster_col] = km_final.fit_predict(X_a_scaled)\n",
|
|||
|
|
"\n",
|
|||
|
|
" counts = df_a[cluster_col].value_counts().sort_index()\n",
|
|||
|
|
" props = counts / counts.sum() * 100\n",
|
|||
|
|
" print(pd.DataFrame({\"n_accounts\": counts, \"pct\": props.round(1)}))\n",
|
|||
|
|
"\n",
|
|||
|
|
" profile_vars_asset = [c for c in asset_features if c in df_a.columns]\n",
|
|||
|
|
" prof = plot_heatmap(\n",
|
|||
|
|
" df_a, profile_vars_asset, cluster_col,\n",
|
|||
|
|
" title=f\"Cluster Signatures — {asset} (K={best_k}, robust z-score)\",\n",
|
|||
|
|
" figsize=(14, 4)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(f\"\\nMedians — {asset}:\")\n",
|
|||
|
|
" print(prof.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
" ASSET_RESULTS[asset] = {\n",
|
|||
|
|
" \"df\": df_a, \"cluster_col\": cluster_col,\n",
|
|||
|
|
" \"k\": best_k, \"silhouette\": best_sil, \"profile\": prof,\n",
|
|||
|
|
" }\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\n\" + \"=\"*60)\n",
|
|||
|
|
"print(\"SUMMARY — Asset-type clustering\")\n",
|
|||
|
|
"print(\"=\"*60)\n",
|
|||
|
|
"for asset, res in ASSET_RESULTS.items():\n",
|
|||
|
|
" print(f\" {asset:20s}: K={res['k']}, sil={res['silhouette']:.4f}, n={len(res['df'])}\")"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "9fb2786e",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"### 5e. Asset-Type Sub-Clustering & Cross-Analysis\n",
|
|||
|
|
"\n",
|
|||
|
|
"A complementary clustering is performed **within each asset type** (Fixed Income, Diversified, Equity, Alternative) using the same behavioral features restricted to each asset's positions. The cross-analysis with the global clustering uses the Adjusted Rand Index to measure how much the two segmentations overlap.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 20,
|
|||
|
|
"id": "05d06b16",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Available columns: ['Registrar Account - ID', 'cluster_k4', 'cluster_alternative', 'cluster_diversified', 'cluster_equity', 'cluster_fixed_income']\n",
|
|||
|
|
"Shape: (7177, 6)\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1800x1200 with 8 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Global × Alternative\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per global cluster (each row sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 86.5 6.2 7.3\n",
|
|||
|
|
"Global C1 93.2 0.9 5.9\n",
|
|||
|
|
"Global C2 48.8 10.8 40.4\n",
|
|||
|
|
"Global C3 91.1 1.1 7.9\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per asset cluster (each column sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 21.3 34.6 12.0\n",
|
|||
|
|
"Global C1 28.2 6.5 11.9\n",
|
|||
|
|
"Global C2 9.5 47.9 52.5\n",
|
|||
|
|
"Global C3 41.0 11.0 23.6\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Global × Diversified\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per global cluster (each row sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 31.6 40.5 27.9\n",
|
|||
|
|
"Global C1 40.9 54.0 5.1\n",
|
|||
|
|
"Global C2 21.2 61.7 17.1\n",
|
|||
|
|
"Global C3 64.2 33.9 1.8\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per asset cluster (each column sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 14.6 18.6 54.6\n",
|
|||
|
|
"Global C1 23.3 30.5 12.3\n",
|
|||
|
|
"Global C2 7.8 22.4 26.5\n",
|
|||
|
|
"Global C3 54.4 28.5 6.6\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Global × Equity\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per global cluster (each row sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1 Asset C2\n",
|
|||
|
|
"Global C0 37.3 29.4 0.3 32.9\n",
|
|||
|
|
"Global C1 44.2 6.8 0.0 49.1\n",
|
|||
|
|
"Global C2 18.6 9.7 48.5 23.1\n",
|
|||
|
|
"Global C3 70.7 3.2 0.2 25.9\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per asset cluster (each column sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1 Asset C2\n",
|
|||
|
|
"Global C0 15.8 57.2 0.9 20.7\n",
|
|||
|
|
"Global C1 23.1 16.2 0.0 38.0\n",
|
|||
|
|
"Global C2 6.2 15.0 98.3 11.5\n",
|
|||
|
|
"Global C3 54.9 11.6 0.9 29.8\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Global × Fixed Income\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per global cluster (each row sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 65.4 21.2 13.5\n",
|
|||
|
|
"Global C1 72.0 24.1 3.9\n",
|
|||
|
|
"Global C2 19.2 52.9 27.8\n",
|
|||
|
|
"Global C3 34.5 61.5 4.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"% per asset cluster (each column sums to 100%):\n",
|
|||
|
|
" Not exposed Asset C0 Asset C1\n",
|
|||
|
|
"Global C0 28.1 10.3 28.2\n",
|
|||
|
|
"Global C1 38.2 14.4 10.1\n",
|
|||
|
|
"Global C2 6.6 20.4 46.2\n",
|
|||
|
|
"Global C3 27.2 54.9 15.5\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Adjusted Rand Index — coherence between global and asset-type clusterings\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"(1 = identical, 0 = independent, <0 = worse than random)\n",
|
|||
|
|
"\n",
|
|||
|
|
" Alternative : ARI=0.0274 (n=1164 shared accounts)\n",
|
|||
|
|
" Diversified : ARI=0.0344 (n=3978 shared accounts)\n",
|
|||
|
|
" Equity : ARI=0.1579 (n=3689 shared accounts)\n",
|
|||
|
|
" Fixed Income : ARI=0.1112 (n=3742 shared accounts)\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"Multi-asset exposure by global cluster\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"Average number of asset types per global cluster:\n",
|
|||
|
|
"cluster_k4\n",
|
|||
|
|
"0 1.79\n",
|
|||
|
|
"1 1.50\n",
|
|||
|
|
"2 2.92\n",
|
|||
|
|
"3 1.40\n",
|
|||
|
|
"Name: n_asset_types, dtype: float64\n",
|
|||
|
|
"\n",
|
|||
|
|
"Distribution of asset type count per global cluster:\n",
|
|||
|
|
" 0 asset type(s) 1 asset type(s) 2 asset type(s) 3 asset type(s) 4 asset type(s)\n",
|
|||
|
|
"Global C0 0.0 49.1 29.3 14.9 6.7\n",
|
|||
|
|
"Global C1 0.0 64.7 23.3 9.6 2.4\n",
|
|||
|
|
"Global C2 0.8 17.5 13.7 24.9 43.1\n",
|
|||
|
|
"Global C3 0.4 73.9 14.8 7.3 3.5\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# ============================================================\n",
|
|||
|
|
"# CROSS-ANALYSIS — Global clustering × Asset-type clustering\n",
|
|||
|
|
"# ============================================================\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 1. Merge asset cluster labels into global dataframe\n",
|
|||
|
|
"dfc_cross = dfc[[ID_COL, \"cluster_k4\"]].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"for asset, res in ASSET_RESULTS.items():\n",
|
|||
|
|
" cluster_col = res[\"cluster_col\"]\n",
|
|||
|
|
" df_a = res[\"df\"][[ID_COL, cluster_col]].copy()\n",
|
|||
|
|
" dfc_cross = dfc_cross.merge(df_a, on=ID_COL, how=\"left\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"Available columns:\", dfc_cross.columns.tolist())\n",
|
|||
|
|
"print(\"Shape:\", dfc_cross.shape)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 2. Contingency tables: global clusters × asset clusters\n",
|
|||
|
|
"fig, axes = plt.subplots(2, 2, figsize=(18, 12))\n",
|
|||
|
|
"axes = axes.flatten()\n",
|
|||
|
|
"\n",
|
|||
|
|
"for i, (asset, res) in enumerate(ASSET_RESULTS.items()):\n",
|
|||
|
|
" cluster_col = res[\"cluster_col\"]\n",
|
|||
|
|
" if cluster_col not in dfc_cross.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" ct = pd.crosstab(\n",
|
|||
|
|
" dfc_cross[\"cluster_k4\"],\n",
|
|||
|
|
" dfc_cross[cluster_col].fillna(-1).astype(int),\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
" ).round(3) * 100\n",
|
|||
|
|
" col_names = {\n",
|
|||
|
|
" c: f\"Asset C{c}\" if c >= 0 else \"Not exposed\"\n",
|
|||
|
|
" for c in ct.columns\n",
|
|||
|
|
" }\n",
|
|||
|
|
" ct = ct.rename(columns=col_names)\n",
|
|||
|
|
" ct.index = [f\"Global C{i}\" for i in ct.index]\n",
|
|||
|
|
" sns.heatmap(\n",
|
|||
|
|
" ct, cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
|
|||
|
|
" ax=axes[i], cbar_kws={\"label\": \"%\"},\n",
|
|||
|
|
" vmin=0, vmax=100,\n",
|
|||
|
|
" )\n",
|
|||
|
|
" axes[i].set_title(f\"Global × {asset} (% per global cluster)\")\n",
|
|||
|
|
" axes[i].set_xlabel(f\"{asset} cluster\")\n",
|
|||
|
|
" axes[i].set_ylabel(\"Global cluster\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.suptitle(\"Cross-Analysis: Global Clustering × Asset-Type Clustering\",\n",
|
|||
|
|
" fontsize=14, y=1.02)\n",
|
|||
|
|
"plt.tight_layout()\n",
|
|||
|
|
"plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 3. Detailed contingency tables (row % and column %)\n",
|
|||
|
|
"for asset, res in ASSET_RESULTS.items():\n",
|
|||
|
|
" cluster_col = res[\"cluster_col\"]\n",
|
|||
|
|
" if cluster_col not in dfc_cross.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" print(f\"\\n{'='*60}\")\n",
|
|||
|
|
" print(f\"Global × {asset}\")\n",
|
|||
|
|
" print(f\"{'='*60}\")\n",
|
|||
|
|
" ct_row = pd.crosstab(\n",
|
|||
|
|
" dfc_cross[\"cluster_k4\"],\n",
|
|||
|
|
" dfc_cross[cluster_col].fillna(-1).astype(int),\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
" ).round(3) * 100\n",
|
|||
|
|
" ct_row.index = [f\"Global C{i}\" for i in ct_row.index]\n",
|
|||
|
|
" ct_row.columns = [f\"Asset C{c}\" if c >= 0 else \"Not exposed\"\n",
|
|||
|
|
" for c in ct_row.columns]\n",
|
|||
|
|
" print(\"\\n% per global cluster (each row sums to 100%):\")\n",
|
|||
|
|
" print(ct_row.to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
" ct_col = pd.crosstab(\n",
|
|||
|
|
" dfc_cross[\"cluster_k4\"],\n",
|
|||
|
|
" dfc_cross[cluster_col].fillna(-1).astype(int),\n",
|
|||
|
|
" normalize=\"columns\"\n",
|
|||
|
|
" ).round(3) * 100\n",
|
|||
|
|
" ct_col.index = [f\"Global C{i}\" for i in ct_col.index]\n",
|
|||
|
|
" ct_col.columns = [f\"Asset C{c}\" if c >= 0 else \"Not exposed\"\n",
|
|||
|
|
" for c in ct_col.columns]\n",
|
|||
|
|
" print(\"\\n% per asset cluster (each column sums to 100%):\")\n",
|
|||
|
|
" print(ct_col.to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 4. Adjusted Rand Index\n",
|
|||
|
|
"from sklearn.metrics import adjusted_rand_score\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\n\" + \"=\"*60)\n",
|
|||
|
|
"print(\"Adjusted Rand Index — coherence between global and asset-type clusterings\")\n",
|
|||
|
|
"print(\"=\"*60)\n",
|
|||
|
|
"print(\"(1 = identical, 0 = independent, <0 = worse than random)\\n\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"for asset, res in ASSET_RESULTS.items():\n",
|
|||
|
|
" cluster_col = res[\"cluster_col\"]\n",
|
|||
|
|
" if cluster_col not in dfc_cross.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" mask = dfc_cross[cluster_col].notna()\n",
|
|||
|
|
" labels_global = dfc_cross.loc[mask, \"cluster_k4\"].values\n",
|
|||
|
|
" labels_asset = dfc_cross.loc[mask, cluster_col].values\n",
|
|||
|
|
" ari = adjusted_rand_score(labels_global, labels_asset)\n",
|
|||
|
|
" print(f\" {asset:20s} : ARI={ari:.4f} (n={mask.sum()} shared accounts)\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 5. Multi-asset exposure by global cluster\n",
|
|||
|
|
"print(\"\\n\" + \"=\"*60)\n",
|
|||
|
|
"print(\"Multi-asset exposure by global cluster\")\n",
|
|||
|
|
"print(\"=\"*60)\n",
|
|||
|
|
"\n",
|
|||
|
|
"asset_cols = [res[\"cluster_col\"] for res in ASSET_RESULTS.values()\n",
|
|||
|
|
" if res[\"cluster_col\"] in dfc_cross.columns]\n",
|
|||
|
|
"dfc_cross[\"n_asset_types\"] = dfc_cross[asset_cols].notna().sum(axis=1)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\nAverage number of asset types per global cluster:\")\n",
|
|||
|
|
"print(dfc_cross.groupby(\"cluster_k4\")[\"n_asset_types\"].mean().round(2))\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\nDistribution of asset type count per global cluster:\")\n",
|
|||
|
|
"ct_multi = pd.crosstab(\n",
|
|||
|
|
" dfc_cross[\"cluster_k4\"],\n",
|
|||
|
|
" dfc_cross[\"n_asset_types\"],\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
").round(3) * 100\n",
|
|||
|
|
"ct_multi.index = [f\"Global C{i}\" for i in ct_multi.index]\n",
|
|||
|
|
"ct_multi.columns = [f\"{c} asset type(s)\" for c in ct_multi.columns]\n",
|
|||
|
|
"print(ct_multi.to_string())"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "7228921e",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"### 5f. Fund-Level Sub-Clustering\n",
|
|||
|
|
"\n",
|
|||
|
|
"Same logic applied within each of the top 15 funds by AUM, with a minimum of 20 accounts per fund.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 21,
|
|||
|
|
"id": "c28bd684",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"=== Available funds (top 20 by AUM) ===\n",
|
|||
|
|
"Product - Fund\n",
|
|||
|
|
"Carmignac Patrimoine 1.779824e+12\n",
|
|||
|
|
"Carmignac Sécurité 1.039756e+12\n",
|
|||
|
|
"Carmignac Investissement 4.742077e+11\n",
|
|||
|
|
"Carmignac Portfolio Sécurité 2.334162e+11\n",
|
|||
|
|
"Carmignac Portfolio Flexible Bond 1.935158e+11\n",
|
|||
|
|
"Carmignac Emergents 1.165950e+11\n",
|
|||
|
|
"Carmignac Portfolio Patrimoine 8.958118e+10\n",
|
|||
|
|
"Carmignac Portfolio Global Bond 8.130476e+10\n",
|
|||
|
|
"Carmignac Portfolio Credit 7.134488e+10\n",
|
|||
|
|
"Carmignac Portfolio Emerging Patrimoine 6.955348e+10\n",
|
|||
|
|
"Carmignac Portfolio Grande Europe 5.661533e+10\n",
|
|||
|
|
"Carmignac Court Terme 4.825378e+10\n",
|
|||
|
|
"Carmignac Portfolio Long-Short European Equities 4.666827e+10\n",
|
|||
|
|
"Carmignac Portfolio Climate Transition 4.595703e+10\n",
|
|||
|
|
"Carmignac Credit 2027 3.838470e+10\n",
|
|||
|
|
"Carmignac Absolute Return Europe 3.649769e+10\n",
|
|||
|
|
"Carmignac Investissement Latitude 3.288443e+10\n",
|
|||
|
|
"Carmignac Multi Expertise 2.605206e+10\n",
|
|||
|
|
"Carmignac Portfolio Emergents 2.596680e+10\n",
|
|||
|
|
"Carmignac Portfolio Asia Discovery 2.234114e+10\n",
|
|||
|
|
"\n",
|
|||
|
|
"Selected funds (15) :\n",
|
|||
|
|
" Carmignac Patrimoine : 6183 clients, AUM=1779824207927\n",
|
|||
|
|
" Carmignac Sécurité : 2825 clients, AUM=1039756177353\n",
|
|||
|
|
" Carmignac Investissement : 4982 clients, AUM=474207712295\n",
|
|||
|
|
" Carmignac Portfolio Sécurité : 1264 clients, AUM=233416206914\n",
|
|||
|
|
" Carmignac Portfolio Flexible Bond : 1431 clients, AUM=193515764737\n",
|
|||
|
|
" Carmignac Emergents : 4231 clients, AUM=116594983722\n",
|
|||
|
|
" Carmignac Portfolio Patrimoine : 1234 clients, AUM=89581181925\n",
|
|||
|
|
" Carmignac Portfolio Global Bond : 2114 clients, AUM=81304760901\n",
|
|||
|
|
" Carmignac Portfolio Credit : 1135 clients, AUM=71344877483\n",
|
|||
|
|
" Carmignac Portfolio Emerging Patrimoine : 1673 clients, AUM=69553477364\n",
|
|||
|
|
" Carmignac Portfolio Grande Europe : 2950 clients, AUM=56615328729\n",
|
|||
|
|
" Carmignac Court Terme : 1331 clients, AUM=48253783176\n",
|
|||
|
|
" Carmignac Portfolio Long-Short European Equities : 689 clients, AUM=46668268707\n",
|
|||
|
|
" Carmignac Portfolio Climate Transition : 3097 clients, AUM=45957034098\n",
|
|||
|
|
" Carmignac Credit 2027 : 327 clients, AUM=38384700194\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month_fund shape: (3865290, 19)\n",
|
|||
|
|
"df_client_fund shape: (20093, 23)\n",
|
|||
|
|
"\n",
|
|||
|
|
"Comptes par fund :\n",
|
|||
|
|
"Product - Fund\n",
|
|||
|
|
"Carmignac Patrimoine 3153\n",
|
|||
|
|
"Carmignac Investissement 2192\n",
|
|||
|
|
"Carmignac Emergents 1779\n",
|
|||
|
|
"Carmignac Portfolio Global Bond 1716\n",
|
|||
|
|
"Carmignac Sécurité 1622\n",
|
|||
|
|
"Carmignac Portfolio Grande Europe 1386\n",
|
|||
|
|
"Carmignac Portfolio Climate Transition 1278\n",
|
|||
|
|
"Carmignac Portfolio Sécurité 1161\n",
|
|||
|
|
"Carmignac Portfolio Patrimoine 1143\n",
|
|||
|
|
"Carmignac Portfolio Emerging Patrimoine 1135\n",
|
|||
|
|
"Carmignac Portfolio Flexible Bond 1087\n",
|
|||
|
|
"Carmignac Portfolio Credit 1016\n",
|
|||
|
|
"Carmignac Portfolio Long-Short European Equities 605\n",
|
|||
|
|
"Carmignac Court Terme 525\n",
|
|||
|
|
"Carmignac Credit 2027 295\n",
|
|||
|
|
"Name: Registrar Account - ID, dtype: int64\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Patrimoine\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.5054 0.8315 656\n",
|
|||
|
|
" 3 0.4949 0.8428 569\n",
|
|||
|
|
" 4 0.3566 1.2291 338\n",
|
|||
|
|
" 5 0.3844 1.1120 276\n",
|
|||
|
|
" 6 0.3207 1.1835 271\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.5054)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_patrimoine \n",
|
|||
|
|
"0 656 20.8\n",
|
|||
|
|
"1 2497 79.2\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABNwAAAGGCAYAAACg4ZwmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdYE1nbBvA7oS4KKEVUECwIFqpd7Ii966prF1TsvTcsoKiLHVDWglixtxXL2nUFXSsWdNdeV+lFUErm+8OPvMaAhggbiPfvunJpzsyceSbJSciTU0SCIAggIiIiIiIiIiKifCFWdQBERERERERERETqhAk3IiIiIiIiIiKifMSEGxERERERERERUT5iwo2IiIiIiIiIiCgfMeFGRERERERERESUj5hwIyIiIiIiIiIiykdMuBEREREREREREeUjJtyIiIiIiIiIiIjyERNuRERERERERERE+YgJNyKi/+fq6opp06apOgyVmDZtGlxdXVUdBqmZfv36oV+/fqoOQy2o8rFU9fMYGRkJOzs7vHr1SmUxFBaXL1+Gra0tjh07pupQ6P+dP38ezs7OiIuLU3UoRERUyDDhRkRq7/nz5/Dy8kLz5s1hb2+PGjVq4JdffkFISAg+fPjwn8SQlpaG1atX4/Lly//J+bLFxcXBx8cHrVu3hoODA+rXr4+ff/4Zv/76K96/f/+fxpIXa9euxcmTJ1UdRqEUExODxYsXo3Xr1nB0dISTkxO6du2KwMBAJCUlqTo8tTRt2jTY2tpKbzVq1EDHjh2xceNGpKen56mut2/fYvXq1YiKiiqgaNXP8uXL0a5dO5ibm0vL+vXrh/bt28vtGx4eDkdHR3Tp0gUJCQlKnzM+Ph7r169Hnz59UK9ePdSqVQs9evRAWFiY0nUWZdevX8fq1av5HpODxo0bw9LSEkFBQaoOhYiIChlNVQdARFSQzp49i7Fjx0JbWxudOnWCjY0NMjIycO3aNfz66694+PAhvL29CzyOtLQ0+Pv7Y9SoUahbt26Bnw8AEhIS0K1bN6SkpKBbt26oWLEiEhIS8ODBA+zYsQO9evVCsWLFAADe3t4QBOE/iUsRQUFBaNWqFdzc3FQdSqESGRkJT09PpKamomPHjqhevToA4M6dO1i3bh2uXr2KjRs3qjjK/9mwYYOqQ8g32tra8PHxAQAkJyfj+PHjWLx4MW7fvo3ly5crXM+7d+/g7+8Pc3NzVK1aVeHjVPlYqvLcUVFRuHTpEkJDQ7+5b3h4OIYNG4YKFSogODgYJUqUUPq8N2/exIoVK9C4cWMMHz4cmpqaOH78OMaPH4+HDx9izJgxStddFN24cQP+/v7o0qULDAwMVB1OodOzZ08sWbIEo0ePRvHixVUdDhERFRJMuBGR2nrx4gXGjx+PsmXLIiQkBKVKlZJu69OnD549e4azZ8+qLsB8kJqaCj09vRy37dmzB69fv8aOHTtQo0YNmW0pKSnQ0tKS3v/8/+pKIpEgIyMDOjo6qg5FKUlJSRg1ahQ0NDSwf/9+VKpUSWb7+PHjsWvXrnw5V1paGn766afvrkdbWzsfoikcNDU10alTJ+n93r17o3v37ggLC8O0adNgZmZWIOfNfi5U+Viq8tx79+5F2bJl4eTk9NX9rly5guHDh6N8+fLfnWwDAGtraxw/flymV13v3r0xcOBArFu3DoMHD871vTcvBEHAx48foaur+911Uf7IzMyERCLJ0+u+VatW8PHxwbFjx/Dzzz8XYHRERFSUcEgpEamt9evXIzU1FQsWLJBJtmWzsrLCgAEDcj1+9erVsLW1lSvft28fbG1t8fLlS2nZ7du3MWjQINStWxcODg5wdXXF9OnTAQAvX75E/fr1AQD+/v7SYWmrV6+WHv/o0SOMGTMGderUgb29Pbp27YpTp07leN4rV65g7ty5qF+/Ppo0aZJr/M+fP4eGhkaOX1SLFy8uk3jKaQ63+Ph4TJ48GTVq1ECtWrUwdepU3L9/H7a2tti3b5/Msc7Oznj79i1GjBgBZ2dn1KtXD4sXL0ZWVpZMnRs2bMAvv/wifZy6du0qNxeRra0tUlNTsX//fuljlT23Xm5zzeX0XNna2mL+/Pk4dOgQ2rVrB3t7e1y4cAHAp2F906dPh4uLC+zs7NCuXTvs2bNHrt4tW7agXbt2cHR0RO3atdG1a1ccPnw4p4e7wIWGhuLt27eYNm2aXLINAExMTDBixAjp/ZMnT8LT0xMNGzaEnZ0d3NzcEBAQIPecZA/Nu3PnDvr06QNHR0csW7YML1++hK2tLTZs2IBt27ahefPmcHR0hIeHB968eQNBEBAQEIDGjRvDwcEBw4cPlxvCl9PcX69evcKwYcPg5OSE+vXrY+HChbhw4QJsbW1lhlxnx/Xw4UP069cPjo6OaNSoEdatWydTX3p6OlauXImuXbuiZs2acHJyQu/evRERESH3GEkkEoSEhKBDhw6wt7dHvXr1MGjQINy+fVvh5yGbWCxGnTp1pNeUkJCAxYsXo0OHDnB2dkaNGjUwePBg3L9/X3rM5cuXpV/Gp0+fLn19Z7en3J6LnB7L7Lm8wsLC4O/vj0aNGsHZ2RljxoxBcnIy0tPTsWDBAtSvXx/Ozs6YPn263PDXzMxMBAQEwM3NDXZ2dnB1dcWyZcvk9vvaudesWYPGjRvD3t4eAwYMwLNnz+Qeq1u3bmHQoEGoWbMmHB0d0bdvX1y7dk2hx/nUqVOoV68eRCJRrvtcvXoVQ4cOhaWlJYKDg1GyZEmF6v6acuXKySTbAEAkEsHNzQ3p6el48eKFUvW6urpi6NChuHDhArp27QoHBwdp770XL15IPwccHR3Ro0ePXH8UkkgkWLZsGRo0aAAnJycMGzYMb968kTtXTvOS5tQuv/Zet3r1aixZsgQA0Lx5c+nr9vPPwM9lf1bldFNkLsAjR46ga9eu0nbUoUMHhISEyOyTlJSEhQsXwtXVFXZ2dmjcuDGmTJkiM49abGwsZsyYARcXF9jb26Njx47Yv3+/TD2fv89t2rQJbm5usLe3x6NHjwAo9tkMAMbGxrC1tc1xGxER/bjYw42I1NaZM2dQrlw5ud5d+S02NhaDBg1CyZIl4enpCQMDA7x8+RJ//PEHAMDIyAhz587F3Llz0aJFC7Ro0QIApAmif/75B7169YKZmRmGDBkCPT09HD16FCNHjsTq1aul+2ebN28ejIyMMHLkSKSmpuYal7m5ObKysnDw4EF06dIlT9ckkUgwfPhwREZGolevXqhYsSJOnTqFqVOn5rh/VlYWBg0aBAcHB0yZMgXh4eHYuHEjypUrh969e0v327x5M1xdXdGhQwdkZGTgyJEjGDt2LIKCgtC0aVMAwJIlSzBr1iw4ODigR48eAABLS8s8xZ8tIiICR48eRZ8+fVCyZEmYm5sjJiYGPXr0gEgkQp8+fWBkZITz589j5syZSElJwcCBAwEAu3btgo+PD1q1aoX+/fvj48ePePDgAW7duoUOHTooFc/3OH36NHR1ddGqVSuF9t+/fz/09PTg7u4OPT09REREYNWqVUhJSZF7HhMSEjBkyBC0a9cOHTt2hLGxsXTb4cOHkZGRgX79+iEhIQHr16/HuHHjUK9ePVy+fBlDhgzBs2fPsHXrVixevBi+vr65xpSamooBAwYgOjoa/fv3h4mJCX7//fdc5zZMTEzE4MGD0aJFC7Rp0wbHjx+Hn58fbGxspMnmlJQU7N69G+3bt0f37t3x/v177NmzB4MHD8bu3btlhm3OnDkT+/btQ+PGjfHzzz8jKysLV69exa1bt2Bvb6/Q4/q57KRLiRIl8OLFC5w8eRKtW7eGhYUFYmJisHPnTvTt2xdHjhyBmZkZKlWqhDFjxmDVqlXo2bMnatasCQAy71Ffey5y8ttvv0FXVxeenp7S50FTUxMikUjaK/LWrVvYt28fzM3NMWrUKOmxs2bNwv79+9GqVSu4u7sjMjISQUFBePToEQICAr55/evWrYNIJIKHhwdSUlKwfv16TJo0Cbt375buEx4ejiFDhsDOzg6jRo2CSCTCvn37MGDAAGzfvh0ODg651v/
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Patrimoine:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_patrimoine \n",
|
|||
|
|
"0 0.085 0.127 1.00 -1.000 4.749 12.0 0.907 0.093 0.0\n",
|
|||
|
|
"1 0.038 2.334 0.65 -0.879 4.740 83.0 0.000 1.000 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Sécurité\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.5729 0.8464 309\n",
|
|||
|
|
" 3 0.5553 0.8254 192\n",
|
|||
|
|
" 4 0.4175 0.9791 96\n",
|
|||
|
|
" 5 0.4124 0.9532 92\n",
|
|||
|
|
" 6 0.2702 1.1743 83\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.5729)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_sécurité \n",
|
|||
|
|
"0 309 19.1\n",
|
|||
|
|
"1 1313 80.9\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Sécurité:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_sécurité \n",
|
|||
|
|
"0 0.111 1.312 1.000 0.067 5.178 12.0 0.916 0.084 0.0\n",
|
|||
|
|
"1 0.071 4.471 0.525 -0.128 4.721 81.0 0.000 1.000 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Investissement\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.4306 1.0230 381\n",
|
|||
|
|
" 3 0.4242 0.9113 363\n",
|
|||
|
|
" 4 0.2926 1.3034 312\n",
|
|||
|
|
" 5 0.3051 1.2219 216\n",
|
|||
|
|
" 6 0.2917 1.2180 209\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.4306)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_investissement \n",
|
|||
|
|
"0 1811 82.6\n",
|
|||
|
|
"1 381 17.4\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Investissement:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_investissement \n",
|
|||
|
|
"0 0.025 1.069 0.738 -1.000 3.241 92.0 0.000 1.000 0.000\n",
|
|||
|
|
"1 0.531 1.860 1.352 -0.468 7.592 0.0 0.123 0.877 0.029\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Sécurité\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.8029 0.3052 180\n",
|
|||
|
|
" 3 0.6465 0.4761 180\n",
|
|||
|
|
" 4 0.6612 0.5242 61\n",
|
|||
|
|
" 5 0.7213 0.4794 61\n",
|
|||
|
|
" 6 0.7007 0.6254 44\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.8029)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_sécurité \n",
|
|||
|
|
"0 981 84.5\n",
|
|||
|
|
"1 180 15.5\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Sécurité:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_sécurité \n",
|
|||
|
|
"0 0.061 7.231 0.419 0.000 6.043 72.0 0.0 1.0 0.0\n",
|
|||
|
|
"1 0.221 1.468 1.000 0.333 8.488 0.0 1.0 0.0 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Flexible Bond\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.6085 0.7567 122\n",
|
|||
|
|
" 3 0.3985 1.1704 75\n",
|
|||
|
|
" 4 0.4165 0.9067 34\n",
|
|||
|
|
" 5 0.3106 1.1499 32\n",
|
|||
|
|
" 6 0.2713 1.1373 32\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.6085)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_flexible_b \n",
|
|||
|
|
"0 965 88.8\n",
|
|||
|
|
"1 122 11.2\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Flexible Bond:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_flexible_b \n",
|
|||
|
|
"0 0.060 3.925 0.562 -0.156 3.982 81.0 0.000 1.000 0.000\n",
|
|||
|
|
"1 0.742 6.079 1.650 0.119 7.743 0.0 0.677 0.323 0.085\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Emergents\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.4352 1.1772 508\n",
|
|||
|
|
" 3 0.4735 0.8703 290\n",
|
|||
|
|
" 4 0.4071 1.0186 140\n",
|
|||
|
|
" 5 0.2776 1.2986 137\n",
|
|||
|
|
" 6 0.2956 1.2394 125\n",
|
|||
|
|
"→ K retenu : 3 (silhouette=0.4735)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_emergents \n",
|
|||
|
|
"0 345 19.4\n",
|
|||
|
|
"1 1144 64.3\n",
|
|||
|
|
"2 290 16.3\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Emergents:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_emergents \n",
|
|||
|
|
"0 0.070 0.031 1.000 -0.926 3.362 12.0 0.907 0.093 0.000\n",
|
|||
|
|
"1 0.018 2.101 0.425 -1.000 2.718 95.0 0.000 1.000 0.000\n",
|
|||
|
|
"2 0.546 2.578 1.000 -0.168 7.015 0.0 0.181 0.819 0.028\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Patrimoine\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.5909 0.5603 251\n",
|
|||
|
|
" 3 0.6446 0.5163 170\n",
|
|||
|
|
" 4 0.6254 0.5994 100\n",
|
|||
|
|
" 5 0.6019 0.7216 74\n",
|
|||
|
|
" 6 0.5870 0.7977 72\n",
|
|||
|
|
"→ K retenu : 3 (silhouette=0.6446)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_patrimoine \n",
|
|||
|
|
"0 238 20.8\n",
|
|||
|
|
"1 735 64.3\n",
|
|||
|
|
"2 170 14.9\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABM8AAAGGCAYAAABseKbNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdYU+fbB/Bv2FMUcbIcCFgEERW3KOJeqL86ce9ZbR2AFrdo6xYHdeNCq4JaF9baWhX3QkVr3SiigCzZyXn/8CU1MkwCGIjfz3WdS3POc55znzxJCDfPEAmCIEBBM2fOREJCAlauXAlXV1ccPnwYmpqaGD9+PBo0aICZM2cqWiUREREREREREVGJI1ImeZacnIxJkybhzp07eP/+PSpWrIjY2Fg4Ozvjl19+gYGBQXHESkRERERERERE9EUplTzLcfXqVTx48ACpqalwcHBA06ZNizI2IiIiIiIiIiIilSpU8oyIiIiIiIiIiEidaSl7Ynh4OMLDwxEXFweJRCJzzN/fv9CBERERERERERERqZpSybOAgACsXbsWderUQYUKFSASiYo6LiIiIiIiIiIiIpVTathm8+bNMXXqVHh6ehZDSERERERERERERCWDhjInZWVlwcXFpahjISIiIiIiIiIiKlGUSp7973//w5EjR4o6FiIiIiIiIiIiohJFqTnPMjIysG/fPoSHh8POzg5aWrLV+Pj4FElwREREREREREREqqRUz7MHDx7A3t4eIpEI//zzD+7duyfdIiMjizpGIlJD7u7u8Pb2VnUYKuHt7Q13d3dVh0FqZuDAgRg4cKCqw1AboaGh6NChAxwcHNCgQQOFzs3rPW5nZ4c1a9YUZYgqFRUVBTs7Oxw8ePCrunaOOXPmYOjQoSq7fkni7e2NevXqqToM+siUKVPw3XffqToMIiK1olTPsx07dhR1HESkJp4/f45Nmzbh/PnzePPmDbS1tWFra4uOHTuiT58+0NPTK/YY0tLSsGnTJri6uqJRo0bFfr0c8fHxWLduHc6dO4dXr17B0NAQ5ubmaNSoEcaNGwdDQ8MvFosiNmzYABsbG3h4eKg6lBInNjYWmzdvxpkzZxAdHQ2RSIQaNWrAw8MDXl5eKFOmjKpDVDve3t4ICQmRPjY0NISFhQU8PT3h5eUFHR2dIrlOTEwM9u3bBw8PD9SuXVvm2KNHj+Dj44MWLVpg1KhRX+RzS1F2dnbS/4tEIpiZmcHW1hajR49W+HPvr7/+wu3btzFx4sSiDlMtvXjxAvv378emTZuk+6KiotCmTRtMnz4dw4cPl+4XBAGzZ8/G3r17MWHChEI9xw8fPsSaNWtw9+5dxMbGQk9PDzY2Nhg+fPhX+QeZXbt2QV9fHz179lR1KCXOyJEj0atXL9y/fx/29vaqDoeISC0olTwjIsrLn3/+ie+++w46Ojro3r07bG1tkZWVhWvXruHnn3/Gv//+i/nz5xd7HGlpaQgICMCECRO+WPIsISEBvXr1QkpKCnr16oUaNWogISEBDx48wJ49e9CvXz9p8mz+/PlQYqHjYhMYGIj27dszefaJ27dvY9SoUUhNTUW3bt3g4OAAALhz5w42btyIq1evYsuWLSqO8j+bN29WdQhFRkdHBwsWLAAAJCcn4+TJk1iyZAkiIiKwYsWKIrnGmzdvEBAQAHNz81zJs8uXL0MikWDmzJmwtrYukuvdvn0bmpqaRVJXjmbNmqF79+4QBAFRUVHYs2cPBg8ejMDAQLi5ucldz19//YVdu3YplNgxNzfH7du3c03d8SWo8toAEBQUBHNzczRu3LjAcoIgYM6cOdi7dy/GjRtX6OTkq1ev8P79e/To0QMVK1ZEWloawsLCMHbsWMybNw99+vQpVP2lzZ49e1CuXDkmz/LwzTffoE6dOtiyZQt++uknVYdDRKQW5P7WMWHCBCxevBhGRkaYMGFCgWUDAgIKHRgRlS4vXrzAlClTULVqVWzfvh0VK1aUHhswYACePXuGP//8U3UBFoHU1FQYGBjkeWz//v149eoV9uzZk2s14pSUFGhra0sff/x/dSWRSJCVlQVdXV1Vh6KUpKQkTJgwAZqamggJCUHNmjVljk+ZMgX79u0rkmulpaVBX1+/0PUUVY+skkBLSwvdu3eXPu7fvz++/fZbHDt2DN7e3qhUqZLSdWdnZ0MikRRYJi4uDgBgbGys9HU+VRzvhWrVqsk8T23btkW3bt0QFBSkUPJMETnPn46Ojsre3yKRSGXXzsrKwpEjR9C3b9/Plp0/fz6Cg4MxZsyYIhlC5+bmlqtdvby80LNnT2zdurXIkmcZGRnQ1taGhoZSs7tQMSjo+0d+OnbsiDVr1uD9+/cltuc7EVFpIvdPxY+/QBobGxe4EdHXZ9OmTUhNTcXChQtlEmc5rK2tMXjw4HzPX7NmjcwwpBwHDx6EnZ0doqKipPsiIiIwfPhwNGrUCE5OTnB3d5cuVBIVFYUmTZoA+JDIt7OzyzXX0KNHjzBp0iS4urrC0dERPXv2xOnTp/O87uXLlzFnzhw0adKkwF9Gnz9/Dk1NTTg7O+c6ZmRkJPOLXl7zIb179w7Tpk2Di4sLGjRogBkzZuD+/fu55vXJmVsmJiYG48aNQ7169dC4cWMsWbIEYrFYps7Nmzejb9++0uepZ8+eOHHihEwZOzs7pKamIiQkRPpc5cxFl9/cbHm1lZ2dHebNm4fDhw+jc+fOcHR0xN9//w3gw/A4Hx8fNG3aFHXq1EHnzp2xf//+XPXu2LEDnTt3Rt26ddGwYUP07NlTZSs7BwcHIyYmBt7e3rkSZwBgZmaGcePGSR///vvvGDVqFJo3b446derAw8MDa9euzdUmAwcORJcuXXDnzh0MGDAAdevWxfLly6VzOG3evBm7du1CmzZtULduXQwbNgzR0dEQBAFr165Fy5Yt4eTkhLFjxyIhISFX3Z/Oefby5UuMGTMGzs7OaNKkCRYtWoS///4bdnZ2uHTpUq64/v33XwwcOBB169ZFixYtsHHjRpn6MjMzsWrVKvTs2RP169eHs7Mz+vfvj4sXL+Z6jiQSCbZv346uXbvC0dERjRs3xvDhwxERESF3O+TQ0NCAq6ur9J6ADwkuX19fNG3aFI6OjujWrZvMcE8AMs/rtm3b4OHhAUdHR+zevRv/+9//AHxY5CjntX/w4EG4u7tLPy+aNGmS6/Nj165d6Ny5M+rUqYPmzZtj7ty5SEpK+uw95DXn2b179zBixAi4uLigXr16GDx4MG7evKnw8/PxNcqVKyf9vLx69SomTZqEVq1aoU6dOnBzc8OiRYuQnp4uPcfb2xu7du2Snp+zFfT8PXr0KM95x3I+n169eoXRo0ejXr16aNGihbT+Bw8eYNCgQXB2dkbr1q3zfH+/ePFC+vlct25d9O7dO9cfXgq6tjyfjRKJBNu2bZN+VjVt2hR+fn5ITEz87HN87do1vHv3Dk2bNi2w3IIFC7Br1y6MHj0aU6ZM+Wy9ytLU1ESVKlWQnJys1PmXLl2CnZ0djh49ihUrVqBFixaoW7cuUlJSAADHjx9Hz5494eTkhEaNGmHq1KmIiYnJs64XL15g+PDhcHZ2RvPmzREQECDTyzrnWh9/9gB5t+fbt2/h4+ODli1bSt9rY8eOlb623d3d8fDhQ1y+fFn6mi1ozseBAwfKvL4/3j43d15KSgoWLlwId3d31KlTB02aNMHQoUNx9+5dmXK3bt3CyJEj0bBhQzg7O6Nr167Yvn27TJnw8HD0798fzs7OaNCgAcaOHYtHjx7JlMn5Gfvvv//ihx9+QMOGDdG/f3/p8UOHDknbxNXVFVOmTEF0dHSuuJs2bYrU1FRcuHChwPsjIiL5yN3zzN/fP8//ExEBwJkzZ2BpaZmr11VRi4uLw/Dhw1GuXDmMGjUKZcqUQVRUFE6dOgUAMDU1xZw5czBnzhy0bdsWbdu2BfDf/EAPHz5Ev379UKlSJYwcORIGBgY4fvw4xo8fjzVr1kjL55g7dy5MTU0xfvx4pKam5huXubk5xGIxDh06hB49eih0TxKJBGPHjsXt27fRr18/1KhRA6dPn8a
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Patrimoine:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_patrimoine \n",
|
|||
|
|
"0 0.040 3.795 0.377 -1.0 5.872 48.0 0.0 1.0 0.0\n",
|
|||
|
|
"1 0.057 5.620 0.458 0.0 5.613 80.0 0.0 1.0 0.0\n",
|
|||
|
|
"2 0.041 4.016 0.463 1.0 5.188 90.0 0.0 1.0 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Global Bond\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.7570 0.3758 294\n",
|
|||
|
|
" 3 0.8452 0.2188 202\n",
|
|||
|
|
" 4 0.8204 0.3165 97\n",
|
|||
|
|
" 5 0.8294 0.3355 94\n",
|
|||
|
|
" 6 0.8277 0.3595 60\n",
|
|||
|
|
"→ K retenu : 3 (silhouette=0.8452)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_global_bon \n",
|
|||
|
|
"0 1244 72.5\n",
|
|||
|
|
"1 270 15.7\n",
|
|||
|
|
"2 202 11.8\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Global Bond:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_global_bon \n",
|
|||
|
|
"0 0.073 7.429 0.373 0.000 4.423 69.0 0.0 1.0 0.0\n",
|
|||
|
|
"1 0.042 3.957 0.432 -1.000 4.338 60.0 0.0 1.0 0.0\n",
|
|||
|
|
"2 0.066 3.125 0.570 0.955 4.353 71.0 0.0 1.0 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Credit\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.7000 0.5933 107\n",
|
|||
|
|
" 3 0.5964 0.6651 36\n",
|
|||
|
|
" 4 0.4836 0.7926 17\n",
|
|||
|
|
" 5 0.2673 1.1077 17\n",
|
|||
|
|
" 6 0.2966 1.0748 17\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.7000)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_credit \n",
|
|||
|
|
"0 107 10.5\n",
|
|||
|
|
"1 909 89.5\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Credit:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_credit \n",
|
|||
|
|
"0 0.821 4.720 1.113 0.586 9.596 0.0 1.0 0.0 0.123\n",
|
|||
|
|
"1 0.069 2.494 0.684 0.098 5.962 25.0 0.0 1.0 0.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Emerging Patrimoine\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.5535 1.0735 255\n",
|
|||
|
|
" 3 0.5964 0.7555 133\n",
|
|||
|
|
" 4 0.4753 0.9800 69\n",
|
|||
|
|
" 5 0.4225 1.1074 30\n",
|
|||
|
|
" 6 0.4453 1.0458 35\n",
|
|||
|
|
"→ K retenu : 3 (silhouette=0.5964)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_emerging_p \n",
|
|||
|
|
"0 850 74.9\n",
|
|||
|
|
"1 152 13.4\n",
|
|||
|
|
"2 133 11.7\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Emerging Patrimoine:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_emerging_p \n",
|
|||
|
|
"0 0.038 3.181 0.500 -0.613 5.206 83.0 0.000 1.000 0.000\n",
|
|||
|
|
"1 0.037 0.283 1.000 -0.142 5.668 29.0 0.926 0.074 0.000\n",
|
|||
|
|
"2 0.714 4.279 2.212 -0.115 9.002 0.0 0.218 0.782 0.063\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Grande Europe\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.3729 1.1301 232\n",
|
|||
|
|
" 3 0.3476 1.1497 217\n",
|
|||
|
|
" 4 0.2731 1.3479 165\n",
|
|||
|
|
" 5 0.2535 1.3399 147\n",
|
|||
|
|
" 6 0.2606 1.3154 118\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.3729)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_grande_eur \n",
|
|||
|
|
"0 232 16.7\n",
|
|||
|
|
"1 1154 83.3\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Grande Europe:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_grande_eur \n",
|
|||
|
|
"0 0.519 4.254 1.00 -0.034 7.587 0.0 0.398 0.602 0.034\n",
|
|||
|
|
"1 0.029 1.864 0.59 -0.524 4.093 81.0 0.000 1.000 0.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Court Terme\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.4725 0.9119 113\n",
|
|||
|
|
" 3 0.4110 0.8591 109\n",
|
|||
|
|
" 4 0.3617 1.1573 108\n",
|
|||
|
|
" 5 0.3467 1.2485 57\n",
|
|||
|
|
" 6 0.3690 1.1097 34\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.4725)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_court_terme \n",
|
|||
|
|
"0 412 78.5\n",
|
|||
|
|
"1 113 21.5\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABNwAAAGGCAYAAACg4ZwmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA/nBJREFUeJzs3XlcTPv/B/DXTBuloiJEoZStZCdcJPuWfd+yU659CVlCluyKriV07ZLlCl07V7jIHte1bzct2hQtc35/+DXfxlSmUabG6/l4zOPRfM45n/M+nfnMmXnP53M+IkEQBBAREREREREREVGeEKs6ACIiIiIiIiIiInXChBsREREREREREVEeYsKNiIiIiIiIiIgoDzHhRkRERERERERElIeYcCMiIiIiIiIiIspDTLgRERERERERERHlISbciIiIiIiIiIiI8hATbkRERERERERERHmICTciIiIiIiIiIqI8xIQbEf10HB0dMWPGDFWHoRIzZsyAo6OjqsMgNTNw4EAMHDhQ1WHQT+bjx49o1KgRjhw5oupQCgQbGxssWLBA1WHQ//vw4QPs7e1x/vx5VYdCREQqoqnqAIiI8srLly+xefNm/PXXX3j//j20tLRgbW2Ndu3aoXfv3ihSpEi+x5CcnIzNmzejfv36aNCgQb7vL0NMTAx8fX1x6dIlvH37Fnp6ejAzM0ODBg0wduxY6Onp/bBYcmPjxo2wsrKCk5OTqkMpcKKiorBlyxacPXsW7969g0gkQqVKleDk5IQBAwbAwMBA1SGqratXryIgIABhYWGIi4uDvr4+atasiW7duqF169Yqieno0aOIjo7GkCFDclxv3bp1WL9+/Tfrq1+/PgICAvIoOtXYsWMH9PT00KFDB2lZxvGHhobCyMhIWv7u3TsMHDgQ8fHx8Pf3R/Xq1ZXap0QiwaFDhxASEoLw8HDExcWhXLlyaN++PYYNGwYdHZ3vPq7CJCIiAvv27YOTkxOqVq2q6nAKlBIlSqBHjx5Ys2YNmjVrpupwiIhIBZhwIyK1cO7cOfz666/Q1tZGly5dYG1tjdTUVNy4cQPLly/Hv//+C09Pz3yPIzk5GevXr4erq+sPS7jFxsaie/fuSExMRPfu3VGpUiXExsbi0aNH2L17N/r27StNuHl6ekIQhB8SlyL8/PzQpk0bJty+cufOHYwcORJJSUno3LmzNDlw7949bNq0CdevX8fWrVtVHOX/bNmyRdUh5Jm1a9fCx8cHFSpUQO/evVG2bFnExsbi/PnzcHNzg7e3Nzp16vTD4/rjjz/w+PHjbybcWrVqBXNzc+nzpKQkzJs3D61atUKrVq2k5SYmJvkV6g+RmpqKHTt2YMiQIdDQ0Mhx3YiICAwaNAhxcXHflWwDvrzHz5w5E/b29ujTpw+MjY0RFhaGdevWITQ0FDt27IBIJFK6/sLm/fv3WL9+PczMzJhwy0Lfvn0REBCA0NBQNGrUSNXhEBHRD8aEGxEVeq9evcLEiRNRtmxZbN++HaVKlZIu69+/P168eIFz586pLsA8kJSUBF1d3SyXHThwAG/fvsXu3btRu3ZtmWWJiYnQ0tKSPs/8t7qSSCRITU0ttD1N4uPj4erqCg0NDQQFBcHS0lJm+cSJE7Fv37482VdycjKKFi363fVoa2vnQTSqd+LECfj4+KBNmzZYsWKFTHsZPnw4Ll68iLS0tB8aU05tPytVqlRBlSpVpM9jYmIwb9482NjYoEuXLj88nvxy7tw5xMTEoF27djmul5Fsi42NxdatW1GjRo3v2q+Wlpbce22vXr1gZmYmTbo5ODh81z4yFJT/NX0hCAI+f/6cq97ylpaWsLa2RlBQEBNuREQ/Id7DjYgKvc2bNyMpKQmLFi2SSbZlsLCwwODBg7Pdft26dbCxsZErP3jwIGxsbPD69Wtp2d27dzFs2DA0aNAAdnZ2cHR0xMyZMwEAr1+/ln6gXr9+PWxsbGBjY4N169ZJt3/y5AnGjx+P+vXrw9bWFt26dcPp06ez3O+1a9cwb948NGrUKMfhKC9fvoSGhgbs7e3llhUrVkwm8ZTVPdw+fPiAqVOnonbt2qhbty6mT5+Ohw8fwsbGBgcPHpTZtlatWoiIiMDYsWNRq1YtNGzYEEuXLkV6erpMnVu2bEGfPn2k/6du3brhxIkTMuvY2NggKSkJQUFB0v9Vxr31srvXXFbnKuO+RUeOHEGHDh1ga2uLixcvAvjyZXvmzJlwcHBAjRo10KFDBxw4cECu3oCAAHTo0AE1a9ZEvXr10K1bNxw9ejSrf3e+27NnDyIiIjBjxgy5ZBvwpWfS2LFjpc9PnTqFkSNHokmTJqhRowacnJzg4+Mjd04GDhyIjh074t69e+jfvz9q1qyJlStX4vXr17CxscGWLVuwc+dOtGzZEjVr1oSLiwvevXsHQRDg4+ODX375BXZ2dhgzZgxiY2Pl6v76Hm5v3rzB6NGjYW9vj0aNGmHx4sW4ePEibGxscPXqVbm4/v33XwwcOBA1a9ZE06ZNsWnTJpn6UlJSsGbNGnTr1g116tSBvb09+vXrhytXrsj9jyQSCbZv345OnTrB1tYWDRs2xLBhw3D37t0c//dr1qxB8eLFsXjx4iyT002bNkWLFi2kz6Ojo+Hu7g4HBwfY2tqic+fOCAoKktnm6tWrcscMQPp/z6qNvXz5EiNGjECtWrUwZcoUDBw4EOfOncObN2+kbeV778X4ve9FGeft4cOHGDBgAGrWrIlWrVpJ2/m1a9fQs2dP2NnZoU2bNrh8+bJcDIq2z6ycOnUKZmZmMr35vvb+/XsMGjQI0dHR2LJlC2xtbRX992RLW1tb7ocNANLeg0+ePFGq3uzOPfAl8bZkyRI0a9YMNWrUQJs2bbBly5ZseysfOXIEbdq0kZ7Xv//+W25fir6//vXXX+jbty/q1q2LWrVqoU2bNli5ciWAL6/tHj16AABmzpwpfW1mfk1nlvGaz+7xLTldfzMo0vbT0tLg4+MDJycn1KhRA46Ojli5ciVSUlJk6nJ0dMSoUaNw8eJFdOvWDXZ2dtizZw+ALz+MLFq0SHpOWrVqhd9++w0SiUQubgcHB5w9e7ZA9S4nIqIfgz3ciKjQO3v2LMqXL5/ll6C8FB0djWHDhqFEiRIYOXIkDAwM8Pr1a/z5558AACMjI8ybN09u+FbGF4nHjx+jb9++MDU1xYgRI6Crq4vjx49j3LhxWLduncxwLwCYP38+jIyMMG7cOCQlJWUbl5mZGdLT03H48GF07do1V8ckkUgwZswY3LlzB3379kWlSpVw+vRpTJ8+Pcv109PTMWzYMNjZ2WHatGkIDQ3F1q1bUb58efTr10+63o4dO+Do6IhOnTohNTUVx44dw6+//go/Pz80b94cALBs2TLMnj0bdnZ26NWrFwDk+OU5J1euXMHx48fRv39/lChRAmZmZoiKikKvXr0gEonQv39/GBkZ4cKFC5g1axYSExOlQ/P27duHhQsXok2bNhg0aBA+f/6MR48e4fbt2yoZOnjmzBkUKVIEbdq0UWj9oKAg6OrqYujQodDV1cWVK1ewdu1aJCYmyp3H2NhYjBgxAh06dEDnzp1hbGwsXXb06FGkpqZi4MCBiI2NxebNmzFhwgQ0bNgQV69exYgRI/DixQv8/vvvWLp0Kby8vLKNKSkpCYMHD0ZkZCQGDRoEExMT/PHHH3JJpwxxcXEYPnw4WrVqhXbt2uHkyZPw9vaGtbW1NMGTmJiI/fv3o2PHjujZsyc+fvyIAwcOYPjw4di/f7/McLZZs2bh4MGD+OWXX9CjRw+kp6fj+vXruH37drZJl+fPn+Pp06fo3r07ihUr9s3/+6dPnzBw4EC8fPkS/fv3R7ly5XDixAnMmDED8fHxOSb5c5KWloZhw4ahTp06mD59OooUKYKSJUsiISEB//33nzTB8D33Zcyr96K4uDiMHj0a7du3R9u2bbF7925MmjQJEokEixcvRp8+fdCxY0ds2bIF48ePx7lz56T/W0XbZ3bCwsJyHBoaHR2N8ePHIyoqClu3boWdnZ3cOsnJyUhOTv7m/0tDQwOGhoY5rhM
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Court Terme:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_court_terme \n",
|
|||
|
|
"0 0.053 7.561 0.333 -0.023 1.924 86.5 0.00 1.00 0.000\n",
|
|||
|
|
"1 0.254 5.379 1.000 0.286 4.609 0.0 0.89 0.11 0.008\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Long-Short European Equities\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.3490 1.3504 188\n",
|
|||
|
|
" 3 0.3816 1.0159 119\n",
|
|||
|
|
" 4 0.3969 0.9229 54\n",
|
|||
|
|
" 5 0.3535 1.0391 54\n",
|
|||
|
|
" 6 0.3742 1.0386 46\n",
|
|||
|
|
"→ K retenu : 4 (silhouette=0.3969)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_long-short \n",
|
|||
|
|
"0 107 17.7\n",
|
|||
|
|
"1 54 8.9\n",
|
|||
|
|
"2 333 55.0\n",
|
|||
|
|
"3 111 18.3\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABM8AAAGGCAYAAABseKbNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdYU9cbwPFv2AKKTJWpgKAiuHFrxdXWUUdr3bta92yddW+ttYqr7j3qwGqdtfqzrYp1b+u2LpQle0ju7w9KNAIaIojj/TxPHs255568l5vcJG/OUCmKoqCjYsWKoVKpMt2uUqno06cPPXr00LVJIYQQQgghhBBCCCHeWqqsJM+OHTuGoih06NCBOXPmYGVlpdlmbGyMo6MjBQoUyJFAhRBCCCGEEEIIIYR404yyUtnf35+nT5/StGlTSpYsSaFChXIqLiGEEEIIIYQQQgghcp1BVncwMjJi9+7dpKSk5EQ8QgghhBBCCCGEEEK8NbKcPAOoVKkSf//9d3bHIoQQQgghhBBCCCHEWyVLwzbT1KhRg++//55//vkHHx8f8uTJo7W9du3a2RKcEEIIIYQQQgghhBC5KUsLBqQpVqxY5g2qVFy6dOm1ghJCCCGEEEIIIYQQ4m2gV/JMCCGEEEIIIYQQQogPgV5zngkhhBBCCCGEEEII8SHQa84zgGPHjrF06VKuX78OgIeHB127dqV8+fLZFpwQQgghhBBCCCGEELlJr55n27Zto1OnTpiZmdGuXTvatWuHmZkZHTt2ZPv27dkdoxDiFQICAhg6dGhuh5Erhg4dSkBAQG6HId4zae9tInsEBQXx8ccf4+Pjk+Uf2TJ6jXt7ezNnzpzsDPG9NnToUMqUKZPbYYhcFhsbS+XKlfnll19yO5Rcd/fuXby9vVmyZEluhyL+c+3aNUqUKME///yT26EIIUSG9Op5tmDBAr755hs6duyoKWvfvj3Lli1j3rx5NGrUKLviE+KDdufOHRYvXsxff/3Fo0ePMDY2xsvLi08++YQvv/wSMzOzHI8hPj6exYsX4+/vT8WKFXP88dKEh4czb948/vzzT+7fv4+FhQVOTk5UrFiRnj17YmFh8cZiyYoFCxbg6elJnTp1cjuUt05oaChLlizhwIEDPHjwAJVKhbu7O3Xq1KFt27bky5cvt0N87wwdOpStW7dq7ltYWODs7EyTJk1o27YtJiYm2fI4ISEhbNy4kTp16lC8eHGtbdevX2fYsGFUr16dbt26vZHrVlZ5e3vTpk0bRo0alduhZElsbCxLlixh79693L17F1NTUwoWLEiFChX46quvKFCgwBuPKavvGcHBwbRv3z7T7TNnzqRBgwbZGeIHa+XKlVhYWGj9PefMmUNgYCBHjhzBxsZGU/7gwQPatWtHVFQUy5Ytw8fHJ1tiSE5O5rPPPuP69et8++23dOnSJVvafVdcu3aNXbt20bRpU5ydnXM7nLeKp6cnNWvWZPbs2QQGBuZ2OEIIkY5eybN///2XWrVqpSsPCAhg5syZrx2UEAIOHjxIv379MDEx4bPPPsPLy4vk5GROnDjB9OnTuXbtGuPHj8/xOOLj4wkMDKR3795vLHkWGRlJ8+bNiYmJoXnz5ri7uxMZGcmVK1dYt24drVq10iTPxo8fz9u07snChQupX7++JM9ecPbsWbp160ZcXByNGzfWfBE7f/48ixYt4vjx4yxdujSXo3zmfeqNYGJiwoQJEwCIjo5mz549TJ06lXPnzvHDDz9ky2M8evSIwMBAnJyc0iXPjh07hlqtZsSIEbi5uWXL4509exZDQ8NsaetdlZycTNu2bblx44YmGRoXF8fVq1fZsWMHdevWzbXkmT7vGe3atcPX1zddeenSpbMxug9XcnIyK1eupGPHjq987YSEhNC+fXuePHmSrYkzgNWrV/PgwYNsa+9dc+3aNQIDA/H395fkWQZatmxJt27duHPnDq6urrkdjhBCaNEreVaoUCGOHDmS7kPw4cOHKVSoULYEJsSH7N9//2XAgAE4OjqyYsUKHBwcNNvatGnD7du3OXjwYO4FmA3i4uIwNzfPcNumTZu4f/8+69ato2zZslrbYmJiMDY21tx//v/vK7VaTXJyMqamprkdil6ioqLo3bs3hoaGbN26FQ8PD63tAwYMYOPGjdnyWPHx8eTJk+e128muHllvAyMjIz777DPN/datW/PFF1+wc+dOhg4d+loJlqdPn6JWq19aJywsDIC8efPq/TgveldfC9npt99+4+LFi8yYMSNdj//ExESSk5PfaDxp1yl9lS9fno8//jgbI9L2svecD8HBgwcJDw/nk08+eWm9tMRZZGQkS5cupWTJktkWQ1hYGHPnzqVr167Mnj0729pNk13Xf5E99PnsUqVKFaysrNi6dSv9+vXLweiEECLr9JrzrFOnTkyYMIHRo0cTFBREUFAQo0aNYtKkSXTu3Dm7YxTig7N48WLi4uKYOHGiVuIsjZubGx06dMh0/zlz5uDt7Z2ufMuWLXh7e3P37l1N2blz5+jSpQsVK1bEz8+PgIAAhg0bBqTOCVK5cmUAAgMD8fb2TjfX0PXr1+nbty/+/v74+vrSrFkz9u/fn+HjHjt2jDFjxlC5cmVq1qyZafx37tzB0NAwwx4HlpaWWh/EMpoPKSIigm+++YayZctSvnx5hgwZwuXLl/H29mbLli1a+5YpU4aQkBB69uxJmTJlqFSpElOnTiUlJUWrzSVLltCyZUvN36lZs2bs3r1bq463tzdxcXFs3bpV87dKm4sus7nZMjpX3t7ejBs3jl9++YUGDRrg6+vLH3/8AaR+sRk2bBhVqlShZMmSNGjQgE2bNqVrd9WqVTRo0IBSpUpRoUIFmjVrlmtzUq5fv56QkBCGDh2aLnEGYGdnR8+ePTX3f/vtN7p160a1atUoWbIkderUYe7cuenOSbt27WjYsCHnz5+nTZs2lCpVipkzZ2rNZbNmzRpq165NqVKl6Ny5Mw8ePEBRFObOnUuNGjXw8/OjR48eREZGpmv7xTnP7t27x9dff03p0qWpXLkykyZN4o8//sDb25vg4OB0cV27do127dpRqlQpqlevzqJFi7TaS0pK4scff6RZs2aUK1eO0qVL07p1a44ePZrub6RWq1mxYgWNGjXC19eXSpUq0aVLF86dO6fzeUhjYGCAv7+/5pgg9Uvt8OHDqVKlCr6+vjRu3FhruCdozxG0fPly6tSpg6+vL2vXruXzzz8HYNiwYZrn/pYtWwgICNBcLypXrpzu+rFmzRoaNGhAyZIlqVatGmPHjiUqKuqVx5DRnGcXL16ka9eulC1bljJlytChQwdOnz6d5b9PZuLi4pgyZQo1a9akZMmS1K9fnyVLlqTr+Zr2+v3tt99o2LCh5nV66NChdG0GBwfTrFkzfH19qVOnDuvXr8/0+v2if//9FyDdDwyQmly0tLRMV67LtS6rx/n8dWrdunWvfM/QV9rz7/lr+POxPP8YaX/Da9euMWjQICpUqEDr1q2B1ITv3LlzqVOnDiVLltSMmkhKStJqMyAggO7du/Pnn3/y2Wef4evry6effsrevXvTPX5UVBQTJ07U/M3q1q3LTz/9lC6xrMv7SNrx6Poc0tVvv/2Gk5PTS3vzPHr0iPbt2xMWFsaSJUsy7An4OmbMmEGRIkVo3Ljxa7eV2fUfdLuePW/58uXUqlULPz8/2rZtm27OrczmwMzoff3XX3+lWbNmlClThrJly9KoUSNWrFgBpH4WSksItW/fXvP6eP7943nBwcGaOi/edJnr9a+//qJVq1aUL1+eMmXKUL9+/XQjhBITE5kzZw7169fH19eXatWq0bt3b+7cuaOp8zrXhKx+djE2Nsbf3z/d50ghhHgb6NXzrHXr1tjb27N06VLNm767uzs//PCDDFUSIhscOHAAFxeXDL8UZaewsDC6dOmCtbU13bp1I1++fNy9e5d9+/YBYGNjw5gxYxgzZgx169albt26AJovdlevXqVVq1YUKFCAr776CnNzc3bt2kWvXr2YM2eOpn6asWPHYmNjQ69evYiLi8s0LicnJ1JSUti2bRt
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Long-Short European Equities:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_long-short \n",
|
|||
|
|
"0 0.125 1.688 1.000 0.537 7.833 3.0 1.0 0.0 0.0\n",
|
|||
|
|
"1 0.041 4.727 0.348 1.000 5.110 56.0 0.0 1.0 0.0\n",
|
|||
|
|
"2 0.119 6.673 0.635 0.000 6.731 32.0 0.0 1.0 0.0\n",
|
|||
|
|
"3 0.056 3.823 0.412 -1.000 4.991 29.0 0.0 1.0 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Portfolio Climate Transition\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.6663 0.6410 156\n",
|
|||
|
|
" 3 0.5630 0.7450 57\n",
|
|||
|
|
" 4 0.4067 0.9958 37\n",
|
|||
|
|
" 5 0.2259 1.2463 37\n",
|
|||
|
|
" 6 0.2268 1.2256 18\n",
|
|||
|
|
"→ K retenu : 2 (silhouette=0.6663)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_portfolio_climate_tr \n",
|
|||
|
|
"0 156 12.2\n",
|
|||
|
|
"1 1122 87.8\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Portfolio Climate Transition:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_portfolio_climate_tr \n",
|
|||
|
|
"0 0.679 5.287 0.962 -0.049 7.379 7.0 0.0 1.0 0.044\n",
|
|||
|
|
"1 0.025 1.503 0.583 -0.792 4.215 92.0 0.0 1.0 0.000\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"FUND : Carmignac Credit 2027\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" k silhouette davies_bouldin min_cluster_size\n",
|
|||
|
|
" 2 0.2840 1.4117 134\n",
|
|||
|
|
" 3 0.3331 1.1580 49\n",
|
|||
|
|
" 4 0.3456 1.0712 39\n",
|
|||
|
|
" 5 0.3182 1.2795 40\n",
|
|||
|
|
" 6 0.3321 1.1970 26\n",
|
|||
|
|
"→ K retenu : 4 (silhouette=0.3456)\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_carmignac_credit_2027 \n",
|
|||
|
|
"0 47 15.9\n",
|
|||
|
|
"1 118 40.0\n",
|
|||
|
|
"2 91 30.8\n",
|
|||
|
|
"3 39 13.2\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Medians — Carmignac Credit 2027:\n",
|
|||
|
|
" flow_freq gross_flow_to_aum avg_n_isin_held flow_direction_balance log_aum_qty_mean months_since_last_tx_fund aum_final_to_peak aum_drawdown_last buy_on_perf_rate\n",
|
|||
|
|
"cluster_carmignac_credit_2027 \n",
|
|||
|
|
"0 0.130 5.183 0.634 0.041 6.130 7.0 0.000 1.000 0.0\n",
|
|||
|
|
"1 0.851 2.353 1.577 0.574 9.763 0.0 0.842 0.158 0.0\n",
|
|||
|
|
"2 0.083 1.024 1.000 1.000 6.967 16.0 1.000 0.000 0.0\n",
|
|||
|
|
"3 0.000 0.000 0.912 -0.869 6.620 130.0 0.405 0.595 0.0\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"SUMMARY — Fund-level clustering\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" Carmignac Patrimoine : K=2, sil=0.5054, n=3153\n",
|
|||
|
|
" Carmignac Sécurité : K=2, sil=0.5729, n=1622\n",
|
|||
|
|
" Carmignac Investissement : K=2, sil=0.4306, n=2192\n",
|
|||
|
|
" Carmignac Portfolio Sécurité : K=2, sil=0.8029, n=1161\n",
|
|||
|
|
" Carmignac Portfolio Flexible Bond : K=2, sil=0.6085, n=1087\n",
|
|||
|
|
" Carmignac Emergents : K=3, sil=0.4735, n=1779\n",
|
|||
|
|
" Carmignac Portfolio Patrimoine : K=3, sil=0.6446, n=1143\n",
|
|||
|
|
" Carmignac Portfolio Global Bond : K=3, sil=0.8452, n=1716\n",
|
|||
|
|
" Carmignac Portfolio Credit : K=2, sil=0.7000, n=1016\n",
|
|||
|
|
" Carmignac Portfolio Emerging Patrimoine : K=3, sil=0.5964, n=1135\n",
|
|||
|
|
" Carmignac Portfolio Grande Europe : K=2, sil=0.3729, n=1386\n",
|
|||
|
|
" Carmignac Court Terme : K=2, sil=0.4725, n=525\n",
|
|||
|
|
" Carmignac Portfolio Long-Short European : K=4, sil=0.3969, n=605\n",
|
|||
|
|
" Carmignac Portfolio Climate Transition : K=2, sil=0.6663, n=1278\n",
|
|||
|
|
" Carmignac Credit 2027 : K=4, sil=0.3456, n=295\n",
|
|||
|
|
"\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
"ARI — coherence: global clustering × fund clustering\n",
|
|||
|
|
"============================================================\n",
|
|||
|
|
" Carmignac Patrimoine : ARI=0.0238 (n=3000)\n",
|
|||
|
|
" Carmignac Sécurité : ARI=0.0119 (n=1477)\n",
|
|||
|
|
" Carmignac Investissement : ARI=0.0426 (n=2053)\n",
|
|||
|
|
" Carmignac Portfolio Sécurité : ARI=0.0820 (n=1047)\n",
|
|||
|
|
" Carmignac Portfolio Flexible Bond : ARI=-0.0448 (n=944)\n",
|
|||
|
|
" Carmignac Emergents : ARI=0.0153 (n=1640)\n",
|
|||
|
|
" Carmignac Portfolio Patrimoine : ARI=0.0118 (n=1029)\n",
|
|||
|
|
" Carmignac Portfolio Global Bond : ARI=0.0799 (n=1584)\n",
|
|||
|
|
" Carmignac Portfolio Credit : ARI=0.0090 (n=901)\n",
|
|||
|
|
" Carmignac Portfolio Emerging Patrimoine : ARI=-0.0332 (n=996)\n",
|
|||
|
|
" Carmignac Portfolio Grande Europe : ARI=-0.0264 (n=1247)\n",
|
|||
|
|
" Carmignac Court Terme : ARI=-0.0347 (n=423)\n",
|
|||
|
|
" Carmignac Portfolio Long-Short European : ARI=0.0516 (n=495)\n",
|
|||
|
|
" Carmignac Portfolio Climate Transition : ARI=-0.0456 (n=1141)\n",
|
|||
|
|
" Carmignac Credit 2027 : ARI=0.0470 (n=238)\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"print(\"=== Available funds (top 20 by AUM) ===\")\n",
|
|||
|
|
"top_funds_aum = (\n",
|
|||
|
|
" df_aum.groupby(FUND_COL)[AUM_VAL_COL].sum()\n",
|
|||
|
|
" .sort_values(ascending=False)\n",
|
|||
|
|
" .head(20)\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(top_funds_aum.to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 1. Select top funds by AUM with minimum account count\n",
|
|||
|
|
"# Critères : top 15 par AUM total + au moins 20 clients\n",
|
|||
|
|
"min_clients_per_fund = 20\n",
|
|||
|
|
"n_top_funds = 15\n",
|
|||
|
|
"\n",
|
|||
|
|
"aum_by_fund_total = df_aum.groupby(FUND_COL)[AUM_VAL_COL].sum()\n",
|
|||
|
|
"clients_by_fund = df_aum.groupby(FUND_COL)[ID_COL].nunique()\n",
|
|||
|
|
"\n",
|
|||
|
|
"valid_funds = (\n",
|
|||
|
|
" aum_by_fund_total[\n",
|
|||
|
|
" clients_by_fund >= min_clients_per_fund\n",
|
|||
|
|
" ]\n",
|
|||
|
|
" .sort_values(ascending=False)\n",
|
|||
|
|
" .head(n_top_funds)\n",
|
|||
|
|
" .index.tolist()\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(f\"\\nSelected funds ({len(valid_funds)}) :\")\n",
|
|||
|
|
"for f in valid_funds:\n",
|
|||
|
|
" print(f\" {f} : {clients_by_fund[f]} clients, AUM={aum_by_fund_total[f]:.0f}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 2. Build account × fund monthly panel\n",
|
|||
|
|
"df_rel_m_fund = df_rel_m.copy()\n",
|
|||
|
|
"df_rel_m_fund = df_rel_m_fund.merge(\n",
|
|||
|
|
" df_aum[[ID_COL, ISIN_COL, \"month\", FUND_COL]].drop_duplicates(),\n",
|
|||
|
|
" on=[ID_COL, ISIN_COL, \"month\"],\n",
|
|||
|
|
" how=\"left\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Monthly panel par compte x fund\n",
|
|||
|
|
"tmp_fund = df_rel_m_fund.copy()\n",
|
|||
|
|
"tmp_fund[\"isin_held_flag\"] = (tmp_fund[\"aum_qty\"] > 0).astype(int)\n",
|
|||
|
|
"tmp_fund[\"isin_active_flag\"] = (tmp_fund[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month_fund = (\n",
|
|||
|
|
" tmp_fund.dropna(subset=[FUND_COL])\n",
|
|||
|
|
" .groupby([ID_COL, FUND_COL, \"month\"], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" aum_qty = (\"aum_qty\", \"sum\"),\n",
|
|||
|
|
" net_flow_qty = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" gross_flow_qty = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" sub_qty = (\"sub_qty\", \"sum\"),\n",
|
|||
|
|
" red_qty = (\"red_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" n_isin_held = (\"isin_held_flag\", \"sum\"),\n",
|
|||
|
|
" ret_fund_m = (\"ret_fund_m\", \"mean\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .sort_values([ID_COL, FUND_COL, \"month\"])\n",
|
|||
|
|
" .reset_index(drop=True)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_month_fund[\"active_month\"] = (df_month_fund[\"gross_flow_qty\"] > 0).astype(int)\n",
|
|||
|
|
"df_month_fund[\"flow_direction\"] = np.where(\n",
|
|||
|
|
" df_month_fund[\"gross_flow_qty\"] > 0,\n",
|
|||
|
|
" df_month_fund[\"net_flow_qty\"] / df_month_fund[\"gross_flow_qty\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month_fund[\"sub_share\"] = np.where(\n",
|
|||
|
|
" df_month_fund[\"gross_flow_qty\"] > 0,\n",
|
|||
|
|
" df_month_fund[\"sub_qty\"] / df_month_fund[\"gross_flow_qty\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_month_fund[\"aum_peak\"] = df_month_fund.groupby(\n",
|
|||
|
|
" [ID_COL, FUND_COL]\n",
|
|||
|
|
")[\"aum_qty\"].cummax()\n",
|
|||
|
|
"df_month_fund[\"aum_drawdown\"] = np.where(\n",
|
|||
|
|
" df_month_fund[\"aum_peak\"] > 0,\n",
|
|||
|
|
" 1 - df_month_fund[\"aum_qty\"] / df_month_fund[\"aum_peak\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Lag performance pour réactivité\n",
|
|||
|
|
"df_month_fund[\"ret_fund_lag1\"] = df_month_fund.groupby(\n",
|
|||
|
|
" [ID_COL, FUND_COL]\n",
|
|||
|
|
")[\"ret_fund_m\"].shift(1)\n",
|
|||
|
|
"df_month_fund[\"buy_on_perf\"] = (\n",
|
|||
|
|
" (df_month_fund[\"net_flow_qty\"] > 0) &\n",
|
|||
|
|
" (df_month_fund[\"ret_fund_lag1\"] > 0)\n",
|
|||
|
|
").astype(int)\n",
|
|||
|
|
"df_month_fund[\"has_perf\"] = df_month_fund[\"ret_fund_lag1\"].notna().astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\ndf_month_fund shape:\", df_month_fund.shape)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 3. Feature engineering per account × fund\n",
|
|||
|
|
"reference_date = df_month_fund[\"month\"].max()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# months_since_last_tx par fund\n",
|
|||
|
|
"last_active_fund = (\n",
|
|||
|
|
" df_month_fund[df_month_fund[\"active_month\"] == 1]\n",
|
|||
|
|
" .groupby([ID_COL, FUND_COL])[\"month\"]\n",
|
|||
|
|
" .max()\n",
|
|||
|
|
" .reset_index(name=\"last_active_month\")\n",
|
|||
|
|
")\n",
|
|||
|
|
"last_active_fund[\"months_since_last_tx_fund\"] = (\n",
|
|||
|
|
" (reference_date.to_period(\"M\") -\n",
|
|||
|
|
" last_active_fund[\"last_active_month\"].dt.to_period(\"M\"))\n",
|
|||
|
|
" .apply(lambda x: x.n)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"df_client_fund = (\n",
|
|||
|
|
" df_month_fund.groupby([ID_COL, FUND_COL], as_index=False)\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_months = (\"month\", \"nunique\"),\n",
|
|||
|
|
" n_active_months = (\"active_month\", \"sum\"),\n",
|
|||
|
|
" flow_freq = (\"active_month\", \"mean\"),\n",
|
|||
|
|
" aum_qty_mean = (\"aum_qty\", \"mean\"),\n",
|
|||
|
|
" aum_qty_max = (\"aum_qty\", \"max\"),\n",
|
|||
|
|
" aum_qty_last = (\"aum_qty\", \"last\"),\n",
|
|||
|
|
" gross_flow_qty_sum = (\"gross_flow_qty\", \"sum\"),\n",
|
|||
|
|
" net_flow_qty_sum = (\"net_flow_qty\", \"sum\"),\n",
|
|||
|
|
" n_tx_total = (\"n_tx\", \"sum\"),\n",
|
|||
|
|
" avg_n_isin_held = (\"n_isin_held\", \"mean\"),\n",
|
|||
|
|
" flow_direction_mean = (\"flow_direction\", \"mean\"),\n",
|
|||
|
|
" sub_share_mean = (\"sub_share\", \"mean\"),\n",
|
|||
|
|
" aum_drawdown_last = (\"aum_drawdown\", \"last\"),\n",
|
|||
|
|
" buy_on_perf_months = (\"buy_on_perf\", \"sum\"),\n",
|
|||
|
|
" has_perf_months = (\"has_perf\", \"sum\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Merge months_since_last_tx\n",
|
|||
|
|
"df_client_fund = df_client_fund.merge(\n",
|
|||
|
|
" last_active_fund[[ID_COL, FUND_COL, \"months_since_last_tx_fund\"]],\n",
|
|||
|
|
" on=[ID_COL, FUND_COL], how=\"left\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"max_months = df_client_fund[\"months_since_last_tx_fund\"].max()\n",
|
|||
|
|
"df_client_fund[\"months_since_last_tx_fund\"] = (\n",
|
|||
|
|
" df_client_fund[\"months_since_last_tx_fund\"].fillna(max_months + 1)\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Ratios protégés\n",
|
|||
|
|
"df_client_fund[\"gross_flow_to_aum\"] = np.where(\n",
|
|||
|
|
" df_client_fund[\"aum_qty_mean\"] > 1,\n",
|
|||
|
|
" df_client_fund[\"gross_flow_qty_sum\"] / df_client_fund[\"aum_qty_mean\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_fund[\"flow_direction_balance\"] = np.where(\n",
|
|||
|
|
" df_client_fund[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" df_client_fund[\"net_flow_qty_sum\"] / df_client_fund[\"gross_flow_qty_sum\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_fund[\"aum_final_to_peak\"] = np.where(\n",
|
|||
|
|
" df_client_fund[\"aum_qty_max\"] > 0,\n",
|
|||
|
|
" np.clip(df_client_fund[\"aum_qty_last\"] / df_client_fund[\"aum_qty_max\"], 0, 1),\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_fund[\"log_aum_qty_mean\"] = np.log1p(\n",
|
|||
|
|
" df_client_fund[\"aum_qty_mean\"].clip(lower=0)\n",
|
|||
|
|
")\n",
|
|||
|
|
"df_client_fund[\"buy_on_perf_rate\"] = np.where(\n",
|
|||
|
|
" df_client_fund[\"has_perf_months\"] >= 6,\n",
|
|||
|
|
" df_client_fund[\"buy_on_perf_months\"] / df_client_fund[\"has_perf_months\"],\n",
|
|||
|
|
" np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Filtre qualité\n",
|
|||
|
|
"df_client_fund = df_client_fund[\n",
|
|||
|
|
" (df_client_fund[\"n_months\"] >= 6) &\n",
|
|||
|
|
" (df_client_fund[\"aum_qty_mean\"] > 0) &\n",
|
|||
|
|
" (df_client_fund[FUND_COL].isin(valid_funds))\n",
|
|||
|
|
"].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"df_client_fund shape:\", df_client_fund.shape)\n",
|
|||
|
|
"print(\"\\nComptes par fund :\")\n",
|
|||
|
|
"print(df_client_fund.groupby(FUND_COL)[ID_COL].nunique().sort_values(ascending=False))\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 4. Features pour le clustering par fund ───────────────────────────────\n",
|
|||
|
|
"fund_features = [\n",
|
|||
|
|
" \"flow_freq\",\n",
|
|||
|
|
" \"gross_flow_to_aum\",\n",
|
|||
|
|
" \"avg_n_isin_held\",\n",
|
|||
|
|
" \"flow_direction_balance\",\n",
|
|||
|
|
" \"log_aum_qty_mean\",\n",
|
|||
|
|
" \"months_since_last_tx_fund\",\n",
|
|||
|
|
" \"aum_final_to_peak\",\n",
|
|||
|
|
" \"aum_drawdown_last\",\n",
|
|||
|
|
" \"buy_on_perf_rate\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 5. Clustering loop across all selected funds\n",
|
|||
|
|
"FUND_RESULTS = {}\n",
|
|||
|
|
"\n",
|
|||
|
|
"for fund in valid_funds:\n",
|
|||
|
|
" print(f\"\\n{'='*60}\")\n",
|
|||
|
|
" print(f\"FUND : {fund}\")\n",
|
|||
|
|
" print(f\"{'='*60}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" df_f = df_client_fund[df_client_fund[FUND_COL] == fund].copy()\n",
|
|||
|
|
" feats = [c for c in fund_features if c in df_f.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Preprocessing\n",
|
|||
|
|
" d = df_f.copy()\n",
|
|||
|
|
" d[\"flow_direction_balance\"] = d[\"flow_direction_balance\"].fillna(0)\n",
|
|||
|
|
" d[\"buy_on_perf_rate\"] = d[\"buy_on_perf_rate\"].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"avg_n_isin_held\", \"months_since_last_tx_fund\",\n",
|
|||
|
|
" \"aum_drawdown_last\", \"aum_final_to_peak\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" vals = d[col].to_numpy(dtype=float)\n",
|
|||
|
|
" med = np.nanmedian(vals)\n",
|
|||
|
|
" mad = np.nanmedian(np.abs(vals - med)) * 1.4826\n",
|
|||
|
|
" if mad > 0:\n",
|
|||
|
|
" d[col] = np.clip(vals, med - 3*mad, med + 3*mad)\n",
|
|||
|
|
" else:\n",
|
|||
|
|
" d[col] = np.clip(vals, 0, np.nanpercentile(vals, 95))\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"gross_flow_to_aum\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" vals = d[col].to_numpy(dtype=float)\n",
|
|||
|
|
" d[col] = np.log1p(np.clip(vals, 0, np.nanpercentile(vals, 90)))\n",
|
|||
|
|
"\n",
|
|||
|
|
" for col in [\"flow_freq\"]:\n",
|
|||
|
|
" if col not in d.columns:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" vals = d[col].to_numpy(dtype=float)\n",
|
|||
|
|
" d[col] = np.log1p(np.clip(vals, 0, None))\n",
|
|||
|
|
"\n",
|
|||
|
|
" X_f = d[feats].fillna(d[feats].median()).to_numpy()\n",
|
|||
|
|
" X_f_scaled = RobustScaler().fit_transform(X_f)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # K-selection\n",
|
|||
|
|
" best_k = 2\n",
|
|||
|
|
" best_sil = -1\n",
|
|||
|
|
" rows_k = []\n",
|
|||
|
|
" max_k = min(6, len(df_f) // 20)\n",
|
|||
|
|
"\n",
|
|||
|
|
" for k in range(2, max_k + 1):\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=30, random_state=RANDOM_STATE)\n",
|
|||
|
|
" labels = km.fit_predict(X_f_scaled)\n",
|
|||
|
|
" # Vérifier que les clusters ne sont pas trop déséquilibrés\n",
|
|||
|
|
" sizes = pd.Series(labels).value_counts()\n",
|
|||
|
|
" if sizes.min() < 10:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" sil = silhouette_score(X_f_scaled, labels)\n",
|
|||
|
|
" db = davies_bouldin_score(X_f_scaled, labels)\n",
|
|||
|
|
" rows_k.append({\n",
|
|||
|
|
" \"k\": k,\n",
|
|||
|
|
" \"silhouette\": round(sil, 4),\n",
|
|||
|
|
" \"davies_bouldin\": round(db, 4),\n",
|
|||
|
|
" \"min_cluster_size\": sizes.min(),\n",
|
|||
|
|
" })\n",
|
|||
|
|
" if sil > best_sil:\n",
|
|||
|
|
" best_sil = sil\n",
|
|||
|
|
" best_k = k\n",
|
|||
|
|
"\n",
|
|||
|
|
" df_k = pd.DataFrame(rows_k)\n",
|
|||
|
|
" print(df_k.to_string(index=False))\n",
|
|||
|
|
" print(f\"→ K retenu : {best_k} (silhouette={best_sil:.4f})\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Clustering final\n",
|
|||
|
|
" km_final = KMeans(n_clusters=best_k, n_init=50, random_state=RANDOM_STATE)\n",
|
|||
|
|
" cluster_col = f\"cluster_{fund.lower().replace(' ','_')[:30]}\"\n",
|
|||
|
|
" df_f[cluster_col] = km_final.fit_predict(X_f_scaled)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Sizes\n",
|
|||
|
|
" counts = df_f[cluster_col].value_counts().sort_index()\n",
|
|||
|
|
" props = counts / counts.sum() * 100\n",
|
|||
|
|
" print(pd.DataFrame({\"n_comptes\": counts, \"pct\": props.round(1)}))\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Heatmap\n",
|
|||
|
|
" profile_vars_fund = [c for c in fund_features if c in df_f.columns]\n",
|
|||
|
|
" prof = plot_heatmap(\n",
|
|||
|
|
" df_f, profile_vars_fund, cluster_col,\n",
|
|||
|
|
" title=f\"Cluster Signatures — {fund[:40]} (K={best_k}, robust z-score)\",\n",
|
|||
|
|
" figsize=(14, 4)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(f\"\\nMedians — {fund}:\")\n",
|
|||
|
|
" print(prof.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
" FUND_RESULTS[fund] = {\n",
|
|||
|
|
" \"df\": df_f,\n",
|
|||
|
|
" \"cluster_col\": cluster_col,\n",
|
|||
|
|
" \"k\": best_k,\n",
|
|||
|
|
" \"silhouette\": best_sil,\n",
|
|||
|
|
" \"profile\": prof,\n",
|
|||
|
|
" \"n\": len(df_f),\n",
|
|||
|
|
" }\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Step 6. Summary\n",
|
|||
|
|
"print(\"\\n\" + \"=\"*60)\n",
|
|||
|
|
"print(\"SUMMARY — Fund-level clustering\")\n",
|
|||
|
|
"print(\"=\"*60)\n",
|
|||
|
|
"for fund, res in FUND_RESULTS.items():\n",
|
|||
|
|
" print(f\" {fund[:40]:40s} : K={res['k']}, \"\n",
|
|||
|
|
" f\"sil={res['silhouette']:.4f}, n={res['n']}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 7. Croisement avec le clustering global ───────────────────────────────\n",
|
|||
|
|
"print(\"\\n\" + \"=\"*60)\n",
|
|||
|
|
"print(\"ARI — coherence: global clustering × fund clustering\")\n",
|
|||
|
|
"print(\"=\"*60)\n",
|
|||
|
|
"\n",
|
|||
|
|
"dfc_fund_cross = dfc[[ID_COL, \"cluster_k4\"]].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"for fund, res in FUND_RESULTS.items():\n",
|
|||
|
|
" cluster_col = res[\"cluster_col\"]\n",
|
|||
|
|
" df_f = res[\"df\"][[ID_COL, cluster_col]].copy()\n",
|
|||
|
|
" dfc_fund_cross = dfc_fund_cross.merge(df_f, on=ID_COL, how=\"left\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" mask = dfc_fund_cross[cluster_col].notna()\n",
|
|||
|
|
" if mask.sum() < 10:\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" labels_global = dfc_fund_cross.loc[mask, \"cluster_k4\"].values\n",
|
|||
|
|
" labels_fund = dfc_fund_cross.loc[mask, cluster_col].values\n",
|
|||
|
|
" ari = adjusted_rand_score(labels_global, labels_fund)\n",
|
|||
|
|
" print(f\" {fund[:40]:40s} : ARI={ari:.4f} (n={mask.sum()})\")"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "16115b05",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 6. Part 2 — Top 400 Accounts Clustering\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Objective\n",
|
|||
|
|
"Focus on the accounts representing the highest AUM (> €5M as of October 2025), which together account for over 97% of total assets. On this restricted universe, the longer and denser time series allow for additional features — in particular, **lagged correlations between flows and fund performance** — that are too sparse to use on the full dataset.\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Additional features (vs global clustering)\n",
|
|||
|
|
"| Feature | Description |\n",
|
|||
|
|
"|---|---|\n",
|
|||
|
|
"| `corr_flow_fund_lag3` | Correlation between flow-to-AUM and fund return lagged 3 months |\n",
|
|||
|
|
"| `corr_flow_fund_lag6` | Same at 6-month lag |\n",
|
|||
|
|
"| `corr_flow_rate_lag3` | Correlation between flow-to-AUM and interest rate change lagged 3 months |\n",
|
|||
|
|
"| `activity_intensity` | Number of transactions per month |\n",
|
|||
|
|
"| `flow_to_aum_vol` | Volatility of the flow-to-AUM ratio |\n",
|
|||
|
|
"\n",
|
|||
|
|
"### Preprocessing\n",
|
|||
|
|
"Identical to Part 1: MAD winsorization, clip p90 + log-transform for `gross_flow_to_aum` and `flow_freq`, RobustScaler.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 28,
|
|||
|
|
"id": "083087d6",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"PART 2 — CLUSTERING 400 GRANDS COMPTES ()\n",
|
|||
|
|
"Selected accounts (AUM > €5M): 431\n",
|
|||
|
|
"After filters: 427 accounts\n",
|
|||
|
|
"Accounts: 427 | Features: 16\n",
|
|||
|
|
"Points > 5 std after scaling: 38 (8.9%)\n",
|
|||
|
|
" k inertia silhouette davies_bouldin\n",
|
|||
|
|
" 2 4295.803536 0.648934 0.554878\n",
|
|||
|
|
" 3 3612.735780 0.271561 1.316068\n",
|
|||
|
|
" 4 3157.857186 0.201780 1.498467\n",
|
|||
|
|
" 5 2952.884036 0.197975 1.433778\n",
|
|||
|
|
" 6 2760.262346 0.152972 1.688634\n",
|
|||
|
|
" 7 2593.416835 0.180291 1.599408\n",
|
|||
|
|
" 8 2486.253809 0.171637 1.658369\n",
|
|||
|
|
" 9 2404.806299 0.146315 1.714646\n",
|
|||
|
|
"10 2333.195111 0.150386 1.685274\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1500x400 with 3 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"K=2 | sil=0.6489 | db=0.5549\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_k2 \n",
|
|||
|
|
"0 389 91.1\n",
|
|||
|
|
"1 38 8.9\n",
|
|||
|
|
"\n",
|
|||
|
|
"K=4 | sil=0.2018 | db=1.4985\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_k4 \n",
|
|||
|
|
"0 71 16.6\n",
|
|||
|
|
"1 36 8.4\n",
|
|||
|
|
"2 217 50.8\n",
|
|||
|
|
"3 103 24.1\n",
|
|||
|
|
"\n",
|
|||
|
|
"K=5 | sil=0.1985 | db=1.4319\n",
|
|||
|
|
" n_comptes pct\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"0 67 15.7\n",
|
|||
|
|
"1 29 6.8\n",
|
|||
|
|
"2 90 21.1\n",
|
|||
|
|
"3 229 53.6\n",
|
|||
|
|
"4 12 2.8\n",
|
|||
|
|
"\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABYsAAAGGCAYAAAA6p97XAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XVYFNv/B/D3kgahiGK3YoFioCJcr4jdARYYoCIqdge2fFX0qmCgGIDdV6/Y1xa7FTsQE0EFBKTO7w9/7GVlEWTRXdn363n2edyZM8NnPp4Z2M+eOSMRQggQERERERERERERkVrTUHYARERERERERERERKR8LBYTEREREREREREREYvFRERERERERERERMRiMRERERERERERERGBxWIiIiIiIiIiIiIiAovFRERERERERERERAQWi4mIiIiIiIiIiIgILBYTEREREREREREREVgsJiIiIiIiIiIiIiKwWExERLmMra0tJkyYoOwwfqldu3bB1NQUYWFhyg6FiCidoKAgWFpa4vPnzzm2z9Tr3q1bt3Jsn6SYzZs3488//0RCQoKyQyEiIiIFsFhMRES/hdDQUHh4eKBp06YwMzND7dq10b17d/j7+yM+Pv6XxBAXFwdvb29cuHDhl/y839W+ffuwfv16ZYeh0kJDQ2FmZpZhsSsqKgpTp05FgwYNUKtWLTg5OeHOnTty93Xs2DF06tQJZmZm+PPPP7F06VIkJSVlGsOjR4/g7e3NLxl+katXr8Lb2xtRUVE5tk9bW1u4urqmW75nzx5UrVoVLi4u+PLlS7b3//r1a/j4+KBr166oV68e6tevDycnJ5w7dy7L+0hOToa3tzccHR2RP3/+bMeiKk6ePAlvb29lh6GSOnfujMTERGzZskXZoRAREZECWCwmIiKVd+LECbRr1w4HDhxAkyZNMHXqVIwePRrFixfHggULMGfOnF8SR1xcHHx8fHDx4sVf8vOyqkOHDrh58yZKlCih7FAAAP/88w8CAgKUHYZKmzt3LrS0tOSuS0lJwcCBA/HPP//A0dERY8eORWRkJJycnPDs2TOZtidPnsSQIUOgr6+PqVOnws7ODitWrMCsWbMyjeHRo0fw8fHBy5cvc+KQKBPXrl2Dj49PjhaL5dm7dy8mTpwIKysrLF++HLq6utne17Fjx7B69WqUKVMGI0aMwODBg/H582f069cPO3fuzNI+jh8/jqdPn6Jbt27ZjkOVnDx5Ej4+PsoOQyXp6uqiY8eOWL9+PYQQyg6HiIiIskn+pxQiIiIV8eLFC4wcORLFixeHv78/ihQpIl3Xq1cvPH/+HCdOnFBegDkgNjYW+fLly/b2mpqa0NTUzMGIVFNcXBzy5s2r7DAUdvr0aZw5cwb9+/fHihUr0q0/ePAgrl27hiVLlqBly5YAgFatWqFFixbw9vbGwoULpW3nz58PU1NTrF27Vlp8zp8/P3x9fdG7d29UqFDh1xwUqYT9+/djwoQJaNCggcKFYgCoX78+jh8/DiMjI+myHj16oEOHDli6dCm6dOmS6T527tyJ2rVrw8TE5LvtUlJSkJiYqHDMlHO+fPkCbW1taGhkfXxRq1at4Ofnh/Pnz6Nhw4Y/MToiIiL6WTiymIiIVJqfnx9iY2MxZ84cmUJxqjJlyqBPnz4Zbu/t7Q1TU9N0y+XN83vr1i24uLigfv36MDc3h62tLSZOnAgACAsLk37w9fHxgampKUxNTWVuR378+DGGDRsGS0tLmJmZoXPnzjh27Jjcn3vx4kVMnz4dDRs2ROPGjb+bg8DAQLRp0wY1a9ZEvXr10LlzZ+zbt++7x5KSkgJvb29YW1ujZs2acHJywqNHj9LN6Zy67ZUrV+Dp6Smd9mDIkCGIjIyUiePo0aMYOHAgrK2tUaNGDdjZ2WHZsmVITk6WtnFycsKJEyfw8uVLaY5sbW0zjBMALly4AFNTU5npPZycnNC2bVvcvn0bvXr1Qs2aNbFo0SIAQEJCApYuXYpmzZqhRo0aaNy4MebPn59unsyzZ8+iR48eqFu3LiwsLNCiRQvpPpQlMTERc+bMQe/evVG6dGm5bQ4dOgRjY2M0b95cuszIyAitWrXCsWPHpMf56NEjPHr0CA4ODjKjlHv27AkhBA4dOpRhHLt27cLw4cMBAL1795b+X6X9P9i4cSPatGmDGjVqwNraGjNmzEg3Kjbt/1P37t2l583mzZuzlI+dO3eid+/eaNiwIWrUqIHWrVtj06ZNctuePHkSjo6OsLCwQO3atdGlSxeZ8wAAbty4gQEDBqBevXqoVasW2rVrB39/f5k2wcHB6NmzJ2rVqoW6devCzc0Njx8/lmkzYcIEab9NS971xNTUFDNnzsTRo0fRtm1b1KhRA23atMGpU6dktps/fz4AoGnTptJ8p54LOdFXg4KCMHbsWFhaWmLFihU5UnStVKmSTKEYAHR0dNC4cWO8efMGMTEx393+y5cvOH36NKysrNKtS83b3r170aZNG5iZmeH06dMAgLt376J///6oXbs2LCws0KdPH1y/fl3uz4iPj4eHhwfq16+P2rVrY9y4cfj06VO6nyVv6ohvr4eJiYnw8fFB8+bNYWZmhvr166NHjx44e/YsgK/9YuPGjdJ9pr4yktpf5L2yMrd+Ztd+AHj79i0mTZokvS7b2tpi2rRpMtfDFy9eSH831axZEw4ODum+ZE29Du/fvx9//fUXbGxsULNmTen/8Y0bN+Di4oI6deqgZs2acHR0xJUrV9LFXKNGDRQoUCDd7z4iIiL6fXBkMRERqbTjx4+jVKlSqF279k/9OREREXBxcUHBggUxcOBAGBgYICwsDEeOHAHwtVg3ffp0TJ8+Hc2aNUOzZs0AQFooePjwIXr06AETExMMGDAA+fLlw4EDBzBkyBB4e3tL26eaMWMGjIyMMGTIEMTGxmYY17Zt2zB79my0aNECvXv3xpcvX3D//n3cuHED7dq1y3C7hQsXws/PD02aNIGNjQ3u3bv33flLZ8+eDQMDAwwdOhQvX76Ev78/Zs6cicWLF0vb7N69G/ny5UO/fv2QL18+nD9/HkuXLkVMTAzGjx8PABg0aBCio6Px5s0baaE9u/OUfvz4EQMGDECbNm3Qvn17FCpUCCkpKXBzc8OVK1fg4OCAChUq4MGDB/D398ezZ8+wfPlyAF//P1xdXWFqaophw4ZBR0cHz58/x9WrV7MVS07x9/dHVFQUBg8ejMOHD8ttExISgmrVqqUbzWdmZoatW7fi6dOnMDU1xd27d6XL0zIxMUHRokUREhKSYRz16tWDk5MTAgMDMWjQIJQvXx4ApCORvb294ePjAysrK/To0QNPnz7F5s2bcevWLWzevBna2trSfX369AkDBw5Eq1at0KZNGxw4cADTp0+HtrY2unbt+t18bN68GZUqVYKtrS20tLRw/PhxzJgxA0II9OrVS9pu165dmDRpEipVqgRXV1fo6+sjJCQEp0+flp4HZ8+ehaurK4oUKYLevXvD2NgYjx8/xokTJ6RfKJ07dw4DBgxAyZIlMXToUMTHx2PDhg3o0aMHdu3ahZIlS3433oxcuXIFhw8fRs+ePZE/f34EBgZi2LBhOH78OAoWLIhmzZrh2bNn+OeffzBx4kQULFgQwNfrSk701UOHDmHs2LGoW7cuVq5ciTx58qRr8+nTJ5kvdjKSN2/eTEfwh4eHZ6nd7du3kZiYiGrVqsldf/78eRw4cAC9evVCwYIFUaJECTx8+BC9evVC/vz50b9/f2hpaWHr1q1wcnLChg0bULNmTZl9zJw5U3rtSu2nr169QmBgICQSSabHm5aPjw98fX1hb28Pc3NzxMTE4Pbt27hz5w4aNWqEbt264d27dzh79qy0+P89zZo1S/el0J07d+Dv75+uCP+trFz73759i65duyI6OhoODg4oX7483r59i0OHDiE+Ph46Ojp4//49unfvjri4ODg5OaFgwYLYvXs33NzcpF+6pbV8+XJoa2vDxcUFCQkJ0NbWRnBwMAYMGIAaNWpg6NChkEgk2LVrF/r06YNNmzbB3NxcZh/VqlVT+rWWiIiIso/FYiI
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1600x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Medians K=2 (400_accounts) ===\n",
|
|||
|
|
" log_aum_qty_mean gross_flow_to_aum flow_freq n_tx_total avg_n_isin_held n_isin_total avg_holding_months_per_isin exit_rate_per_isin flow_direction_balance aum_drawdown_last aum_final_to_peak months_since_last_tx corr_flow_fund_lag3 corr_flow_fund_lag6 corr_flow_rate_lag3\n",
|
|||
|
|
"cluster_k2 \n",
|
|||
|
|
"0 11.204 4.335 0.763 1436.0 11.471 28.0 46.273 0.606 0.114 1.000 0.000 0.0 0.047 0.046 -0.040\n",
|
|||
|
|
"1 11.126 1.506 0.043 4.0 1.380 2.5 40.357 0.292 0.580 0.303 0.697 17.5 0.006 -0.004 -0.008\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1600x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Medians K=5 (400_accounts) ===\n",
|
|||
|
|
" log_aum_qty_mean gross_flow_to_aum flow_freq n_tx_total avg_n_isin_held n_isin_total avg_holding_months_per_isin exit_rate_per_isin flow_direction_balance aum_drawdown_last aum_final_to_peak months_since_last_tx corr_flow_fund_lag3 corr_flow_fund_lag6 corr_flow_rate_lag3\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"0 10.551 3.243 0.351 88.0 3.317 13.0 33.000 0.419 0.263 0.169 0.831 1.0 0.028 0.016 -0.039\n",
|
|||
|
|
"1 11.579 1.013 0.043 4.0 1.460 2.0 42.429 0.333 0.783 0.180 0.820 30.0 0.013 0.020 -0.012\n",
|
|||
|
|
"2 12.619 4.668 0.992 7761.5 32.634 63.0 62.937 0.621 0.017 1.000 0.000 0.0 0.160 0.130 -0.144\n",
|
|||
|
|
"3 10.975 4.083 0.763 1386.0 9.485 24.0 45.412 0.643 0.116 1.000 0.000 0.0 0.020 0.025 -0.014\n",
|
|||
|
|
"4 10.750 4.102 0.079 8.0 1.735 7.5 33.394 0.360 0.250 0.378 0.622 7.0 -0.000 -0.002 0.008\n",
|
|||
|
|
"seg_2D\n",
|
|||
|
|
"Highly active 137\n",
|
|||
|
|
"Dormant 136\n",
|
|||
|
|
"Small rebalancers 77\n",
|
|||
|
|
"Occasional large movers 77\n",
|
|||
|
|
"Name: count, dtype: int64\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x500 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Overall churn rates ===\n",
|
|||
|
|
"churn_hard 0.684\n",
|
|||
|
|
"churn_soft 0.775\n",
|
|||
|
|
"churn_warning 0.321\n",
|
|||
|
|
"dtype: float64\n",
|
|||
|
|
"\n",
|
|||
|
|
"=== Churn per cluster K=2 (400_accounts) ===\n",
|
|||
|
|
" n_clients churn_hard churn_soft churn_warning\n",
|
|||
|
|
"cluster_k2 \n",
|
|||
|
|
"0 389 0.740 0.823 0.342\n",
|
|||
|
|
"1 38 0.105 0.289 0.105\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAGGCAYAAADmRxfNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUlVJREFUeJzt3Xt8z/X///H79rbZ5rDNhiRneWOGIbJE4UsRcpgcppwKOacPVpIRU6gckvPMWs6H+BB9VKSaDqKtfVCZ05DDRtI2m/f7/fvDb+9Pb9vY9t72xm7Xy8Wl3q/D8/V4vb3eb6/7+/V8vl5OFovFIgAAAACwg7OjCwAAAABw7yNYAAAAALAbwQIAAACA3QgWAAAAAOxGsAAAAABgN4IFAAAAALsRLAAAAADYjWABAAAAwG4ECwAAAAB2I1gAyBGj0aipU6c6uowcmThxogICAhxdhiSpX79+6tevn6PLQD6aMmWKBgwY4OgyUABmz56toKAgR5cB3LMIFkARd+rUKU2ePFlt2rSRv7+/GjVqpF69eikiIkKpqamOLg93sGjRIu3evdvRZdgtISFBRqNRy5cvt5lusVg0efJkGY1GzZ8/365txMTEaOrUqerYsaMaNmyoJ554QqNHj9bx48dz3Mbp06e1YcMGDRkyJNtlfvzxRxmNRhmNRiUlJWWaf/78eY0ePVpNmjRRo0aNNGzYMJ0+fTrLttavX6+nn35a/v7+ateunSIjI3Nca1EVFRWlTZs25WndF154QUeOHNHnn3+ez1UBRUMxRxcAwHH27Nmj0aNHy9XVVV26dFGtWrWUnp6uAwcOaNasWfr99981bdo0R5eJ21i8eLHat2+vtm3bOrqUfGexWDRlyhStXbtWL7/8skaOHGlXe8uWLdNPP/2kp556SkajURcvXlRUVJS6deumtWvXqlatWndsY9WqVapYsaIeffTRLOebzWa99dZb8vDwUHJycqb5f//9t55//nn99ddfGjJkiFxcXLRy5UoFBwdry5Yt8vb2ti67Zs0avfnmm2rfvr0GDBigH3/8UW+99ZZSUlL00ksv5f2NuM+tXr1a3t7e6tatW67XLVu2rNq0aaMVK1aoTZs2BVAdcH8jWABF1OnTpzV27Fg9+OCDioiIULly5azz+vbtq5MnT2rPnj2FWpPZbFZ6erqKFy9eqNuFrevXr8vFxUXOzo69qD1t2jStWbNGQ4cO1ejRo+1ur3///po9e7ZcXV2t0zp06KBOnTppyZIlmj179m3XT09P17Zt29SrV69sl1m7dq3OnTunHj16aNWqVZnmf/zxxzpx4oTWr1+v+vXrS5Ief/xxderUSeHh4XrllVckSampqXrvvff0xBNPaN68eZKknj17ymw268MPP9Rzzz0nT0/PXL8HuLOnn35ao0eP1unTp1WpUiVHlwPcU+gKBRRRy5YtU3JysqZPn24TKjJUqVJFL7zwQqbpu3fv1jPPPKN69eqpY8eO+uqrr2zmT5w4Ua1bt8603vz582U0Gm2mZYzb2Lp1qzp27Ch/f3/t27dPmzZtktFo1IEDBxQWFqZHH31UDRs21PDhw7PsWpKd06dPa9CgQWrYsKFatGihBQsWyGKxSLr5a3jr1q01bNiwTOtdv35djRs31uTJk++4jU8++UQ9evRQgwYN9Mgjj6hv3776+uuvs10+Y98SEhJspn/33XcyGo367rvvrNNOnDihkSNH6rHHHpO/v79atmypsWPH6q+//pJ08/1LTk7W5s2brV1vJk6caF3//PnzCgkJUWBgoPXva8OGDVlud/v27Xrvvff0+OOPq0GDBrp27dod970gvfXWW4qKitKQIUM0duzYfGmzUaNGNqFCkqpWraqHH35Y8fHxd1z/wIEDunz5sgIDA7Ocf+XKFb3//vsaNWqUSpcuneUyu3btkr+/vzVUSFKNGjXUvHlzffrpp9Zp3333na5cuaI+ffrYrN+3b18lJyfnKvRfuXJFb7/9tjp16qSAgAA1atRIgwcP1pEjRzIte/36dc2fP1/t27eXv7+/WrRooREjRujUqVPWZcxmsyIiItSpUyf5+/vr0Ucf1aBBgxQbG2td5saNG/rggw/Utm1b1atXT61bt9a7776rtLQ0m+1l18WtdevWNsdyTr8TWrdurd9++03ff/+99TORMcYpPT1dCxYsULt27eTv769mzZqpd+/e+uabb2y2nfH3S3coIPe4YgEUUV9++aUqVaqkRo0a5XidAwcO6LPPPlOfPn1UokQJRUZGatSoUfryyy9tunDkxv79+/Xpp5+qb9++8vb2VsWKFXX16lVJN08uS5curREjRujMmTOKiIjQ1KlT9f7779+xXZPJpMGDB6tBgwb617/+pX379mn+/PkymUwaPXq0nJyc1KlTJy1fvlxXrlyRl5eXdd0vvvhC165dU+fOnW+7jQULFmj+/PkKCAjQqFGj5OLiop9//ln79+9XixYt8vR+ZEhLS9OgQYOUlpam4OBg+fr66vz589qzZ4+uXr2qUqVK6Z133tGkSZNUv3599ezZU5JUuXJlSdKlS5fUs2dPOTk5qW/fvipTpoy++uorvf7667p27Zr69+9vs72FCxfKxcXFuk0XFxe76rfHjBkzFBkZqRdffNH6C/4/mc1mXblyJUdtlSpV6rb7YrFYdOnSJT388MN3bOvgwYNycnJS3bp1s5w/d+5clS1bVr169dLChQuzrPvo0aPq3r17pnn+/v76+uuvde3aNZUsWVL//e9/JUn16tWzWc7Pz0/Ozs46fPiwunTpcseapZsBe/fu3Xrqqaf00EMP6dKlS1q7dq2Cg4O1fft2lS9fXtLNz8yQIUMUHR2tjh076vnnn9fff/+tb775Rr/++qv12Hr99de1adMmtWzZUj169JDJZNKPP/6on3/+Wf7+/pKkSZMmafPmzdZuXDExMVq8eLGOHTumDz74IEd1Z+VO3wmvvfaapk2bJg8PDw0dOlSS5OvrK+nm53Xx4sUKCgpS/fr1de3aNf3yyy+Ki4vTY489Zt1GqVKlVLlyZf3000+ZPicAbo9gARRB165d0/nz53Pdh/jYsWPasWOH9QSjWbNm6tKli7Zv367g4OA81XL8+HFt27ZNNWvWtE47fPiwJMnLy0srVqyQk5OTpJsnZpGRkfrrr79UqlSp27Z7/fp1Pf7445o0aZIkqU+fPho6dKiWLl2qfv36qUyZMnr22We1aNEiffrpp+rdu7d13a1bt6pixYpq3Lhxtu2fPHlSH3zwgf7v//5P8+bNs+k2lHFVxB7Hjh1TQkKC5s6dq6eeeso6fcSIEdb/79Kli6ZMmaJKlSplOsl87733ZDKZtG3bNmvo6927t1555RUtWLBAvXr1kpubm3X569eva+PGjTbTHCEqKkpnzpzRoEGD9Oqrr2a5zNmzZ3N87K5atUrNmjXLdv7WrVt1/vx5jRo16o5txcfHy9PTUyVLlsw078iRI1q7dq2WLFkig8GQ5fpXrlxRWlqaypYtm2lexrQLFy6oZMmSunjxogwGg3x8fGyWc3V1lZeXly5cuHDHejMYjUbt2rXL5hjt0qWLnn76aW3YsEHDhw+XJG3ZskXR0dEKCQmxOaF+6aWXrMf0/v37tWnTJvXr18/62ZKkgQMHWpc5cuSINm/erKCgIL311luSZA23K1as0P79+7Mdo3Ind/pOaNu2rd5//315e3tn+kzs2bNHrVq1ytG4sUqVKun333/PU41AUUawAIqgjG4uJUqUyNV6gYGB1lAhSbVr11bJkiWzvaNNTjzyyCM2oeKfMn5xz9CkSROtXLlSZ86cUe3ate/Ydt++fa3/n/HL/Z49e6y/yFarVk0NGjTQtm3brMHiypUr2rdvnwYNGmSz7Vvt3r1bZrNZw4cPzzQW4Xbr5VTGyevXX3+tVq1ayd3dPcfrWiwWffbZZ3r66adlsVhsuoq0aNFC27dvV1xcnE1wevbZZx0eKqSbV1okqVq1atkuU7ZsWYWHh+eovdsdJ8eOHdPUqVMVEBCgrl273rGtK1euZDuuYfr06WrZsuVtr1Rdv35dkjJ1x5J
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x400 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Churn per cluster K=5 (400_accounts) ===\n",
|
|||
|
|
" n_clients churn_hard churn_soft churn_warning\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"0 67 0.015 0.075 0.134\n",
|
|||
|
|
"1 29 0.138 0.276 0.138\n",
|
|||
|
|
"2 90 0.944 0.978 0.478\n",
|
|||
|
|
"3 229 0.882 0.987 0.349\n",
|
|||
|
|
"4 12 0.000 0.333 0.083\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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Xbpz547y5cunKVOm6P3331eVKlXUqVMnSf8/pOf69evq1KmTTCaTunXrJm9vb+3evVujRo1SZGSkXnvtNbvjffbZZ3J1dbUd09XV1VD9RkyaNEnLli1Tv3799M477yRaHx8fr1u3bqWqrXz58iV6LyNHjlRUVJTMZrNq1qyp4cOHJxoalZTDhw/Ly8tLxYsXT7buunXrqnHjxnYhIcGVK1d048YNBQQEJFpXpUoVu5B+/PhxeXp6qly5com2S1hfq1atR9YsSWFhYTp8+LBat26tokWL6uLFi1qxYoV69uypzZs3y8PDQ9KDYVzdunVTeHi4OnTooEqVKunmzZv6/vvvdeXKFXl7e8tisah///4KDQ1V69at1bNnT929e1d79+7VyZMnVbJkSVmtVg0cOFAHDhzQyy+/rIoVK2rPnj2aMmWKrly5on//+9+pqjspb7/9tp588km98847+t///qc1a9bI29tb7733niSl+DsxZswYffvtt+revbvKlSunW7du6dChQwoPD1flypVtxyhfvrzc3d31yy+/6Nlnn013rUBOQ7AAcqjIyEhduXJFzZo1S9N+4eHh2rJli+0Pdd26dfXiiy9q8+bN6t69e7pqOXPmjDZt2qTy5cvblh0/flyS5OXlpUWLFslkMkl68IFy2bJl+vvvv5UvX74U2713754aNmxou3tP165dNWDAAM2fP189evSQt7e3XnrpJc2ZM0dbt25Vly5dbPtu3LhRxYsXV82aNZNt/+zZs/r000/17LPPaubMmXbDhh7+Vja9wsPDdeHCBX3yySd2w3aGDBli+/eLL76osWPHqkSJEnrxxRft9p8xY4YsFos2bdpkC31dunTRO++8o1mzZqlz585yd3e3bX/v3j2tXbvWbpkzLF++XBcvXlSfPn307rvvJrnNpUuXUt13ly5dqrp160qSXF1d1bJlSzVq1EgFCxZUeHi4Fi5cqG7dumnlypWqVKlSim2dPn062VCxa9cu7d27V19//XWy+1+9elWSVKhQoUTrChUqpFu3bik2NlZubm66du2afHx8bH3/4e0ebis1mjRpkmjo1zPPPKNXXnlF3377rV566SVJD+aPnDx5UrNmzbL7QD1o0CBbn96wYYNCQ0M1cuRIu3D6+uuv27bZuXOn9u/fr7ffftt2RbBbt2568803tXTpUnXv3j3Vc1r+qWLFipo0aZLt9a1bt/TVV1/ZgkVKvxM//PCDOnXqZHdVL6lJ+Lly5VLRokV16tSpdNUI5FQECyCHShjmktbxw/Xr17f7QODv76+8efPq/Pnz6a6ldu3adqHiYQnfuCeoVauWlixZoosXL8rf3/+RbXfr1s3274Rv7nft2mX7trVMmTKqWrWqNm3aZAsWt27d0p49e9SnT59EH+oetmPHDsXHx2vw4MGJ5iKktF9q5c2bV5L0448/qnHjxrZvlVPDarVq+/btev7552W1Wu2GsTRo0ECbN2/WsWPH7ILTSy+95PRQIT240iJJZcqUSXabQoUKafHixalq7+F+UqNGDbsrdM2aNVPLli3Vtm1bTZ8+PdEdqf7p1q1bKlKkSKLlsbGxCg4OVufOnZPty9KD8CbJboJ2goR5RTExMXJzc7P9f0rbpdbDP9e4uDhFRkaqZMmSyp8/v/73v//ZgsX27dvl7++
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x400 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAuEAAAKyCAYAAAB7WgDLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvXm0ZkV1Nv5UnfGd79y3Z5qhmznMg1Ehjp9JVAQ1QVGjGHFAHD5+AlETjQZQo0ZAEQcQZ6PGGJWoUWM+NYjBGHFAQKDn7nv7Du99xzNW/f6oql3n0g00BgT07LV6rbfPPUPNtWvvZz+bSSklSimllFJKKaWUUkoppZTfmvCHuwCllFJKKaWUUkoppZTy+yalEl5KKaWUUkoppZRSSim/ZSmV8FJKKaWUUkoppZRSSvktS6mEl1JKKaWUUkoppZRSym9ZSiW8lFJKKaWUUkoppZRSfstSKuGllFJKKaWUUkoppZTyW5ZSCS+llFJKKaWUUkoppZTfspRKeCmllFJKKaWUUkoppfyWpVTCSymllFJKKaWUUkop5bcspRJeyu+lXHnlldi0adPDXYxS7kMuvvhiPOEJT3i4i/E7I295y1vw4he/+OEuRikPgfz93/89nvOc5zzcxSillFIeoJRKeCmPevmnf/onbNq0if4dddRReOxjH4tzzz0XH//4x9Hr9R6U78zMzODKK6/Erbfe+qC87/dFhsMhrrzyStx0000Pd1EeEtm0aRP+9m//dq/rH/zgB7Fp0yZccsklEEL8xu/fvn37svFd/Pe1r31tv96xbds2fOELX8B55513r/fcfPPN9N6FhYW9/j4zM4PXvOY1OOGEE3DcccfhFa94BbZt27bPd33+85/H0572NBx11FF4ylOegk984hP7V9nfY/nUpz6Ff/qnf/qNnn3Ri16EX/3qV/j2t7/9IJeqlFJKeSjFfbgLUEopD5ZccMEFWLNmDbIsw9zcHH70ox/h0ksvxcc+9jF84AMfwKGHHkr3vuIVr8DLXvayB/T+2dlZXHXVVVi9ejUOO+ywB7v4v7MyHA5x1VVX4fzzz8fJJ5+838+97W1vg5TyISzZQycf+tCH8N73vhfPetaz8Hd/93fg/H9v7/jTP/1TPP7xj1927ZhjjtmvZz/+8Y9j9erVOOWUU/b5dyEE3v72t6NarWIwGOz1936/jxe+8IXodrs477zz4HkePvaxj+Gcc87BP//zP2N0dJTu/exnP4u/+Zu/wVOf+lS8+MUvxs0334y3v/3tGA6HD3jO/T7JZz7zGYyOjuLMM898wM9OTk7iiU98Iq699lo88YlPfAhKV0oppTwUUirhpfzOyOMf/3gcddRR9P/zzjsPN954I17+8pfjla98JW644QaEYQgAcF0XrlsO/0eiDAYDVKtVeJ73cBflN5KPfOQjePe7340zzjgDl1566YOigAPA4Ycfjmc+85kP+Lk0TfGVr3wFf/7nf36v93zuc5/Drl278OxnPxsf//jH9/r7pz/9aWzevBmf//zncfTRRwMAHve4x+HpT386rrvuOrz+9a8HAERRhPe+9704/fTTccUVVwAAnvvc50IIgauvvhp/9md/hlar9YDrUMr9y9Oe9jS85jWvwbZt27B27dqHuzillFLKfkgJRynld1pOPfVUvPKVr8SOHTvwL//yL3R9X5jwH/zgBzj77LNxwgkn4Nhjj8VTn/pUvOc97wEA3HTTTXj2s58NALjkkkvIbW/cxzfffDMuuOACnH766TjyyCNx2mmn4dJLL0UURcu+cfHFF+PYY4/FzMwMXvnKV+LYY4/FKaecgne84x3I83zZvUIIXH/99Xj605+Oo446CqeccgrOPfdc/OxnP1t235e//GWceeaZOProo3HSSSfhda97HXbt2nW/bWPa4O6778aFF16I448/Hqeccgr+4R/+AVJK7Nq1C694xStw3HHH4Q//8A9x7bXXLns+SRK8733vw5lnnonjjz8exxxzDJ73vOfhhz/8Id2zfft2nHrqqQCAq666itrtyiuvXNYeW7duxV/+5V/i2GOPxYUXXkh/K2LCr7jiChx66KG48cYbl5XjzW9+M4488kj86le/ut86P9Ry3XXX4V3vehee8Yxn4LLLLnvQFHAjg8EASZI8oGd+/OMfY3FxEY95zGP2+fd2u41/+Id/wAUXXIBms7nPe77xjW/gqKOOIgUcAA466CCceuqp+Nd//Ve6dtNNN6HdbuN5z3vesuef//znYzAY4Lvf/e5+l7vdbuMd73gHnv70p+PYY4/Fcccdh5e+9KX77Oc4jnHllVfiqU99KsHRzj//fGzdupXu2Z/5lGUZ3v/+9+NJT3oSjjzySDzhCU/Ae97znr3avDiGi/KEJzwBF198Mf3fQOV+/OMf47LLLsMpp5yCY445Bq961auWQX6e8IQn4I477sCPfvQjmiMveMELAKhD1FVXXYWnPOUpOOqoo3DyySfj7LPPxg9+8INl3zb9W0JSSinl0SOlEl7K77wY6+H3v//9e73njjvuwHnnnYckSXDBBRfgoosuwhOe8AT893//NwClcFxwwQUAgD/7sz/DO9/5Trzzne/EiSeeCAD4+te/jiiKcPbZZ+PNb34zHvvYx+KTn/wk3vCGN+z1rTzPce6552JkZARveMMbcNJJJ+Haa6/F5z73uWX3vfGNb8Sll16K6elpXHjhhXjZy16GIAjw05/+lO65+uqrcdFFF2H9+vW4+OKL8cIXvhA33ngjnv/856PT6exX+7zuda+DlBL/9//+X/zBH/wBrr76alx//fV48YtfjBUrVuDCCy/EunXr8I53vAP/9V//Rc/1ej18/vOfx0knnYQLL7wQ559/PhYWFvDSl76UcPNjY2N4y1veAgB48pOfTO325Cc/md6TZRnOPfdcjI+P46KLLsJTnvKUfZbzFa94BQ477DC88Y1vJJz/9773PfzjP/4jXvnKVy6DGz0ccv311+Pyyy/Hn/7pn+Lyyy/fpwK+sLCwX//2pWhfddVVOPbYY3H00UfjrLPOus/xXJSf/OQnYIzh8MMP3+ff3/e+92FycvJeLeVCCNx222048sgj9/rbUUcdha1bt1J//PKXvwSAve494ogjwDl/QPEU27Ztw7e+9S2cfvrpuPjii3Huuefi9ttvxznnnIOZmRm6L89znHfeebjqqqtwxBFH0Dzodru4/fbb6b79mU9vetObcMUVV+Dwww/HJZdcghNPPBHXXHMNXve61+13ufclb3/72/GrX/0K559/Ps4++2z8+7//+7I4gr/6q7/C9PQ0DjzwQJojL3/5ywGofr/qqqtw8skn46//+q/x8pe/HKtWrcIvfvGLZd9oNBpYt24drVmllFLKo0BkKaU8yuWLX/yi3Lhxo7zlllvu9Z7jjz9ennHGGfT/K664Qm7cuJH+f91118mNGzfK+fn5e33HLbfcIjdu3Ci/+MUv7vW34XC417VrrrlGbtq0Se7YsYOuXXTRRXLjxo3yqquuWnbvGWecIZ/1rGfR/2+88Ua5ceNG+ba3vW2v9wohpJRSbt++XR522GHy6quvXvb32267TR5++OF7Xb+nmDZ485vfTNeyLJOPf/zj5aZNm+Q111xD15eWluTRRx8tL7roomX3xnG87J1LS0vyMY95jLzkkkvo2vz8vNy4caO84oor9iqDaY+///u/3+ff/uiP/mivuh1xxBHyjW98o1xaWpKPe9zj5JlnninTNL3Puj6UsnHjRvlHf/RHcuPGjfL1r3+9zLLsPu/dn3/FMbZjxw75kpe8RH7605+W3/72t+XHPvYxefrpp8tDDz1U/vu///v9lu/CCy+UJ5100j7/duutt8rDDjtMfu9735NS2jFRnAem/+45ZqWU8pOf/KTcuHGjvPPOO6WUUr71rW+Vhx122D6/dcopp8jXve5191teI3EcyzzPl13btm2bPPLII5eV5Qtf+ILcuHGjvO666/Z6h5kr+zOfbr31Vrlx40b5xje+cdnfL7/8crlx40Z544030rV7G89/9Ed/tGyOmLXpL/7iL+g7Ukp56aWXysMOO0x
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x700 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# 6a. Account selection — AUM > €5M as of October 2025\n",
|
|||
|
|
"ref_date = pd.Timestamp(\"2025-10-01\") # premier jour du mois dans le panel\n",
|
|||
|
|
"df_ref = df_aum[df_aum[\"month\"] == ref_date]\n",
|
|||
|
|
"\n",
|
|||
|
|
"aum_account = (\n",
|
|||
|
|
" df_ref.groupby(ID_COL)[AUM_VAL_COL].sum()\n",
|
|||
|
|
" .reset_index().sort_values(AUM_VAL_COL, ascending=False)\n",
|
|||
|
|
")\n",
|
|||
|
|
"aum_account = aum_account[aum_account[AUM_VAL_COL] > 5_000_000]\n",
|
|||
|
|
"selected_accounts = aum_account[ID_COL]\n",
|
|||
|
|
"print(f\"Selected accounts (AUM > €5M): {len(selected_accounts)}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Filtrer df_month sur les top accounts\n",
|
|||
|
|
"df_month_400_accounts = df_month[df_month[ID_COL].isin(selected_accounts)].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 6b. Feature engineering\n",
|
|||
|
|
"dfc_400_accounts = df_client_base[df_client_base[ID_COL].isin(selected_accounts)].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"dfc_400_accounts[\"log_aum_qty_mean\"] = np.log1p(dfc_400_accounts[\"aum_qty_mean\"].clip(lower=0))\n",
|
|||
|
|
"dfc_400_accounts[\"log_gross_flow_qty_mean\"] = np.log1p(dfc_400_accounts[\"gross_flow_qty_mean\"].clip(lower=0))\n",
|
|||
|
|
"dfc_400_accounts[\"gross_flow_to_aum\"] = dfc_400_accounts[\"gross_flow_qty_sum\"] / (dfc_400_accounts[\"aum_qty_mean\"].abs() + EPS)\n",
|
|||
|
|
"dfc_400_accounts[\"flow_direction_balance\"] = np.where(\n",
|
|||
|
|
" dfc_400_accounts[\"gross_flow_qty_sum\"] > 0,\n",
|
|||
|
|
" dfc_400_accounts[\"net_flow_qty_sum\"] / dfc_400_accounts[\"gross_flow_qty_sum\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc_400_accounts[\"redemption_bias\"] = dfc_400_accounts[\"red_qty_sum\"] - dfc_400_accounts[\"sub_qty_sum\"]\n",
|
|||
|
|
"dfc_400_accounts[\"exit_rate_per_isin\"] = np.where(\n",
|
|||
|
|
" dfc_400_accounts[\"n_isin_total\"] > 0,\n",
|
|||
|
|
" dfc_400_accounts[\"full_exit_count\"] / dfc_400_accounts[\"n_isin_total\"], np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc_400_accounts[\"aum_drawdown_last\"] = dfc_400_accounts[\"aum_drawdown_last\"].clip(0, 1)\n",
|
|||
|
|
"dfc_400_accounts[\"aum_final_to_peak\"] = np.where(\n",
|
|||
|
|
" dfc_400_accounts[\"aum_qty_max\"] > 0,\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_qty_last\"] / dfc_400_accounts[\"aum_qty_max\"]).clip(0, 1), np.nan\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc_400_accounts[\"activity_intensity\"] = dfc_400_accounts[\"n_tx_total\"] / (dfc_400_accounts[\"n_months\"] + EPS)\n",
|
|||
|
|
"dfc_400_accounts[\"flow_to_aum_vol\"] = dfc_400_accounts[\"rel_flow_to_aum_vol_avg\"]\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Performance reactivity features (lag 3 & 6 months) — viable on large accounts due to denser time series\n",
|
|||
|
|
"def corr_lag(x, y, lag):\n",
|
|||
|
|
" x = np.asarray(x, dtype=float)\n",
|
|||
|
|
" y = np.asarray(y, dtype=float)\n",
|
|||
|
|
" mask = np.isfinite(x) & np.isfinite(y)\n",
|
|||
|
|
" x, y = x[mask], y[mask]\n",
|
|||
|
|
" if len(x) <= lag + 3:\n",
|
|||
|
|
" return np.nan\n",
|
|||
|
|
" return pd.Series(x[lag:]).corr(pd.Series(y[:-lag]))\n",
|
|||
|
|
"\n",
|
|||
|
|
"rows_corr = []\n",
|
|||
|
|
"for acc, g in df_month_400_accounts.groupby(ID_COL):\n",
|
|||
|
|
" g = g.sort_values(\"month\")\n",
|
|||
|
|
" flow = g[\"flow_to_aum_m\"].values\n",
|
|||
|
|
" ret = g[\"ret_fund_m\"].values\n",
|
|||
|
|
" rate = g[\"delta_rate_m\"].values\n",
|
|||
|
|
" rows_corr.append({\n",
|
|||
|
|
" ID_COL: acc,\n",
|
|||
|
|
" \"corr_flow_fund_lag3\": corr_lag(flow, ret, 3),\n",
|
|||
|
|
" \"corr_flow_fund_lag6\": corr_lag(flow, ret, 6),\n",
|
|||
|
|
" \"corr_flow_rate_lag3\": corr_lag(flow, rate, 3),\n",
|
|||
|
|
" })\n",
|
|||
|
|
"df_corr_400_accounts = pd.DataFrame(rows_corr)\n",
|
|||
|
|
"dfc_400_accounts = dfc_400_accounts.merge(df_corr_400_accounts, on=ID_COL, how=\"left\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Recency feature\n",
|
|||
|
|
"dfc_400_accounts = add_months_since_last_tx(dfc_400_accounts, df_month_400_accounts, ID_COL)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Quality filters\n",
|
|||
|
|
"dfc_400_accounts = dfc_400_accounts[(dfc_400_accounts[\"n_months\"] >= 6) & (dfc_400_accounts[\"aum_qty_mean\"] > 0)].copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Geographic grouping\n",
|
|||
|
|
"top_countries_l = dfc_400_accounts[\"country\"].fillna(\"Unknown\").value_counts().head(10).index\n",
|
|||
|
|
"top_regions_l = dfc_400_accounts[\"region\"].fillna(\"Unknown\").value_counts().head(10).index\n",
|
|||
|
|
"dfc_400_accounts[\"country_grp\"] = np.where(\n",
|
|||
|
|
" dfc_400_accounts[\"country\"].isin(top_countries_l), dfc_400_accounts[\"country\"], \"Other\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"dfc_400_accounts[\"region_grp\"] = np.where(\n",
|
|||
|
|
" dfc_400_accounts[\"region\"].isin(top_regions_l), dfc_400_accounts[\"region\"], \"Other\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(f\"After filters: {len(dfc_400_accounts)} accounts\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Feature selection — Top 400 accounts enrichi (15 variables)\n",
|
|||
|
|
"base_features_400_accounts = [\n",
|
|||
|
|
" \"log_aum_qty_mean\",\n",
|
|||
|
|
" \"flow_freq\",\n",
|
|||
|
|
" \"gross_flow_to_aum\",\n",
|
|||
|
|
" \"flow_to_aum_vol\",\n",
|
|||
|
|
" \"activity_intensity\",\n",
|
|||
|
|
" \"n_tx_total\",\n",
|
|||
|
|
" \"avg_n_isin_held\",\n",
|
|||
|
|
" \"n_isin_total\",\n",
|
|||
|
|
" \"avg_holding_months_per_isin\",\n",
|
|||
|
|
" \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"flow_direction_balance\",\n",
|
|||
|
|
" \"aum_drawdown_last\",\n",
|
|||
|
|
" \"months_since_last_tx\",\n",
|
|||
|
|
" \"corr_flow_fund_lag3\",\n",
|
|||
|
|
" \"corr_flow_fund_lag6\",\n",
|
|||
|
|
" \"corr_flow_rate_lag3\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"all_features_400_accounts = [c for c in base_features_400_accounts if c in dfc_400_accounts.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Preprocessing — RobustScaler + winsorisation MAD \n",
|
|||
|
|
"dfc_400_accounts_clean = dfc_400_accounts.copy()\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"flow_direction_balance\", \"months_since_last_tx\",\n",
|
|||
|
|
" \"corr_flow_fund_lag3\", \"corr_flow_fund_lag6\", \"corr_flow_rate_lag3\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = dfc_400_accounts_clean[col].fillna(0)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"n_isin_total\", \"avg_n_isin_held\", \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"avg_holding_months_per_isin\", \"months_since_last_tx\",\n",
|
|||
|
|
" \"activity_intensity\", \"flow_to_aum_vol\",\n",
|
|||
|
|
" \"aum_drawdown_last\", \"aum_final_to_peak\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = winsorize_mad(dfc_400_accounts_clean[col], n_sigma=3)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"gross_flow_to_aum\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" vals = dfc_400_accounts_clean[col].to_numpy(dtype=float)\n",
|
|||
|
|
" vals = np.clip(vals, 0, np.nanpercentile(vals, 90))\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = np.log1p(vals)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"flow_freq\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" vals = dfc_400_accounts_clean[col].to_numpy(dtype=float)\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = np.log1p(np.clip(vals, 0, None))\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"log_aum_qty_mean\", \"log_gross_flow_qty_mean\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = winsorize_mad(dfc_400_accounts_clean[col], n_sigma=3)\n",
|
|||
|
|
"\n",
|
|||
|
|
"for col in [\"n_tx_total\"]:\n",
|
|||
|
|
" if col in dfc_400_accounts_clean.columns:\n",
|
|||
|
|
" vals = dfc_400_accounts_clean[col].to_numpy(dtype=float)\n",
|
|||
|
|
" dfc_400_accounts_clean[col] = np.log1p(np.clip(vals, 0, None))\n",
|
|||
|
|
"\n",
|
|||
|
|
"X_400_accounts = dfc_400_accounts_clean[all_features_400_accounts].copy()\n",
|
|||
|
|
"X_400_accounts = X_400_accounts.loc[:, ~X_400_accounts.columns.duplicated()]\n",
|
|||
|
|
"X_400_accounts = X_400_accounts.replace([np.inf, -np.inf], np.nan).fillna(X_400_accounts.median())\n",
|
|||
|
|
"\n",
|
|||
|
|
"scaler_400_accounts = RobustScaler()\n",
|
|||
|
|
"X_400_accounts_scaled = scaler_400_accounts.fit_transform(X_400_accounts)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Diagnostic\n",
|
|||
|
|
"X_df_l = pd.DataFrame(X_400_accounts_scaled, columns=X_400_accounts.columns)\n",
|
|||
|
|
"extreme_l = (X_df_l.abs() > 5).any(axis=1).sum()\n",
|
|||
|
|
"print(f\"Accounts: {X_400_accounts.shape[0]} | Features: {X_400_accounts.shape[1]}\")\n",
|
|||
|
|
"print(f\"Points > 5 std after scaling: {extreme_l} ({extreme_l/len(X_df_l):.1%})\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 6e. K-selection\n",
|
|||
|
|
"rows_l = []\n",
|
|||
|
|
"for k in range(2, 11):\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=30, random_state=RANDOM_STATE)\n",
|
|||
|
|
" labels = km.fit_predict(X_400_accounts_scaled)\n",
|
|||
|
|
" rows_l.append({\n",
|
|||
|
|
" \"k\": k, \"inertia\": km.inertia_,\n",
|
|||
|
|
" \"silhouette\": silhouette_score(X_400_accounts_scaled, labels),\n",
|
|||
|
|
" \"davies_bouldin\": davies_bouldin_score(X_400_accounts_scaled, labels),\n",
|
|||
|
|
" })\n",
|
|||
|
|
"df_kdiag_400_accounts = pd.DataFrame(rows_l)\n",
|
|||
|
|
"print(df_kdiag_400_accounts.to_string(index=False))\n",
|
|||
|
|
"\n",
|
|||
|
|
"fig, axes = plt.subplots(1, 3, figsize=(15, 4))\n",
|
|||
|
|
"for ax, col, title in zip(axes,\n",
|
|||
|
|
" [\"inertia\", \"silhouette\", \"davies_bouldin\"],\n",
|
|||
|
|
" [\"Elbow / Inertia\", \"Silhouette (higher=better)\", \"Davies-Bouldin (lower=better)\"]):\n",
|
|||
|
|
" ax.plot(df_kdiag_400_accounts[\"k\"], df_kdiag_400_accounts[col], marker=\"o\")\n",
|
|||
|
|
" ax.set_title(title); ax.set_xlabel(\"K\")\n",
|
|||
|
|
"plt.suptitle(\"K-selection — 400 top accounts (400_accounts)\")\n",
|
|||
|
|
"plt.tight_layout(); plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Clustering K=2, 4, 5 (comme 400_accounts)\n",
|
|||
|
|
"RESULTS_400_accounts = {}\n",
|
|||
|
|
"for k in [2, 4, 5]:\n",
|
|||
|
|
" km = KMeans(n_clusters=k, n_init=50, random_state=RANDOM_STATE)\n",
|
|||
|
|
" dfc_400_accounts[f\"cluster_k{k}\"] = km.fit_predict(X_400_accounts_scaled)\n",
|
|||
|
|
" RESULTS_400_accounts[k] = {\n",
|
|||
|
|
" \"model\": km,\n",
|
|||
|
|
" \"silhouette\": silhouette_score(X_400_accounts_scaled, dfc_400_accounts[f\"cluster_k{k}\"]),\n",
|
|||
|
|
" \"davies_bouldin\": davies_bouldin_score(X_400_accounts_scaled, dfc_400_accounts[f\"cluster_k{k}\"]),\n",
|
|||
|
|
" }\n",
|
|||
|
|
" print(f\"K={k} | sil={RESULTS_400_accounts[k]['silhouette']:.4f} \"\n",
|
|||
|
|
" f\"| db={RESULTS_400_accounts[k]['davies_bouldin']:.4f}\")\n",
|
|||
|
|
" counts = dfc_400_accounts[f\"cluster_k{k}\"].value_counts().sort_index()\n",
|
|||
|
|
" props = counts / counts.sum() * 100\n",
|
|||
|
|
" print(pd.DataFrame({\"n_comptes\": counts, \"pct\": props.round(1)}))\n",
|
|||
|
|
" print()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Profile vars — top 400 accounts (avec les corrélations)\n",
|
|||
|
|
"profile_vars_400_accounts = [\n",
|
|||
|
|
" \"log_aum_qty_mean\",\n",
|
|||
|
|
" \"gross_flow_to_aum\",\n",
|
|||
|
|
" \"flow_freq\",\n",
|
|||
|
|
" \"n_tx_total\",\n",
|
|||
|
|
" \"avg_n_isin_held\",\n",
|
|||
|
|
" \"n_isin_total\",\n",
|
|||
|
|
" \"avg_holding_months_per_isin\",\n",
|
|||
|
|
" \"exit_rate_per_isin\",\n",
|
|||
|
|
" \"flow_direction_balance\",\n",
|
|||
|
|
" \"aum_drawdown_last\",\n",
|
|||
|
|
" \"aum_final_to_peak\",\n",
|
|||
|
|
" \"months_since_last_tx\",\n",
|
|||
|
|
" \"corr_flow_fund_lag3\",\n",
|
|||
|
|
" \"corr_flow_fund_lag6\",\n",
|
|||
|
|
" \"corr_flow_rate_lag3\",\n",
|
|||
|
|
"]\n",
|
|||
|
|
"profile_vars_400_accounts = [c for c in profile_vars_400_accounts if c in dfc_400_accounts.columns]\n",
|
|||
|
|
"\n",
|
|||
|
|
"for k in [2, 5]:\n",
|
|||
|
|
" prof_l = plot_heatmap(\n",
|
|||
|
|
" dfc_400_accounts, profile_vars_400_accounts, f\"cluster_k{k}\",\n",
|
|||
|
|
" title=f\"Cluster signatures — 400 top accounts K={k} (robust z-score)\",\n",
|
|||
|
|
" figsize=(16, 4)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(f\"\\n=== Medians K={k} (400_accounts) ===\")\n",
|
|||
|
|
" print(prof_l.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 2D Segmentation (comme 400_accounts)\n",
|
|||
|
|
"thr_int = dfc_400_accounts[\"gross_flow_to_aum\"].median()\n",
|
|||
|
|
"thr_freq = dfc_400_accounts[\"flow_freq\"].median()\n",
|
|||
|
|
"\n",
|
|||
|
|
"def quadrant(row):\n",
|
|||
|
|
" low_int = row[\"gross_flow_to_aum\"] < thr_int\n",
|
|||
|
|
" low_frq = row[\"flow_freq\"] < thr_freq\n",
|
|||
|
|
" if low_int and low_frq: return \"Dormant\"\n",
|
|||
|
|
" if low_int and not low_frq: return \"Small rebalancers\"\n",
|
|||
|
|
" if not low_int and low_frq: return \"Occasional large movers\"\n",
|
|||
|
|
" return \"Highly active\"\n",
|
|||
|
|
"\n",
|
|||
|
|
"dfc_400_accounts[\"seg_2D\"] = dfc_400_accounts.apply(quadrant, axis=1)\n",
|
|||
|
|
"print(dfc_400_accounts[\"seg_2D\"].value_counts())\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.figure(figsize=(8, 5))\n",
|
|||
|
|
"for name, g in dfc_400_accounts.groupby(\"seg_2D\"):\n",
|
|||
|
|
" plt.scatter(g[\"flow_freq\"], g[\"gross_flow_to_aum\"], s=15, label=name)\n",
|
|||
|
|
"plt.yscale(\"log\")\n",
|
|||
|
|
"plt.axvline(thr_freq, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.axhline(thr_int, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.xlabel(\"Activity frequency\")\n",
|
|||
|
|
"plt.ylabel(\"Gross flow / mean AUM [log scale]\")\n",
|
|||
|
|
"plt.title(\"2D behavioral segmentation — 400 top accounts\")\n",
|
|||
|
|
"plt.legend(markerscale=2)\n",
|
|||
|
|
"plt.tight_layout()\n",
|
|||
|
|
"plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Analyse churn (comme 400_accounts)\n",
|
|||
|
|
"dfc_400_accounts[\"churn_hard\"] = (dfc_400_accounts[\"aum_final_to_peak\"] < 0.10).astype(int)\n",
|
|||
|
|
"dfc_400_accounts[\"churn_soft\"] = (\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_final_to_peak\"] < 0.40) &\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_drawdown_last\"] > 0.40)\n",
|
|||
|
|
").astype(int)\n",
|
|||
|
|
"dfc_400_accounts[\"churn_warning\"] = (\n",
|
|||
|
|
" (dfc_400_accounts[\"flow_direction_balance\"] < 0) &\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_drawdown_last\"] > 0.20)\n",
|
|||
|
|
").astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\n=== Overall churn rates ===\")\n",
|
|||
|
|
"print(dfc_400_accounts[[\"churn_hard\",\"churn_soft\",\"churn_warning\"]].mean().round(3))\n",
|
|||
|
|
"\n",
|
|||
|
|
"for k in [2, 5]:\n",
|
|||
|
|
" churn_profile = (\n",
|
|||
|
|
" dfc_400_accounts.groupby(f\"cluster_k{k}\")\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_clients = (ID_COL, \"count\"),\n",
|
|||
|
|
" churn_hard = (\"churn_hard\", \"mean\"),\n",
|
|||
|
|
" churn_soft = (\"churn_soft\", \"mean\"),\n",
|
|||
|
|
" churn_warning = (\"churn_warning\", \"mean\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(f\"\\n=== Churn per cluster K={k} (400_accounts) ===\")\n",
|
|||
|
|
" print(churn_profile.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
" churn_profile[[\"churn_hard\",\"churn_soft\",\"churn_warning\"]].plot(\n",
|
|||
|
|
" kind=\"bar\", figsize=(8, 4)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" plt.title(f\"Churn by cluster — K={k} (400_accounts)\")\n",
|
|||
|
|
" plt.ylabel(\"Rate\"); plt.xlabel(\"Cluster\")\n",
|
|||
|
|
" plt.tight_layout(); plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Matrice de distance inter-cluster\n",
|
|||
|
|
"def plot_distance_matrix(X_scaled, labels, max_points=400, title=\"Distance matrix\"):\n",
|
|||
|
|
" n = X_scaled.shape[0]\n",
|
|||
|
|
" idx = np.arange(n)\n",
|
|||
|
|
" if n > max_points:\n",
|
|||
|
|
" rng = np.random.default_rng(42)\n",
|
|||
|
|
" idx = rng.choice(idx, size=max_points, replace=False)\n",
|
|||
|
|
" X_sub = X_scaled[idx]\n",
|
|||
|
|
" labels_sub = np.asarray(labels)[idx]\n",
|
|||
|
|
" order = np.lexsort((np.arange(len(labels_sub)), labels_sub))\n",
|
|||
|
|
" X_sub = X_sub[order]; labels_sub = labels_sub[order]\n",
|
|||
|
|
" D = pairwise_distances(X_sub)\n",
|
|||
|
|
" plt.figure(figsize=(8, 7))\n",
|
|||
|
|
" sns.heatmap(D, cmap=\"viridis\")\n",
|
|||
|
|
" unique_labels, counts = np.unique(labels_sub, return_counts=True)\n",
|
|||
|
|
" for b in np.cumsum(counts)[:-1]:\n",
|
|||
|
|
" plt.axhline(b, color=\"red\", linewidth=2)\n",
|
|||
|
|
" plt.axvline(b, color=\"red\", linewidth=2)\n",
|
|||
|
|
" plt.title(title); plt.tight_layout(); plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"plot_distance_matrix(\n",
|
|||
|
|
" X_400_accounts_scaled,\n",
|
|||
|
|
" dfc_400_accounts[\"cluster_k5\"].values,\n",
|
|||
|
|
" title=\"Distance matrix — K=5 (400_accounts)\"\n",
|
|||
|
|
")"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 29,
|
|||
|
|
"id": "b394752d",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Selected accounts (AUM > €5M): 431\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"ref_date = pd.Timestamp(\"2025-10-01\") # first day of month (panel convention)\n",
|
|||
|
|
"df_ref = df_aum[df_aum[\"month\"] == ref_date]\n",
|
|||
|
|
"\n",
|
|||
|
|
"aum_account = (\n",
|
|||
|
|
" df_ref.groupby(ID_COL)[AUM_VAL_COL].sum()\n",
|
|||
|
|
" .reset_index().sort_values(AUM_VAL_COL, ascending=False)\n",
|
|||
|
|
")\n",
|
|||
|
|
"aum_account = aum_account[aum_account[AUM_VAL_COL] > 5_000_000]\n",
|
|||
|
|
"selected_accounts = aum_account[ID_COL]\n",
|
|||
|
|
"print(f\"Selected accounts (AUM > €5M): {len(selected_accounts)}\")"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "078c2442",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"### 6h. Visualization — Top 400 Accounts\n",
|
|||
|
|
"\n",
|
|||
|
|
"The 2D intensity-frequency space provides an intuitive view of behavioral profiles. The churn analysis links clusters to concrete retention risk signals.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 30,
|
|||
|
|
"id": "715c7165",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"seg_2D\n",
|
|||
|
|
"Highly active (high int, high freq) 137\n",
|
|||
|
|
"Dormant (low int, low freq) 136\n",
|
|||
|
|
"Small rebalancers (low int, high freq) 77\n",
|
|||
|
|
"Occasional large movers (high int, low freq) 77\n",
|
|||
|
|
"Name: count, dtype: int64\n",
|
|||
|
|
"thr_int: 4.0037 | thr_freq: 0.7231\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 900x500 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA3kAAAHqCAYAAAC5nYcRAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA7/xJREFUeJzs3XdUE+nXB/BvgiAgotIsSBMlKkVAbIiiYkdEsRdsWLCubQVEV8GCdW1YsDd0bcCKIK66a++KHawgIBYEEWkCSd4/fDM/AgkkEEjA+znHI5lMuTN5Mpk7TxkWn8/ngxBCCCGEEEJItcCWdwCEEEIIIYQQQmSHkjxCCCGEEEIIqUYoySOEEEIIIYSQaoSSPEIIIYQQQgipRijJI4QQQgghhJBqhJI8QgghhBBCCKlGKMkjhBBCCCGEkGqEkjxCCCGEEEIIqUYoySOEEEIIIYSQaoSSPEIqWEhICDgcDpKSkuQdClEw3t7e6Natm7zDIKTC8Hg89OvXD9u3b5drHBXxXRO1Tg6Hgy1btsh0O6TqSkpKAofDQUhICDNty5Yt4HA4coyq/I4ePYouXbogLy9P3qGQElCSRxSOICl68uSJ0PTv379j8ODBsLS0xJUrV8q1DcFJtug/S0tLidfB5XJx6tQpuLu7o23btrCwsEC3bt3g4+NTLPaKdPny5V/qooLD4cDf379My4aHh2P//v2yDUiGcnJysGXLFty+fbtC1s/j8dC+fXvs2rVL7DzivhscDgdHjx6tkLh+Zffu3cPEiRPRqVMnWFpaokuXLvD09ER4eLi8Q5OJM2fO4MOHDxg9erS8QyFSeP36NbZs2UI3JyWk6L8tMTExmD9/PhwdHWFhYYG2bdti3LhxOHXqFLhcLgDg69ev2L17N0aNGoX27dvDzs4OQ4cORWRkZLH1ubm5IT8/H3/99Vdl7wqRQg15B0CIJDIzMzFhwgS8ePECgYGB6Ny5s0zWu3TpUqirqzOvlZSUJFouNzcXM2bMwNWrV9GmTRtMmTIFderUwfv373H27FmEhobi0qVLaNCggUziLMnly5cRHByMmTNnVvi2qrozZ87g1atXGDdunLxDAQAsW7YMfD6feZ2Tk4PAwEDMmDED7dq1k/n2Hj9+jK9fv6JLly6lzlv0uwEArVq1knlMv7KzZ89izpw5aNGiBcaMGYM6deogKSkJd+/exfHjx+Hi4iLvEMttz549cHZ2Ru3ateUdSqV4/PixxL8jiuz169cIDAxE27Zt0bhxY3mHo/Ck+W2ZOnUqJk+eXPFB/b8TJ05gyZIl0NbWhqurK4yMjJCVlYVbt27B19cXKSkp8PT0xMOHD7Fx40Z07twZU6dORY0aNXDu3DnMmTMHr1+/xqxZs5h11qxZEwMGDMD+/fvh7u4OFotVaftDJEdJHlF4mZmZ8PDwQExMDAIDA+Ho6Cizdffq1QtaWlpSL7dmzRpcvXoVPj4+xU7qM2bMUOg7epLg8/n48eMHVFVV5R1KtaasrFyp27t8+TL09fXRrFmzUueV5ruRnZ1dLCEkpQsMDETTpk1x7NgxqKioCL2Xmpoqp6hk5/nz54iNjYW3t7e8Q6k0NWvWlHcIlY5+L6RTo0YN1Kghu8vvnJwcqKmpiXzv4cOHWLJkCaytrbFz505oaGgw740bNw5PnjzBq1evAABNmzbFuXPnoK+vz8wzcuRIjBs3Drt27cLEiROFzvN9+vTB7t27cevWLXTo0EFm+0Nkh5prEoWWlZWFiRMn4tmzZ9iyZYtENRDSyszMFKpNKc3Hjx9x7NgxdOzYUeRdOyUlJXh4eJRYiyeu30a3bt2ELojy8/MRGBiInj17wtLSEu3atcOIESNw/fp1AD/7hAQHBzPrFPwT4PF42L9/P5ydnWFpaQl7e3v88ccf+PbtW7HtTpkyBVevXoWbmxusrKyYZhjXr1/HiBEjYGdnBxsbG/Tq1Qt//vmnxMerot2+fRscDgeRkZHYvn07OnfuDEtLS4wdOxbv3r1j5nN3d8elS5fw/v175jgV7k+Tl5eHzZs3o0ePHrCwsICjoyPWrFlTrM+BoLnohQsX0K9fP1hYWMDZ2blYE+LMzEysWLEC3bp1g4WFBTp06IDx48fj2bNnzDyF+/QkJSUxP5SBgYFMjFu2bMGpU6fA4XDw/PnzYvu/Y8cOtGjRAp8+fSr1WF2+fLncN0kEzanv3LmDpUuXokOHDkLrvHz5MkaOHAlra2vY2Nhg8uTJzEVEYYLjZ2lpiX79+uH8+fPF+jgJPtuizVdF9XMBgDdv3mDWrFlo27YtLC0t4ebmhosXL4qM//79+wgICED79u1hbW2N6dOnIy0trVicly9fxujRo2FjYwNbW1sMGjSIaUq5efNmmJubi1xu8eLFsLOzw48fP8Qey4SEBFhaWhZL8ABAW1u72P7u2bMH+/fvR9euXWFlZYXRo0fj5cuXQssJkionJydYWlqiY8eO8PHxwdevX4tt49OnT1i4cCEcHByY5uZLliwRKvMZGRlYsWIF08yrR48e2LlzJ3g8ntj9Erhw4QKUlZVhZ2cncts+Pj6wt7dnvkMnT55k3s/NzUXv3r3Ru3dv5ObmMtPT09Ph4OCA4cOHM83MgJI/J1GkLVuiyqsoRc/tgubP7969g7e3N+zs7NC6dWv4+PggJydHaNnc3FwsX74c7dq1g42NDTw9PfHp0yeJ+/lJcg7z8vKCpaUl3rx5I7Ssh4cH2rRpg0+fPiEkJAS//fYbAGDMmDHMuUhwrEr6vZCkvBQuz8HBwXByckKrVq0wYcIEfPjwAXw+H1u3bkXnzp1hZWWFqVOnIj09vdj+SnquESUxMZE5V7Rq1QpDhw7FpUuXhOYR15++aNkp7belKHF98v7++2/meLZt2xZz5szBhw8fhOZxd3dHv3798PTpU4waNQqtWrUq8fc4MDAQLBYL69atE0rwBATnSQAwMDAQSvAAgMVioXv37sjLy0NiYqLQexYWFqhbt26xcyxRHFSTRxRWTk4OJk2ahKdPn2LTpk3o2rVrsXny8vKQmZkp0fpE1Uo4OTkxtRBOTk7w9vaGjo5Oieu5cuUKCgoK0L9/f8l2pBwCAwMRFBSEIUOGwMrKCpmZmXj69CmePXuGjh07YtiwYfj8+TOuX7+ONWvWFFv+jz/+QGhoKNzc3ODu7o6kpCQEBwfj+fPnOHr0qFBNUlxcHObNm4dhw4Zh6NChMDExwatXrzBlyhRwOBzMmjULKioqePfuHR48eFDh+y6tXbt2gcViYcKECcjMzMTu3bsxf/58nDhxAgDg6emJ79+/4+PHj/Dx8QEA1KpVC8DPZHjq1Km4f/8+hg4dClNTU7x8+RIHDhxAfHw8tm3bJrSt+/fv459//sHIkSNRq1YtHDp0CLNmzcJ///2HevXqAQCWLFmCc+fOYfTo0TA1NUV6ejru37+PN2/ewNzcvFj8WlpaWLp0KZYuXYoePXqgR48eAH5eNDZu3Bj+/v4IDw9Hy5YthZYLDw9H27ZtUb9+/RKPT0pKCp4/fy7U5KYkRW8EKCkpoU6dOsxrPz8/aGlpYfr06cjOzgYAhIWFwdvbGw4ODpg/fz5ycnJw9OhRjBw5EqGhoUyzr2vXrmHmzJlo2rQp5s2bh69fv8LHx6dczZtfvXqFESNGoH79+pg0aRLU1dVx9uxZTJ8+HVu2bGGOp8Dy5cuhqamJGTNm4P379zhw4AD8/f2xceNGZp6QkBAsXLgQzZo1w5QpU1C7dm3ExMTg6tWrcHFxgaurK7Zu3YrIyEihPmd5eXk4d+4cevbsWWLNTqNGjXDz5k18/PhRon0PCwtDVlYWRo4ciR8/fuDQoUMYO3YswsPDmfPWjRs3kJiYCDc3N+jq6uLVq1c4fvw4Xr9+jePHjzPNqj59+oTBgwfj+/fvGDp0KJo0aYJPnz7h3LlzyM3NhYqKCnJycjB69Gh8+vQJw4cPR8OGDREdHY0///wTKSkp8PX1LTHe6OhomJmZFaux/vLlC4YOHQoWi4VRo0ZBS0sLV65cga+vLzIzMzFu3Dioqqpi9erVGDFiBDZs2MB8Z/39/fH
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 900x500 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAHqCAYAAAB/bWzAAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdcE/f/B/BXQFARQUC0ioCIgiigIC4cKG5w4q6Ae1TrHuBoFTeOutBq3bNV60QRq7ZupW4cuAciVhFEVEAgye8PfrkvMQECBMN4PR8PH5LLJ3fvu0vuPnnnc+8TSaVSKYiIiIiIiIiIiIioQNDSdABERERERERERERE9D9M2hIREREREREREREVIEzaEhERERERERERERUgTNoSERERERERERERFSBM2hIREREREREREREVIEzaEhERERERERERERUgTNoSERERERERERERFSBM2hIREREREREREREVIEzaEhERERERERERERUgTNoS5YGPjw98fHyEx1FRUbC1tcX+/fu/eSxhYWGwtbVFWFjYN1+2umlyO34rGzZsQKtWrWBnZ4cuXbpoOpwiKTw8HPb29nj16pUw7evPLGlOQdkXvXr1wqJFizQdBhFRkWNra4vZs2drOow88/f3h7u7e45es3//ftja2uL27dv5FJV6FaS+t2zbRUVFfZPlKesvqiI37wv6dtTdz/z999/RokULpKSkqG2eRKpg0pYKNVtbW5X+FYVEZn568+YNVq1ahYiICE2HkqkzZ85g1apVmg5DLc6fP4/FixfD2dkZCxYswIQJEzQdUpG0bNkyeHp6wszMTNOh4PHjx1i1atU3+wJSUBSG9R46dCh27dqFmJgYTYdCRFQoREZG4ueff0arVq3g4OAAZ2dn9OnTB1u3bkVycrKmw6MiaOfOnfmWUC5I/cWM8nOd82LXrl0YM2YMWrRoAVtbW/j7+2s6JLVJSEhA48aNYWtri9DQULnnvLy8kJqaij/++END0VFxVULTARDlxdejow4dOoQLFy4oTLe2tv4m8ZiZmSE8PBwlSnz7j1b9+vURHh4OHR2dHL/27du3CAoKgpmZGezs7PIhupxRth3PnDmDnTt3YvTo0RqMTD0uX74MLS0tzJs3D7q6upoOp0iKiIjAxYsXC0zH6vHjxwgKCkKDBg1QpUoVTYfzzWS13hs3btRQVPJatWoFfX197Nq1C2PHjtV0OEREBdrp06cxduxY6OrqokuXLrCxsUFqaiquXbuGxYsX4/Hjx5gzZ46mw1SrOXPmQCqVajqMYu3333+HkZERvLy81DrfgtZfzCi/1jmvNmzYgM+fP8PBwaHI/eC9cuXKTH94KlmyJLp27YotW7bAx8cHIpHoG0dHxRWTtlSofX1Z+a1bt3DhwoVsLzdPSkpC6dKl1R6PSCRCyZIl1T5fVWhpaWls2eqmye34LcTGxqJUqVLZJmwlEglSU1OL9LbIL/v27UPlypVRt25dTYeSY1KpFF++fEGpUqU0HUq+Kig/WGhpaaFdu3Y4dOgQxowZw044EVEmXr58ifHjx6Ny5crYunUrKlSoIDzXr18/vHjxAqdPn/6mMeVnXykxMRF6enq5GhBBhUNh7i/mRlpaGiQSSZ76YNu3b0flypUhEong5OSkxug06+HDh/j9998xcuRIrFy5UmmbDh06YMOGDbh8+TIaN278jSOk4orlEajI8/HxQceOHXHnzh3069cPderUwS+//AIAOHnyJIYNG4amTZvC3t4erVu3xurVqyEWixXms3v3brRu3RqOjo7o0aMHrl69qtBGWT0of39/ODk54c2bNxg5ciScnJzQqFEjBAYGKizn/fv3mDx5MpydneHi4gI/Pz/cv39fpRpTymraytb98ePH8PHxQZ06ddCsWTOsX79e7nU9evQAAEydOlUoKZFxebdu3cLgwYNRr1491KlTB97e3rh27Zrc8letWgVbW1u8ePEC/v7+cHFxQb169TB16lQkJSXJtb1w4QL69u0LFxcXODk5oV27dsI+UbYd/f39sXPnTgDyJTGkUinc3d3xww8/KGyPL1++oF69evj5558z3WYdO3ZUWutIIpGgWbNmGDNmjDDt6NGj8PLygpOTE5ydndGpUyds3bo103lnRrZeiYmJCttaVvvt8OHD8PT0hIODA86dOwcgvYTF1KlT4erqCnt7e3h6euLPP/9UmP9///2HkSNHom7dumjcuDHmz5+Pc+fOKbw33N3dlV7OpKz+U0pKClauXIk2bdrA3t4ebm5uWLRokUJNJ1n8J0+eRMeOHYU4z549q7CcN2/eYNq0acJnz93dHTNnzkRKSgpevnwJW1tbbNmyReF1169fh62tLY4cOZLldj516hQaNWqkUgIuNjYW06ZNg6urKxwcHNC5c2ccOHBAoV1uP5/79+8XRnD6+voqlG1xd3fH8OHDce7cOXh5ecHR0VEY8bFv3z74+vqicePGsLe3h4eHB3bt2qWwDNk8rl69ih49esDBwQGtWrXCwYMH5dqlpqYiKCgIbdu2hYODAxo2bIi+ffviwoULQpv79+/D399fuOy1SZMmmDp1Kt6/f6+w3Kz2Y3brrey9psq+kB0fNm7cKByX7e3t0b17d4SHh8u1jYmJwdSpU9G8eXPY29ujadOm+OGHHxTKNbi6uuLVq1cFukQMEZGmbdiwAYmJiZg3b55cwlbG0tIS/fv3V5ieXb8gs9qgsr5lRpn1lWR1UK9du4YFCxagUaNGqFu3LkaNGoW4uLhs103WX4+MjMTQoUPh5OSESZMmZRpfbvqFHz58QI8ePdC8eXM8ffpUaZvbt2/D1tZWaT9E1p/7559/AACfPn3CvHnz4O7uDnt7ezRu3BgDBw7E3bt3s11fVV26dAnff/896tatCxcXF/zwww948uSJ8Pzly5dha2uLEydOKLw2ODgYtra2uHHjBoCc9S8ycnd3x6NHj/Dvv/8KfQkfH5987y+eOXMG3t7ewj7u3r07goODM51XZvcXUfb9MLv+SWbrLJOQkIB58+bBzc0N9vb2aNOmDX777TdIJBKF5W7cuBFbtmxB69at4eDgIOy/7du3w9PTE3Xq1EH9+vXh5eWV5frJmJmZ5foH7vj4eAQGBqJTp07Cdh0yZAju378v1062LUNCQvDrr7+iefPmcHBwQP/+/fHixQuF+aryPT078+bNQ+vWreHi4pJpG3t7e5QrVw6nTp3K8fyJcosjbalYiI+Px9ChQ+Hp6YnOnTvDxMQEAHDgwAHo6elh4MCB0NPTw+XLl7Fy5Up8+vQJfn5+wuv37t2Ln3/+GU5OTujfvz9evnyJH374AYaGhqhUqVK2yxeLxRg8eDAcHR0xZcoUXLp0CZs2bYK5uTm+//57AOmJwh9++AHh4eHo27cvqlWrhlOnTsnFkRsfPnzAkCFD0KZNG3To0AHHjx/HkiVLYGNjAzc3N1hbW2PMmDFYuXIlevfujXr16gEAnJ2dAaR31oYOHQp7e3v8+OOPEIlE2L9/P/r3749du3bB0dFRbnnjxo1DlSpVMGHCBNy7dw979+6FsbExJk+eDAB49OgRhg8fDltbW4wZMwa6urp48eIFrl+/nuk69O7dG2/fvlUofSESidCpUyds3LgR8fHxKFeunPDc33//jU+fPqFz586ZzrdDhw4ICgpCTEwMTE1NhenXrl3D27dv4eHhASA9yTxhwgQ0btxY6MA/ffoU169fV/rlJCuLFi3Cnj17EB4ejrlz5wL437YG0ju/x44dQ79+/WBkZAQzMzO8e/cOvXr1gkgkQr9+/WBsbIyzZ89i+vTp+PTpEwYMGAAASE5ORv/+/fH69Wv4+PigQoUKOHToEC5fvpyjGDOSvS+vXbuGXr16wdraGg8fPsTWrVvx/PlzrFmzRq79tWvX8Ndff+H7779HmTJlsH37dowZMwb//PMPjIyMAKQn+nr06IGPHz+iV69eqFatGt68eYPjx48jOTkZ5ubmcHZ2xuHDh4V1kwkODkaZMmXQqlWrTGN+8+YNoqOjUatWrWzXLzk5GT4+PoiMjES/fv1QpUoVhIaGwt/
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1400x500 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1600x400 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Medians K=5 (Top400) ===\n",
|
|||
|
|
" log_aum_qty_mean gross_flow_to_aum flow_freq n_tx_total avg_n_isin_held n_isin_total avg_holding_months_per_isin exit_rate_per_isin flow_direction_balance aum_drawdown_last aum_final_to_peak months_since_last_tx corr_flow_fund_lag3 corr_flow_fund_lag6 corr_flow_rate_lag3\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"0 10.551 3.243 0.351 88.0 3.317 13.0 33.000 0.419 0.263 0.169 0.831 1.0 0.028 0.016 -0.039\n",
|
|||
|
|
"1 11.579 1.013 0.043 4.0 1.460 2.0 42.429 0.333 0.783 0.180 0.820 30.0 0.013 0.020 -0.012\n",
|
|||
|
|
"2 12.619 4.668 0.992 7761.5 32.634 63.0 62.937 0.621 0.017 1.000 0.000 0.0 0.160 0.130 -0.144\n",
|
|||
|
|
"3 10.975 4.083 0.763 1386.0 9.485 24.0 45.412 0.643 0.116 1.000 0.000 0.0 0.020 0.025 -0.014\n",
|
|||
|
|
"4 10.750 4.102 0.079 8.0 1.735 7.5 33.394 0.360 0.250 0.378 0.622 7.0 -0.000 -0.002 0.008\n",
|
|||
|
|
"\n",
|
|||
|
|
"=== Overall churn rates ===\n",
|
|||
|
|
"churn_hard 0.684\n",
|
|||
|
|
"churn_soft 0.775\n",
|
|||
|
|
"churn_warning 0.321\n",
|
|||
|
|
"dtype: float64\n",
|
|||
|
|
"\n",
|
|||
|
|
"=== Churn per cluster K=2 ===\n",
|
|||
|
|
" n_clients churn_hard churn_soft churn_warning\n",
|
|||
|
|
"cluster_k2 \n",
|
|||
|
|
"0 389 0.740 0.823 0.342\n",
|
|||
|
|
"1 38 0.105 0.289 0.105\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "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
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x400 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"=== Churn per cluster K=5 ===\n",
|
|||
|
|
" n_clients churn_hard churn_soft churn_warning\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"0 67 0.015 0.075 0.134\n",
|
|||
|
|
"1 29 0.138 0.276 0.138\n",
|
|||
|
|
"2 90 0.944 0.978 0.478\n",
|
|||
|
|
"3 229 0.882 0.987 0.349\n",
|
|||
|
|
"4 12 0.000 0.333 0.083\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAGGCAYAAADmRxfNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUyRJREFUeJzt3XlUVPX/x/EXjCAgrihq5m4zLmBi7mmampZbLpmaZrllZllmqXwtFVMxlyzN3MMN11zSMrXNMkUz08SlMvctF9A0AYFhfn94mF8ji8AFBvD5OMdznDv3fu577nyA+7r3c+91sdlsNgEAAACAAa7OLgAAAABA7kewAAAAAGAYwQIAAACAYQQLAAAAAIYRLAAAAAAYRrAAAAAAYBjBAgAAAIBhBAsAAAAAhhEsAAAAABhGsAByGIvFonHjxjm7jPtC8+bNNXDgQGeXIenO9z5z5kxnlwHc1+bPn68nn3xSCQkJzi4lWc8++6wmT57s7DKAFBEsgGxy5swZjR49Wi1atJC/v79q166t7t27a/HixYqJiXF2eU7zww8/sEOdxS5duqSZM2fq6NGjzi7FEIvFkqZ/e/bsyda6fvnlF/u6IyMjk7x/6dIlvf7666pTp45q166tQYMG6ezZs8m2tWbNGj311FPy9/dXq1attHTp0qwuP1eaM2eOvvnmm0xt899//9WCBQs0YMAAubr+/+5RSgd75syZI4vFosDAQENB5Ny5cyn25S+//NJh3gEDBmj58uW6cuVKhtcHZKV8zi4AuB9s375dr7/+utzd3fX000/LbDYrLi5O+/bt05QpU/TXX3/pvffec3aZTvHDDz8oNDRUr732mrNLybMuX76sjz/+WGXKlFG1atWcXU6G3X2k9vPPP9fOnTuTTK9cuXK21ZSQkKDx48fLy8tLUVFRSd6/deuWevfurZs3b2rgwIFyc3PTokWL1KtXL23YsEFFixa1z7ty5UqNGTNGrVu3Vp8+ffTLL79o/Pjxio6O1ksvvZRtnyk3mDt3rlq3bq2WLVtmWpufffaZ4uPj1a5du3vOO2/ePE2fPl2dOnXShAkTHIJIRrVr106PPfaYw7RatWo5vG7RooW8vb21fPlyvf7664bXCWQ2ggWQxc6ePauhQ4fqgQce0OLFi+Xr62t/r2fPnjp9+rS2b9+erTUlJCQoLi5O+fPnz/S2o6Ki5OXllentIufJ7u/66aefdnj922+/aefOnUmmZ6dVq1bp4sWLeuaZZ7RkyZIk7y9fvlynTp3SmjVrVLNmTUlSkyZN1L59e4WEhOjNN9+UJMXExGj69Olq1qyZZsyYIenOsJeEhATNnj1b3bp1U+HChbPvg92H1q1bp+bNm9/z9+KCBQs0bdo0dezYURMnTsyUUCFJ1atXv2dfdnV1VevWrfX5559ryJAhcnFxyZR1A5mFoVBAFluwYIGioqI0YcIEh1CRqHz58nrhhReSTP/mm2/Url07+fn5qW3btvrxxx8d3h85cqSaN2+eZLmZM2fKYrE4TEs8lb9x40a1bdtW/v7+2rFjh9atWyeLxaJ9+/YpODhYDRo0UK1atTR48OBkh3TcbeTIkQoICNCZM2c0YMAABQQE6K233pJ0Z3jIkCFD1KxZM/n5+alp06aaOHGiw7CvkSNHKjQ01F5j4r9ECQkJWrRokb3mRo0aafTo0frnn38c6ggPD1e/fv1Uv3591axZU82bN1dgYOA960/0008/6emnn5a/v7/atGmjbdu22d87e/asLBaLFi1alGS5X3/9VRaLRV988UWq7d++fVszZ85U69at5e/vr8aNG+vVV1/VmTNnUlwmPd/vzp071aNHD9WpU0cBAQFq3bq1PvjgA0nSnj179Mwzz0iSAgMD7dt43bp19uV/++039evXT4888ogefvhh9erVS/v27Ut2vX/99ZeGDRumunXr6rnnnkv1cztDVFSUJk2apKZNm8rPz0+tW7fWwoULZbPZHOb7789E4vfSuXNn7d27N83run79uj788EMNGTJEhQoVSnaerVu3yt/f3x4qpDtnVBo2bKivvvrKPm3Pnj26fv16km3as2dPRUVF3fPgw/nz5zV27Fi1bt1aNWvWVP369TVkyBCdO3cuybw3btzQxIkT1bx5c/n5+emxxx7T8OHDHX7m09Jn07KtE4f5/Le/Jbr7uqLEPnb69GmNHDlSderU0SOPPKLAwEBFR0c7LBcVFaX169fb+/PIkSMl3RnONGHCBPtna9iwofr06aPDhw+nuv3Onj2rP/74Q40aNUp1vpCQEE2ZMkUdOnRQcHBwpoWKRFFRUYqNjU11nkaNGun8+fO5fmgj8ibOWABZ7Pvvv1fZsmVVu3btNC+zb98+bdu2Tc8995wKFCigpUuXasiQIfr+++8dhk6kx+7du/XVV1+pZ8+eKlq0qMqUKaMbN25IksaPH69ChQrp1Vdf1fnz57V48WKNGzdOH3744T3bjY+Pt++UjhgxQh4eHpKkLVu2KCYmRj169FCRIkV08OBBLVu2TH///bf9iGy3bt10+fLlZIezSNLo0aO1fv16de7cWc8//7zOnTun0NBQHTlyRCtWrJCbm5siIiLUr18/FS1aVC+99JIKFSqkc+fO6euvv07Tdjl16pSGDh2q7t27q1OnTlq7dq1ef/11LViwQI8++qj9u9u4caNefPFFh2U3bdqkAgUKqEWLFim2b7VaNXDgQIWFhalt27bq3bu3bt26pZ07d+rPP/9UuXLl0lRnSo4dO6aBAwfKYrFoyJAhcnd31+nTp/Xrr79KurMTO2TIEM2YMUPdunXTI488Ikn2/hgWFqYBAwbIz89Pr776qlxcXLRu3Tq98MILWr58ucMOsSS9/vrrKl++vIYOHZpkZ93ZbDabBg0aZA9T1apV044dOzR58mRdunRJ//vf/xzm37t3rzZv3qznn39e7u7uWrFihfr37681a9bIbDbfc30fffSRSpQooe7du+uTTz5J8n5CQoL++OMPdenSJcl7/v7++umnn/Tvv//K29tbR44ckST5+fk5zFejRg25urrq6NGjqR7NDg8P1/79+9W2bVuVKlVK58+f14oVK9S7d299+eWX8vT0lHRnaFbPnj11/PhxdenSRdWrV9e1a9f03Xff6dKlSypWrFia+mx6t3V6vPHGG3rwwQf15ptv6siRI1qzZo2KFSumt99+W9KdIXHvvPOOatasqWeffVaS7D9HY8aM0datW9WrVy9VrlxZ169f1759+3T8+HHVqFEjxXXu379f0p2zBilZvHixJk2apHbt2mnSpEnJhoq0HJCRJG9vb7m7uztM+/jjjzV58mS5uLioRo0aGjp0qBo3bpxk2cQ+8uuvv6ZaL+AUNgBZ5ubNmzaz2WwbNGhQmpcxm822GjVq2E6fPm2fdvToUZvZbLYtXbrUPm3EiBG2xx9/PMnyM2bMsJnN5iRtVq1a1Xbs2DGH6WvXrrWZzWbbiy++aEtISLBPnzhxoq1atWq2GzdupFrriBEjbGaz2TZ16tQk70VHRyeZNnfuXJvFYrGdP3/ePi0oKChJvTabzbZ3716b2Wy2bdy40WH6jz/+6DD966+/tpnNZtvBgwdTrTU5jz/+uM1sNtu2bt1qn3bz5k3bo48+auvYsaN92sqVK21ms9n2119/2afFxsba6tevbxsxYkSq6/jss89sZrPZFhISkuS9/25zs9lsmzFjhv11Wr/fkJAQm9lstkVERKRYw8GDB21ms9m2du3aJOtv1aqVrW/fvg61REdH25o3b27r06dPkvW++eabqX7e7HR330nsC5988onDfK+99prNYrE4/EyZzWab2Wy2hYeH26edP3/e5u/vbxs8ePA913306FFbtWrVbDt27LDZbP+/ff77PURERNjMZrPt448/TrL8smXLbGaz2Xb8+HH7Z6lWrVqy62rQoIFt6NChqdaT3M/b/v37bWaz2bZ+/Xr7tI8++shmNptt27ZtSzJ/Yh9IS59N67Y+e/Zssn3PZkva5xO3YWBgoMN8gwcPttWrV89hWq1atZL92XvkkUdsQUFBSabfy/Tp021
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x400 with 1 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAuEAAAKyCAYAAAB7WgDLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvXe4ZUWVPvxW7XjyzZ2bbkI3Ag2CZBmZAVERlJEoamMAhVGE0Z+j+OmjoCgGBhUwOwIiZnQERNARE4IJs5KEzul233TyTlXfH1W1al+6gUaRMLPX8/TT556zQ+Vatda73sWklBKFFFJIIYUUUkghhRRSyBMm/MkuQCGFFFJIIYUUUkghhfxfk0IJL6SQQgoppJBCCimkkCdYCiW8kEIKKaSQQgoppJBCnmAplPBCCimkkEIKKaSQQgp5gqVQwgsppJBCCimkkEIKKeQJlkIJL6SQQgoppJBCCimkkCdYCiW8kEIKKaSQQgoppJBCnmAplPBCCimkkEIKKaSQQgp5gqVQwgsppJBCCimkkEIKKeQJlkIJL+QpI9/85jexfPlyrF+//skuypMqK1euxMqVK5/sYhSyAyn6ppBCgD/84Q/YZ599sGHDhie7KDuUSy+9FKeccsqTXYxCCnlUKZTwp6kYhfWPf/zjY7631+vhiiuuwC9+8Yt/QMmeuvLjH/8YV1xxxRP6zgsuuADLly+nf/vvvz+OPvponHfeebj11lshhHhc3vOb3/wGV1xxBZrN5uPyvCdbTLsdcMAB6Pf72/2+evVqatP/+q//eszP37JlC6644grcfffdj0dxnzKycuXKWePt4f490fOg2WzisMMOw/Lly3HLLbds93scx/jwhz+MI444Avvuuy9OOeUU/OxnP9vhs37zm9/g9NNPx3777YdnP/vZuPjii9HpdB5TeR544AEsX74cK1as+F8zZ3YkN954I66++urH/bkf+chHcNxxx2HBggX03cqVK3H88cdvd+2dd96J/fbbDy95yUswPT39d733qKOO2uF4fte73jXrule+8pW455578IMf/ODvel8hhfyjxX2yC1DIEy+9Xg9XXnklzj33XBxyyCFPdnGeMPnxj3+M6667Dm984xuf0Pf6vo+LL74YABBFETZs2IAf/vCHOO+883DwwQfjk5/8JKrVKl3/tyiVv/3tb3HllVfiJS95Cer1+uNW9idTXNdFv9/Hbbfdhhe+8IWzfrvxxhsRBAGiKPqbnj0+Po4rr7wSCxYswDOe8Yydvu9v6ZsnUs455xycfPLJ9Pcf//hHXHvttTjnnHOw66670vfLly9/Qst1+eWX7/AwZeSCCy7ArbfeijPOOANLlizBt771Lbzuda/DNddcgwMPPJCuu/vuu/GqV70Ku+22Gy644AJs3rwZn//857F69Wp87nOf2+ny3HDDDRgdHcXMzAxuvfXW/7VW05tuugn3338/XvWqVz1uz7z77rtxxx134Ctf+cqjXnvnnXfinHPOwdKlS3HVVVdhYGDg737/M57xDLz61a+e9d3SpUtn/T06Ooqjjz4an//853H00Uf/3e8spJB/lBRKeCGPm3S7XZTL5Se7GE+oSCkRRRHCMHzYa1zXxQknnDDruze96U34zGc+g//8z//EO9/5Tnz0ox+l33zf/0cV92klvu/jgAMOwHe+853tlPCbbroJ//zP/4xbb731CSlLr9dDqVR6yvfNs5/97Fl/B0GAa6+9FocffviTduC+77778OUvfxmvf/3rcfnll2/3+x/+8Ad85zvfwVvf+laceeaZAIB//dd/xfHHH49LL710lrJ32WWXoV6v49prr6WD68KFC/HOd74Tt99+O4444ohHLY+UEjfeeCOOP/54rF+/HjfccMP/WiX8HyHXX3895s+fj2c+85mPeN0vf/lL/Nu//RuWLFnyuCngADBnzpzt1tMdybHHHovzzz8f69atw6JFix6XdxdSyOMtBRzlf5FccMEF2H///bFlyxa8/vWvx/77749DDz0UH/zgB5FlGQBg/fr1OOywwwAAV1555Q7d0w888ABZaVesWIETTzxxO7eegcP88pe/xIUXXojDDjsMRx555COW74EHHsD555+PQw89FPvuuy+e//zn4yMf+cgj3vNwrvOjjjoKF1xwAf2dJAmuvPJKPO95z8OKFStwyCGH4PTTTyeX9gUXXIDrrruOnmn+GRFC4Oqrr8Zxxx2HFStW4PDDD8e73vUuzMzMbPfes88+Gz/96U9x4oknYt99990pi9CO5HWvex2OOOII3HLLLVi1ahV9vyPc8bXXXovjjjsO++23Hw466CCceOKJuPHGGwEAV1xxBT70oQ8BAI4++miqm8HWX3/99TjjjDNw2GGHYZ999sELX/hCfOlLX9phm5599tn49a9/jZNPPhkrVqzA0Ucfjf/+7//e7tpms4n3v//9OOqoo7DPPvvgOc95Dt761rdicnKSronjGJdffjmOOeYY7LPPPjjyyCPxoQ99CHEc73QbHX/88fjJT34yCzLwhz/8AatXr96h63t6ehof/OAH8aIXvQj7778/DjjgAJx11lm455576Jpf/OIXZC1++9vfTu31zW9+E4B1q//pT3/Cy1/+cuy333647LLL6Ld837ztbW/DihUr8MADD8wqx5lnnomDDjoIW7Zs2em6PpFy3XXX4bjjjsM+++yDI444AhdddNF2sIx8O7z0pS/Fvvvui6OOOgpf/vKXH9O73ve+9+G5z33uLIt2Xm655RY4joPTTjuNvguCACeffDJ++9vfYtOmTQCAdruNO+64Ay9+8YtneY5OOOEElMtlfPe7392p8tx1113YsGEDXvjCF+KFL3whfv3rX2Pz5s3bXSeEwDXXXIMXvehFWLFiBQ499FCceeaZ20EAv/3tb+Pkk0+mufnyl78ct99++6xrdqa9H7qmGXnomPvFL36B5cuX4+abb8YnP/lJPOc5z8GKFSvwyle+EmvWrJl1349+9CNs2LCBxvhRRx1Fvz/SmvJI8oMf/ACHHnooGGMPe82vf/1rnH322Vi8eDGuuuoqDA4OPupzH4vEcYxut/uI1xx++OFU3kIKeapKYQn/XyZZluHMM8/Evvvui7e+9a2488478fnPfx6LFi3Cy172MgwNDeHCCy/EhRdeiGOOOQbHHHMMAOuevv/++3H66adjzpw5eO1rX0ub2xve8AZcccUVdL2Riy66CENDQ3jDG97wiIviPffcg5e//OVwXRennXYaFixYgLVr1+K2227Dm970pr+73ldeeSU+/elP45RTTsG+++6LdruNP/3pT/jzn/+MZz/72TjttNMwPj6On/3sZ6Sw5uVd73oXvvWtb+HEE0/EypUrsX79elx33XX4y1/+gi9/+cvwPI+uXbVqFf7f//t/OO2003Dqqadu5wp9LPLiF78Yt99+O+64446Hfc7XvvY1XHzxxXj+85+PM844A1EU4d5778Xvf/97vOhFL8IxxxyD1atX46abbsLb3/522vCGhoYAAF/+8pexxx574KijjoLruvjhD3+Iiy66CFJKvPzlL5/1rjVr1uD888/HySefjJe85CW4/vrrccEFF2DvvffGHnvsAQDodDp4+ctfjgceeAAnnXQS9tprL0xNTeG2227Dli1bMDQ0BCEE/u3f/g133XUXTj31VOy222647777cM0112D16tX4xCc+sVPtc8wxx+Dd7343vve975HifNNNN2HXXXfFXnvttd3169atw//8z//gBS94ARYuXIht27bhq1/9Kl7xilfgO9/5DubMmYPddtsN5513Hi6//HKcdtppeNazngUAOOCAA+g509PTeO1rX4vjjjsOL37xizE8PLzD8r3jHe/Az3/+c7ztbW/DV7/6VTiOg6985Su4/fbb8aEPfQhz5szZqXo+kXLFFVfgyiuvxOGHH47TTz8dq1atwpe//GX88Y9/3G6sz8zM4HWvex2OPfZYHHfccfjud7+LCy+8EJ7nzYK9PJx897vfxW9/+1vcfPPNDxvEd/fdd2PJkiWzFGsA2Hfffen3efPm4d5770Wapthnn31
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x700 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"===== K=2 =====\n",
|
|||
|
|
" n_clients aum_qty_mean_med freq_med gross_flow_to_aum_med n_tx_med holding_med exit_rate_med flow_dir_med drawdown_med months_inactive_med corr_fund_lag3_med corr_rate_lag3_med\n",
|
|||
|
|
"cluster_k2 \n",
|
|||
|
|
"0 389 73435.780 0.763 4.335 1436.0 46.273 0.606 0.114 1.000 0.0 0.047 -0.040\n",
|
|||
|
|
"1 38 67965.743 0.043 1.506 4.0 40.357 0.292 0.580 0.303 17.5 0.006 -0.008\n",
|
|||
|
|
"\n",
|
|||
|
|
"===== K=5 =====\n",
|
|||
|
|
" n_clients aum_qty_mean_med freq_med gross_flow_to_aum_med n_tx_med holding_med exit_rate_med flow_dir_med drawdown_med months_inactive_med corr_fund_lag3_med corr_rate_lag3_med\n",
|
|||
|
|
"cluster_k5 \n",
|
|||
|
|
"3 229 58391.143 0.763 4.083 1386.0 45.412 0.643 0.116 1.000 0.0 0.020 -0.014\n",
|
|||
|
|
"2 90 302331.761 0.992 4.668 7761.5 62.937 0.621 0.017 1.000 0.0 0.160 -0.144\n",
|
|||
|
|
"0 67 38200.766 0.351 3.243 88.0 33.000 0.419 0.263 0.169 1.0 0.028 -0.039\n",
|
|||
|
|
"1 29 106782.844 0.043 1.013 4.0 42.429 0.333 0.783 0.180 30.0 0.013 -0.012\n",
|
|||
|
|
"4 12 46632.822 0.079 4.102 8.0 33.394 0.360 0.250 0.378 7.0 -0.000 0.008\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# 1. 2D behavioral segmentation: relative intensity vs activity frequency\n",
|
|||
|
|
"thr_int = dfc_400_accounts[\"gross_flow_to_aum\"].median()\n",
|
|||
|
|
"thr_freq = dfc_400_accounts[\"flow_freq\"].median()\n",
|
|||
|
|
"\n",
|
|||
|
|
"def quadrant(row):\n",
|
|||
|
|
" low_int = row[\"gross_flow_to_aum\"] < thr_int\n",
|
|||
|
|
" low_frq = row[\"flow_freq\"] < thr_freq\n",
|
|||
|
|
" if low_int and low_frq: return \"Dormant (low int, low freq)\"\n",
|
|||
|
|
" if low_int and not low_frq: return \"Small rebalancers (low int, high freq)\"\n",
|
|||
|
|
" if not low_int and low_frq: return \"Occasional large movers (high int, low freq)\"\n",
|
|||
|
|
" return \"Highly active (high int, high freq)\"\n",
|
|||
|
|
"\n",
|
|||
|
|
"dfc_400_accounts[\"seg_2D\"] = dfc_400_accounts.apply(quadrant, axis=1)\n",
|
|||
|
|
"print(dfc_400_accounts[\"seg_2D\"].value_counts())\n",
|
|||
|
|
"print(f\"thr_int: {thr_int:.4f} | thr_freq: {thr_freq:.4f}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.figure(figsize=(9, 5))\n",
|
|||
|
|
"for name, g in dfc_400_accounts.groupby(\"seg_2D\"):\n",
|
|||
|
|
" plt.scatter(g[\"flow_freq\"], g[\"gross_flow_to_aum\"], s=10, label=name)\n",
|
|||
|
|
"plt.yscale(\"log\")\n",
|
|||
|
|
"plt.axvline(thr_freq, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.axhline(thr_int, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.xlabel(\"Activity frequency (share of active months)\")\n",
|
|||
|
|
"plt.ylabel(\"Gross flow / mean AUM [log scale]\")\n",
|
|||
|
|
"plt.title(\"2D Behavioral Segmentation — Top 400 Accounts\")\n",
|
|||
|
|
"plt.legend(markerscale=2)\n",
|
|||
|
|
"plt.tight_layout()\n",
|
|||
|
|
"plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 2. K=5 clusters in the 2D intensity-frequency space\n",
|
|||
|
|
"labels_map = {\n",
|
|||
|
|
" 0: \"Cluster 0: Large & highly active movers\",\n",
|
|||
|
|
" 1: \"Cluster 1: Occasional large movers\",\n",
|
|||
|
|
" 3: \"Cluster 3: Dormant profiles\",\n",
|
|||
|
|
" 4: \"Cluster 4: Loyal clients\"\n",
|
|||
|
|
"}\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.figure(figsize=(9, 5))\n",
|
|||
|
|
"for name, g in dfc_400_accounts[~dfc_400_accounts[\"cluster_k5\"].isin([2])].groupby(\"cluster_k5\"):\n",
|
|||
|
|
" plt.scatter(\n",
|
|||
|
|
" g[\"flow_freq\"], g[\"gross_flow_to_aum\"],\n",
|
|||
|
|
" s=10, label=labels_map.get(int(name), f\"Cluster {int(name)}\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
"plt.yscale(\"log\")\n",
|
|||
|
|
"plt.axvline(thr_freq, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.axhline(thr_int, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"plt.xlabel(\"Activity frequency (share of active months)\")\n",
|
|||
|
|
"plt.ylabel(\"Gross flow / mean AUM [log scale]\")\n",
|
|||
|
|
"plt.title(\"K=5 Clusters — Intensity / Frequency Space (excluding extreme outlier C2)\")\n",
|
|||
|
|
"plt.ylim(0.1, 1000)\n",
|
|||
|
|
"plt.legend(markerscale=2)\n",
|
|||
|
|
"plt.tight_layout()\n",
|
|||
|
|
"plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 3. Dual view: trading intensity and churn/loyalty dimensions\n",
|
|||
|
|
"if \"log_n_tx_total\" not in dfc_400_accounts.columns:\n",
|
|||
|
|
" dfc_400_accounts[\"log_n_tx_total\"] = np.log1p(dfc_400_accounts[\"n_tx_total\"].clip(lower=0))\n",
|
|||
|
|
"\n",
|
|||
|
|
"thr_log_tx = dfc_400_accounts[\"log_n_tx_total\"].median()\n",
|
|||
|
|
"thr_churn = dfc_400_accounts[\"aum_drawdown_last\"].median()\n",
|
|||
|
|
"thr_hold = dfc_400_accounts[\"avg_holding_months_per_isin\"].median()\n",
|
|||
|
|
"\n",
|
|||
|
|
"color_map = {1: \"#ff7f0e\", 4: \"red\"}\n",
|
|||
|
|
"\n",
|
|||
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Graphique 1 : log_n_tx_total vs gross_flow_to_aum\n",
|
|||
|
|
"for name, g in dfc_400_accounts[~dfc_400_accounts[\"cluster_k5\"].isin([2, 4])].groupby(\"cluster_k5\"):\n",
|
|||
|
|
" axes[0].scatter(\n",
|
|||
|
|
" g[\"log_n_tx_total\"], g[\"gross_flow_to_aum\"],\n",
|
|||
|
|
" s=10, label=labels_map.get(int(name), f\"Cluster {int(name)}\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
"axes[0].set_yscale(\"log\")\n",
|
|||
|
|
"axes[0].axvline(thr_log_tx, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"axes[0].axhline(thr_int, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"axes[0].set_xlabel(\"Activity frequency (log n_tx_total)\")\n",
|
|||
|
|
"axes[0].set_ylabel(\"Gross flow / mean AUM\")\n",
|
|||
|
|
"axes[0].set_title(\"Trading intensity vs. frequency (log transactions)\")\n",
|
|||
|
|
"axes[0].set_ylim(0.1, 1000)\n",
|
|||
|
|
"axes[0].legend(markerscale=2)\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Graphique 2 : avg_holding_months_per_isin vs aum_drawdown_last\n",
|
|||
|
|
"for name, g in dfc_400_accounts[~dfc_400_accounts[\"cluster_k5\"].isin([0, 2, 3])].groupby(\"cluster_k5\"):\n",
|
|||
|
|
" axes[1].scatter(\n",
|
|||
|
|
" g[\"avg_holding_months_per_isin\"], g[\"aum_drawdown_last\"],\n",
|
|||
|
|
" s=10,\n",
|
|||
|
|
" color=color_map.get(int(name), \"gray\"),\n",
|
|||
|
|
" label=labels_map.get(int(name), f\"Cluster {int(name)}\")\n",
|
|||
|
|
" )\n",
|
|||
|
|
"axes[1].set_yscale(\"log\")\n",
|
|||
|
|
"axes[1].axvline(thr_hold, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"axes[1].axhline(thr_churn, linestyle=\"--\", color=\"gray\")\n",
|
|||
|
|
"axes[1].set_xlabel(\"avg_holding_months_per_isin\")\n",
|
|||
|
|
"axes[1].set_ylabel(\"aum_drawdown_last\")\n",
|
|||
|
|
"axes[1].set_title(\"Churn risk vs. loyalty (clusters 1 and 4)\")\n",
|
|||
|
|
"axes[1].set_ylim(0.001, 1.3)\n",
|
|||
|
|
"axes[1].legend(markerscale=2)\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.tight_layout()\n",
|
|||
|
|
"plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 4. Cluster signature heatmap — K=5\n",
|
|||
|
|
"prof_louis_k5 = plot_heatmap(\n",
|
|||
|
|
" dfc_400_accounts, profile_vars_400_accounts, \"cluster_k5\",\n",
|
|||
|
|
" title=\"Cluster signatures — 400 top accounts K=5 (robust z-score)\",\n",
|
|||
|
|
" figsize=(16, 4)\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(\"\\n=== Medians K=5 (Top400) ===\")\n",
|
|||
|
|
"print(prof_louis_k5.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 5. Churn analysis\n",
|
|||
|
|
"dfc_400_accounts[\"churn_hard\"] = (dfc_400_accounts[\"aum_final_to_peak\"] < 0.10).astype(int)\n",
|
|||
|
|
"dfc_400_accounts[\"churn_soft\"] = (\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_final_to_peak\"] < 0.40) &\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_drawdown_last\"] > 0.40)\n",
|
|||
|
|
").astype(int)\n",
|
|||
|
|
"dfc_400_accounts[\"churn_warning\"] = (\n",
|
|||
|
|
" (dfc_400_accounts[\"flow_direction_balance\"] < 0) &\n",
|
|||
|
|
" (dfc_400_accounts[\"aum_drawdown_last\"] > 0.20)\n",
|
|||
|
|
").astype(int)\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(\"\\n=== Overall churn rates ===\")\n",
|
|||
|
|
"print(dfc_400_accounts[[\"churn_hard\", \"churn_soft\", \"churn_warning\"]].mean().round(3))\n",
|
|||
|
|
"\n",
|
|||
|
|
"for k in [2, 5]:\n",
|
|||
|
|
" churn_profile = (\n",
|
|||
|
|
" dfc_400_accounts.groupby(f\"cluster_k{k}\")\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_clients = (ID_COL, \"count\"),\n",
|
|||
|
|
" churn_hard = (\"churn_hard\", \"mean\"),\n",
|
|||
|
|
" churn_soft = (\"churn_soft\", \"mean\"),\n",
|
|||
|
|
" churn_warning = (\"churn_warning\", \"mean\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(f\"\\n=== Churn per cluster K={k} ===\")\n",
|
|||
|
|
" print(churn_profile.round(3).to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
" churn_profile[[\"churn_hard\", \"churn_soft\", \"churn_warning\"]].plot(\n",
|
|||
|
|
" kind=\"bar\", figsize=(8, 4),\n",
|
|||
|
|
" color=[\"#d62728\", \"#ff7f0e\", \"#ffbb78\"]\n",
|
|||
|
|
" )\n",
|
|||
|
|
" plt.title(f\"Churn rates by cluster — Top 400 accounts (K={k})\")\n",
|
|||
|
|
" plt.ylabel(\"Rate\")\n",
|
|||
|
|
" plt.xlabel(\"Cluster\")\n",
|
|||
|
|
" plt.xticks(rotation=0)\n",
|
|||
|
|
" plt.tight_layout()\n",
|
|||
|
|
" plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 6. Inter-cluster distance matrix\n",
|
|||
|
|
"def plot_distance_matrix(X_scaled, labels, max_points=400,\n",
|
|||
|
|
" title=\"Distance matrix\"):\n",
|
|||
|
|
" n = X_scaled.shape[0]\n",
|
|||
|
|
" idx = np.arange(n)\n",
|
|||
|
|
" if n > max_points:\n",
|
|||
|
|
" rng = np.random.default_rng(42)\n",
|
|||
|
|
" idx = rng.choice(idx, size=max_points, replace=False)\n",
|
|||
|
|
" X_sub = X_scaled[idx]\n",
|
|||
|
|
" labels_sub = np.asarray(labels)[idx]\n",
|
|||
|
|
" order = np.lexsort((np.arange(len(labels_sub)), labels_sub))\n",
|
|||
|
|
" X_sub = X_sub[order]\n",
|
|||
|
|
" labels_sub = labels_sub[order]\n",
|
|||
|
|
" D = pairwise_distances(X_sub)\n",
|
|||
|
|
" plt.figure(figsize=(8, 7))\n",
|
|||
|
|
" sns.heatmap(D, cmap=\"viridis\")\n",
|
|||
|
|
" unique_labels, counts = np.unique(labels_sub, return_counts=True)\n",
|
|||
|
|
" for b in np.cumsum(counts)[:-1]:\n",
|
|||
|
|
" plt.axhline(b, color=\"red\", linewidth=2)\n",
|
|||
|
|
" plt.axvline(b, color=\"red\", linewidth=2)\n",
|
|||
|
|
" plt.title(title)\n",
|
|||
|
|
" plt.tight_layout()\n",
|
|||
|
|
" plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"plot_distance_matrix(\n",
|
|||
|
|
" X_400_accounts_scaled,\n",
|
|||
|
|
" dfc_400_accounts[\"cluster_k5\"].values,\n",
|
|||
|
|
" title=\"Inter-cluster Distance Matrix — Top 400 Accounts (K=5)\"\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 7. Full cluster profile table\n",
|
|||
|
|
"for k in [2, 5]:\n",
|
|||
|
|
" print(f\"\\n===== K={k} =====\")\n",
|
|||
|
|
" prof = (\n",
|
|||
|
|
" dfc_400_accounts.groupby(f\"cluster_k{k}\")\n",
|
|||
|
|
" .agg(\n",
|
|||
|
|
" n_clients = (ID_COL, \"count\"),\n",
|
|||
|
|
" aum_qty_mean_med = (\"aum_qty_mean\", \"median\"),\n",
|
|||
|
|
" freq_med = (\"flow_freq\", \"median\"),\n",
|
|||
|
|
" gross_flow_to_aum_med = (\"gross_flow_to_aum\", \"median\"),\n",
|
|||
|
|
" n_tx_med = (\"n_tx_total\", \"median\"),\n",
|
|||
|
|
" holding_med = (\"avg_holding_months_per_isin\",\"median\"),\n",
|
|||
|
|
" exit_rate_med = (\"exit_rate_per_isin\", \"median\"),\n",
|
|||
|
|
" flow_dir_med = (\"flow_direction_balance\", \"median\"),\n",
|
|||
|
|
" drawdown_med = (\"aum_drawdown_last\", \"median\"),\n",
|
|||
|
|
" months_inactive_med = (\"months_since_last_tx\", \"median\"),\n",
|
|||
|
|
" corr_fund_lag3_med = (\"corr_flow_fund_lag3\", \"median\"),\n",
|
|||
|
|
" corr_rate_lag3_med = (\"corr_flow_rate_lag3\", \"median\"),\n",
|
|||
|
|
" )\n",
|
|||
|
|
" .sort_values(\"n_clients\", ascending=False)\n",
|
|||
|
|
" )\n",
|
|||
|
|
" print(prof.round(3).to_string())"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "markdown",
|
|||
|
|
"id": "c97f67e5",
|
|||
|
|
"metadata": {},
|
|||
|
|
"source": [
|
|||
|
|
"---\n",
|
|||
|
|
"## 7. Cross-Analysis: Global vs Top 400 Accounts\n",
|
|||
|
|
"\n",
|
|||
|
|
"The Adjusted Rand Index (ARI) measures whether the two segmentations agree on the accounts they share. An ARI close to 0 means the two clusterings are independent — which is expected and desirable, as they are built on different feature sets and objectives.\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": 31,
|
|||
|
|
"id": "b2716808",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"COMPARAISON — Global approach vs 400 top accounts\n",
|
|||
|
|
"Accounts present in both analyses: 293\n",
|
|||
|
|
"\n",
|
|||
|
|
"Croisement K=4 x K=5 (n=293) :\n",
|
|||
|
|
" Top 400 Accounts C0 Top 400 Accounts C1 Top 400 Accounts C2 Top 400 Accounts C3 Top 400 Accounts C4\n",
|
|||
|
|
"Global C0 39.0 27.0 0.0 23.0 11.0\n",
|
|||
|
|
"Global C1 0.0 100.0 0.0 0.0 0.0\n",
|
|||
|
|
"Global C2 16.0 1.0 8.0 74.0 1.0\n",
|
|||
|
|
"Global C3 30.0 44.0 0.0 11.0 15.0\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAuwAAAHqCAYAAABfkRt8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAkHZJREFUeJzs3XdYFFfbBvB7QTpIt1IUlRKl2cVCLIk99ooYjQV790vsXYnRGBXF3nuJGjVq1KgxikZjQRRsgBWVIiDS2fn+8GXNCuiyhYXx/uXa68qeKfvMPu7y7JkzZySCIAggIiIiIqJiSUfbARARERERUcFYsBMRERERFWMs2ImIiIiIijEW7ERERERExRgLdiIiIiKiYowFOxERERFRMcaCnYiIiIioGGPBTkRERERUjLFgJyIiIiIqxliwlxCXL1+Gi4sLLl++rO1QSjR/f3/4+/trOwyllJR/A82aNcMPP/xQ6O1yj+/48eNqi2X58uVwcXFReP1BgwZh6tSpant9dXr9+jW8vLxw7tw5bYdCpLSYmBi4u7vj33//1XYo+dq5cye+/PJLZGZmajsUIjks2FX0+PFjTJ8+Hc2bN4e7uztq1qyJnj17YvPmzUhPT9d2eCXa4cOHsWnTJm2HUSirVq3CqVOntB0GKeHff//FhQsXMGjQIFlbQT8iMjMzERAQAFdXV+zbt0+tcUydOhUuLi4ICAiQa7e0tETXrl2xdOlStb6eIq5evQoXFxe4uLggISEhz/KXL19i9OjRqF27NmrWrImhQ4fiyZMn+e5r7969aN26Ndzd3fH1119j69atCsVw7do1LF++HMnJySodCynm3LlzWL58udr3u2LFCnh6eqJWrVqyth9++AHe3t551o2IiEC9evXQrFkzPH36VKXX9ff3l/0b/u9jwIABcut17twZWVlZ2LVrl0qvR6RupbQdQEl29uxZjB49Gvr6+ujQoQOcnZ2RlZWFf//9Fz/99BMePHiAOXPmqOW16tSpg9DQUOjp6allfyXBkSNHcP/+ffTr10/boShs9erVaNmyJVq0aKH2fX+O/waK0vr169GgQQM4Ojp+dL2srCyMGjUK586dw5w5c9C1a1e1xXDr1i0cOHAABgYG+S7v1asXtm7dipCQEDRo0EBtr/sxUqkUc+fOhbGxMVJTU/Msf/v2Lfr27Ys3b94gICAAenp62LRpE/r06YODBw/C0tJStu6uXbswY8YMtGzZEv3798fVq1cxd+5cpKWlYfDgwR+N4/r16wgKCkKnTp1QunRptR8nyTt37hy2b9+OkSNHqm2fCQkJOHjwIAIDAz+57r1799CvXz8YGxtj8+bNsLOzU/n1y5Urh3Hjxsm1lSlTRu65gYEBOnbsiE2bNsHf3x8SiUTl1yVSBxbsSnry5AnGjh2LChUqYPPmzXIfej8/Pzx69Ahnz54tcHupVIqsrKwC/zB/SEdHR+F1SVwyMjKgp6fHfwMaFB8fj3PnzmHmzJkfXS8rKwtjxozB2bNnMXv2bHTr1k1tMQiCgHnz5qFDhw64dOlSvutUqVIFzs7OOHDgQJEV7Lt370ZMTAy6du2KLVu25Fm+Y8cOREdHY+/evfDw8AAANG7cGO3bt8fGjRtlBVJ6ejqWLFmCL7/8EsuWLQMAdO/eHVKpFMHBwejRowfMzc2L5JhIO3777Tfo6uqiadOmH13v/v37+Pbbb2FoaIgtW7bA3t5eLa9vZmaGDh06fHK91q1bY926dbh06VKRfc6IPoVDYpS0bt06pKamYt68eXl+oQOAo6Mjvv32W9lzFxcXzJ49G7/99hvatm0Ld3d3nD9/HgBw584dDBw4EDVr1oS3tze+/fZb3LhxQ25/+Y1fjo6OxsiRI9GwYUO4u7ujSZMmGDt2LN68eSO37aFDh9C5c2d4eHigbt26GDt2LGJiYuTW8ff3R7t27RAREYE+ffrA09MTX331lWwowD///INu3brBw8MDLVu2xMWLF/Mc88uXLzFp0iT4+PigRo0aaNu2bZ7hArnH8fvvvyM4OBhNmjSBu7s7vv32Wzx69EgunrNnz+LZs2eyU5fNmjX7WEpkx9q1a1d4enqiTp068PPzw99//13g+r/++itcXFzynG5V5v12cXFBamoqDhw4IIv5v2O5C/P+HD16FEuWLEHjxo3h6emJlJSUfGPKzduDBw/g7+8PT09PNG7cGGvXrs1zrM+ePcOQIUPg5eWFBg0aYP78+Th//rzC4+IvX76Mzp07w93dHS1atMCuXbsUHiP+5MkTjBo1CnXr1oWnpye6d+9e4A9aqVSKn3/+GQ0bNoSXlxeGDBmS59/r1atXMWrUKHz55ZeoUaMGfH19MX/+fKWHoZ09exbZ2dnw8fEpcJ3s7GyMGzcOp0+fxsyZM9G9e3elXqsghw4dwr179zB27NiPrufj44MzZ85AEIQC1xEEAf7+/qhfvz7i4+Nl7ZmZmWjfvj1atGiRb2/5hxITE/HLL79g1KhRBfZqnzhxAu7u7rJiHXj3w6JBgwY4duyYrO3y5ctITExE79695bb38/NDamrqRzs4li9fjoULFwIAmjdvLvt85X5us7OzsWLFCrRo0QI1atRAs2bN8PPPP+cZh9ysWTMEBATg77//RocOHeDu7o42bdrgjz/++OR7Abw7C9OzZ0/Uq1cPHh4e6Ny5c4HXXCjyXXTu3Dn06dMH3t7eqFmzJrp06YLDhw/LrXPs2DHZ93e9evUwYcIEvHz5Um6dgq7N+eGHH+S+N58+fQoXFxesX78eu3fvlr1fXbp0QWhoqNx227dvBwC54SO5jh49is6dO8vibt++PTZv3vzJ9+/UqVPw8PCAiYlJges8fPgQ/fr1g76+vlqL9VzZ2dl4+/btR9epUaMGLCwscPr0abW+NpEq2MOupDNnzsDe3h41a9ZUeJtLly7h2LFj8PPzg6WlJSpWrIj79+/Dz88PJiYmGDhwIEqVKoXdu3fD398f27Ztg6enZ777yszMxIABA5CZmYk+ffrAxsYGL1++xNmzZ5GcnAwzMzMAQHBwMJYuXYrWrVuja9euSEhIwLZt2+Dn54eDBw/K/RFOSkrCkCFD0KZNG7Rq1Qo7d+7EuHHjIJVKMX/+fPTs2RPt2rXD+vXrMWrUKJw9exampqYAgLi4OHTv3h0SiQR+fn6wsrLCX3/9hSlTpiAlJSXPsJa1a9dCIpHgu+++Q0pKCtatW4cJEyZg7969AIAhQ4bgzZs3ePHiBSZNmgQAH/2SB4CgoCAsX74c3t7eGDVqFPT09HDz5k1cunQJjRo1UjhPyr7fCxcuxNSpU+Hh4SEr5hwcHJR6f1auXAk9PT3Za35sGExSUhIGDhyIr776Cq1bt8aJEyewaNEiODs7w9fXFwCQmpqKb7/9FrGxsejbty9sbGxw5MgRhS9gzf1RaWtri5EjR0IqlWLFihWwsrL65LZxcXHo2bMn0tLS4O/vD0tLSxw4cABDhw7FsmXL8NVXX8mtHxwcDIlEgkGDBiE+Ph6bN29Gv379cOjQIRgaGgIAjh8/jvT0dPTq1QsWFhYIDQ3Ftm3b8OLFC1nvbWFcv34dFhYWqFixYr7Lc3JyMG7cOJw8eRLTp09Hz54986yTlZWV58dyQSwsLKCj876/JCUlBYsWLcKQIUNga2v70W2rV6+OTZs24f79+3B2ds53HYlEgvnz5+Obb77BjBkzEBQUBOBd4Xv//n1s3boVxsbGn4xz6dKlsLW1Rc+ePbFy5co8y6VSKe7evYsuXbrkWebu7o6///4bKSkpMDU1xZ07dwC8K4Y+PB4dHR2Eh4cX2Pv51VdfITo6GkeOHMGkSZNkw2xy//1NnToVBw4ckA21CQ0NxerVq/Hw4UOsWLFCbl/R0dEYO3YsevbsiU6dOmH//v0YPXo01q1bh4YNG370/diyZQuaNWuG9u3bIysrC0ePHsXo0aOxevVqfPnll7L1FPku+vXXXzF58mRUq1YNAQEBMDMzQ3h4OM6fP4/27dvL1pk0aRLc3d0xbtw4xMfHY8uWLbh27Vqe7+/COHLkCN6+fYsePXpAIpFg3bp1GDl
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 800x500 with 2 Axes>"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"\n",
|
|||
|
|
"Adjusted Rand Index Global x Top 400 Accounts : 0.3394\n",
|
|||
|
|
"(0 = independent segmentations, 1 = identical)\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"source": [
|
|||
|
|
"# Comptes communs\n",
|
|||
|
|
"common_ids = set(dfc[ID_COL]).intersection(set(dfc_400_accounts[ID_COL]))\n",
|
|||
|
|
"print(f\"Accounts present in both analyses: {len(common_ids)}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Croisement des clusterings sur les shared accounts\n",
|
|||
|
|
"dfc_compare = (\n",
|
|||
|
|
" dfc[dfc[ID_COL].isin(common_ids)][[ID_COL, \"cluster_k4\"]]\n",
|
|||
|
|
" .rename(columns={\"cluster_k4\": \"cluster_global\"})\n",
|
|||
|
|
" .merge(\n",
|
|||
|
|
" dfc_400_accounts[dfc_400_accounts[ID_COL].isin(common_ids)][[ID_COL, \"cluster_k5\"]]\n",
|
|||
|
|
" .rename(columns={\"cluster_k5\": \"cluster_400_accounts\"}),\n",
|
|||
|
|
" on=ID_COL\n",
|
|||
|
|
" )\n",
|
|||
|
|
")\n",
|
|||
|
|
"\n",
|
|||
|
|
"print(f\"\\nCroisement K=4 x K=5 (n={len(dfc_compare)}) :\")\n",
|
|||
|
|
"ct = pd.crosstab(\n",
|
|||
|
|
" dfc_compare[\"cluster_global\"],\n",
|
|||
|
|
" dfc_compare[\"cluster_400_accounts\"],\n",
|
|||
|
|
" normalize=\"index\"\n",
|
|||
|
|
").round(2) * 100\n",
|
|||
|
|
"ct.index = [f\"Global C{i}\" for i in ct.index]\n",
|
|||
|
|
"ct.columns = [f\"Top 400 Accounts C{i}\" for i in ct.columns]\n",
|
|||
|
|
"print(ct.to_string())\n",
|
|||
|
|
"\n",
|
|||
|
|
"plt.figure(figsize=(8, 5))\n",
|
|||
|
|
"sns.heatmap(ct, cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
|
|||
|
|
" cbar_kws={\"label\": \"%\"})\n",
|
|||
|
|
"plt.title(\"Croisement clustering global (K=4) x 400 top accounts (K=5)\")\n",
|
|||
|
|
"plt.tight_layout(); plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
"ari = adjusted_rand_score(\n",
|
|||
|
|
" dfc_compare[\"cluster_global\"].values,\n",
|
|||
|
|
" dfc_compare[\"cluster_400_accounts\"].values\n",
|
|||
|
|
")\n",
|
|||
|
|
"print(f\"\\nAdjusted Rand Index Global x Top 400 Accounts : {ari:.4f}\")\n",
|
|||
|
|
"print(\"(0 = independent segmentations, 1 = identical)\")"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": null,
|
|||
|
|
"id": "b50d0209-deed-4ab2-ab2c-cb862282d7b5",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": []
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"execution_count": null,
|
|||
|
|
"id": "3b85bdca-920a-46ab-8032-0f15dcdc083b",
|
|||
|
|
"metadata": {},
|
|||
|
|
"outputs": [],
|
|||
|
|
"source": []
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"metadata": {
|
|||
|
|
"kernelspec": {
|
|||
|
|
"display_name": "Python 3 (ipykernel)",
|
|||
|
|
"language": "python",
|
|||
|
|
"name": "python3"
|
|||
|
|
},
|
|||
|
|
"language_info": {
|
|||
|
|
"codemirror_mode": {
|
|||
|
|
"name": "ipython",
|
|||
|
|
"version": 3
|
|||
|
|
},
|
|||
|
|
"file_extension": ".py",
|
|||
|
|
"mimetype": "text/x-python",
|
|||
|
|
"name": "python",
|
|||
|
|
"nbconvert_exporter": "python",
|
|||
|
|
"pygments_lexer": "ipython3",
|
|||
|
|
"version": "3.13.11"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"nbformat": 4,
|
|||
|
|
"nbformat_minor": 5
|
|||
|
|
}
|