Project_Carmignac/brouillon/clustering_global.ipynb

3068 lines
2.4 MiB
Plaintext
Raw Permalink Normal View History

2026-04-05 17:52:42 +02:00
{
"cells": [
{
"cell_type": "markdown",
"id": "f6ea29f1",
"metadata": {},
"source": [
"# Global Clustering \n",
"\n",
"**Sections:**\n",
"1. Imports & Data Loading\n",
"2. Monthly Panel Construction\n",
"3. Feature Engineering (base + enriched)\n",
"4. Global Clustering (all active accounts)"
]
},
{
"cell_type": "markdown",
"id": "e727f666",
"metadata": {},
"source": [
"## 0. Imports & Data Loading"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9314f229-0b5d-4a4c-846c-869847d32c73",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import s3fs\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",
"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\"])"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "61e33897",
"metadata": {},
"outputs": [],
"source": [
"import warnings\n",
"warnings.filterwarnings(\"ignore\")\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 silhouette_score, davies_bouldin_score, pairwise_distances\n",
"from sklearn.linear_model import LinearRegression\n",
"\n",
"sns.set_style(\"whitegrid\")\n",
"pd.set_option(\"display.max_columns\", 100)\n",
"\n",
"EPS = 1e-9\n",
"RANDOM_STATE = 42\n",
"\n",
"# 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",
"#external data projet-bdc-data /carmignac /Data Modélisation /Nav\n",
"PATH_NAV = \"s3://projet-bdc-data/carmignac/Data Modélisation/Nav/NAV_Bench_data.csv\" #Cest la table de valorisation / performance du produit.\n",
"PATH_RATES = \"s3://projet-bdc-data/carmignac/Data Modélisation/market data/esterRates.csv\"\n",
"\n",
"# optional competitors\n",
"PATH_COMP_FLOWS = \"s3://projet-bdc-data/carmignac/Data Modélisation/competitors/daily_estimated_flows.csv\"\n",
"PATH_COMP_PERF = \"s3://projet-bdc-data/carmignac/Data Modélisation/competitors/weekly_perf_full.csv\"\n",
"PATH_PEERS = \"s3://projet-bdc-carmignac-g3/peers/CAD_peers.csv\""
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "eb3b2908",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"flows: (2574461, 26)\n",
"aum: (4880297, 19)\n",
"nav: (623914, 6)\n"
]
}
],
"source": [
"df_flows = pd.read_csv(\"flows.csv\", low_memory=False)\n",
"df_aum = df_aum = pd.read_csv(\"s3://projet-bdc-carmignac-g3/paco/AUM_repaired.csv\", low_memory=False)\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",
"\n",
"# Month column\n",
"for df, col in [(df_flows, FLOW_DATE_COL), (df_aum, AUM_DATE_COL),\n",
" (df_nav, NAV_DATE_COL), (df_rates, RATE_DATE_COL)]:\n",
" df[\"month\"] = df[col].dt.to_period(\"M\").dt.to_timestamp()\n",
"\n",
"# Numeric coercion\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",
"# ISIN as string\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",
"print(\"flows:\", df_flows.shape)\n",
"print(\"aum: \", df_aum.shape)\n",
"print(\"nav: \", df_nav.shape)"
]
},
{
"cell_type": "markdown",
"id": "5929db69",
"metadata": {},
"source": [
"## 1. Monthly Panel Construction\n",
"\n",
"Build a full outer join of AUM and flows at (account, ISIN, month) granularity,\n",
"then enrich with NAV performance and macro rates."
]
},
{
"cell_type": "code",
"execution_count": 73,
"id": "d36d0a70",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Panel shape: (4791501, 20)\n"
]
}
],
"source": [
"# --- Flows aggregated to monthly ---\n",
"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",
"# --- AUM aggregated to monthly ---\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",
"# --- Full outer join ---\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\", suffixes=(\"\", \"_flow\"))\n",
")\n",
"\n",
"for c in [\"aum_qty\",\"aum_val\",\"net_flow_qty\",\"gross_flow_qty\",\"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",
"\n",
"# --- Active / holding flags ---\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",
"print(\"Panel shape:\", df_rel_m.shape)"
]
},
{
"cell_type": "code",
"execution_count": 74,
"id": "965d2564",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Enriched panel shape: (4791501, 24)\n"
]
}
],
"source": [
"# --- NAV & benchmark returns ---\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)\n",
" .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",
"# --- Interest rates ---\n",
"df_rates_m = (\n",
" df_rates\n",
" .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",
"# --- Merge into panel ---\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(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(\"Enriched panel shape:\", df_rel_m.shape)"
]
},
{
"cell_type": "markdown",
"id": "1ef7bba8",
"metadata": {},
"source": [
"## 2. Feature Engineering\n",
"\n",
"### 2a. Monthly account-level aggregation\n",
"### 2b. ISIN-level features (where / when investors put their money)\n",
"### 2c. Asset type & fund composition features\n",
"### 2d. Rolling metrics (1M / 3M / 6M)\n",
"### 2e. Behavioural features (entry/exit, momentum, rate sensitivity)\n",
"### 2f. Trend & beta features"
]
},
{
"cell_type": "code",
"execution_count": 168,
"id": "db5a297c-78ea-4048-98f8-624612fbb60d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"df_month shape: (931333, 21)\n",
"ISIN-level client features: (12584, 12)\n",
"Asset shares: (7475, 6)\n",
"Fund shares: (6562, 11)\n",
"Rolling features: (12584, 3)\n",
"df_client shape: (12584, 46)\n",
"After outlier removal: 7179 accounts\n",
"dfc shape: (7179, 60)\n",
"gross_flow_to_aum: min=0.0000, max=270698.4708, nan=336, inf=0\n",
"flow_direction_balance: min=-1.0000, max=1.0000, nan=1069, inf=0\n",
"sub_share_mean: min=-0.0985, max=77.5673, nan=1069, inf=0\n",
"redemption_bias: min=-154.1345, max=0.0828, nan=1069, inf=0\n",
"entry_rate_per_isin: min=0.0217, max=5.0000, nan=0, inf=0\n",
"aum_final_to_peak: min=-2.4841, max=1.0000, nan=0, inf=0\n",
"flow_roll3m_norm: min=-4935000000000.0000, max=1400000000000.0000, nan=0, inf=0\n",
"flow_roll6m_norm: min=-8699999999999.9990, max=322428000000.0000, nan=0, inf=0\n"
]
}
],
"source": [
"# ============================================================\n",
"# 2a. Monthly account-level panel\n",
"# ============================================================\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",
" 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",
"\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(),\n",
" 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(),\n",
" 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\"],\n",
" 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\"],\n",
" 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\"],\n",
" np.nan\n",
")\n",
"\n",
"print(\"df_month shape:\", df_month.shape)\n",
"\n",
"# ============================================================\n",
"# 2b. ISIN-level features\n",
"# ============================================================\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",
"\n",
"# Lag de 1 mois pour la réaction à la performance (causalité correcte)\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,\n",
" df_rel_feat[\"rel_aum_mean\"] / isin_aum,\n",
" 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",
"# ============================================================\n",
"# 2c. Asset type & fund composition features\n",
"# ============================================================\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\"],\n",
" np.nan\n",
")\n",
"asset_shares = (\n",
" aum_by_asset.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\"],\n",
" 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",
"# ============================================================\n",
"# 2d. Rolling metrics — supprimées car trop sparse (80-90% zéros)\n",
"# On garde uniquement flow_roll3m et flow_roll6m comme signaux\n",
"# de tendance récente, normalisés par l'AUM\n",
"# ============================================================\n",
"df_month_s = df_month.sort_values([ID_COL, \"month\"]).copy()\n",
"\n",
"for w in [3, 6]:\n",
" df_month_s[f\"flow_roll{w}m_norm\"] = (\n",
" df_month_s.groupby(ID_COL)\n",
" .apply(lambda g: (\n",
" g[\"net_flow_qty\"].rolling(w, min_periods=1).sum() /\n",
" (g[\"aum_qty\"].abs().rolling(w, min_periods=1).mean() + EPS)\n",
" ))\n",
" .reset_index(level=0, drop=True)\n",
" )\n",
"\n",
"rolling_feats = (\n",
" df_month_s.groupby(ID_COL, as_index=False)\n",
" .last()[[ID_COL, \"flow_roll3m_norm\", \"flow_roll6m_norm\"]]\n",
")\n",
"\n",
"print(\"Rolling features:\", rolling_feats.shape)\n",
"\n",
"# ============================================================\n",
"# 2e. Static client features\n",
"# ============================================================\n",
"df_client = (\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",
" 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",
" aum_drawdown_last = (\"aum_drawdown\", \"last\"),\n",
" region = (\"region\", \"last\"),\n",
" country = (\"country\", \"last\"),\n",
" )\n",
")\n",
"\n",
"df_client = (\n",
" df_client\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",
" .merge(rolling_feats, on=ID_COL, how=\"left\")\n",
")\n",
"\n",
"print(\"df_client shape:\", df_client.shape)\n",
"\n",
"# ============================================================\n",
"# 2f. Engineered ratios + filtres\n",
"# ============================================================\n",
"dfc = df_client.copy()\n",
"\n",
"dfc[\"log_aum_qty_mean\"] = np.log1p(dfc[\"aum_qty_mean\"].clip(lower=0))\n",
"\n",
"dfc[\"gross_flow_to_aum\"] = np.where(\n",
" dfc[\"aum_qty_mean\"] > 1,\n",
" dfc[\"gross_flow_qty_sum\"] / dfc[\"aum_qty_mean\"],\n",
" 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\"],\n",
" 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\"],\n",
" 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\"],\n",
" np.nan\n",
")\n",
"dfc[\"activity_intensity\"] = np.where(\n",
" dfc[\"n_months\"] > 0,\n",
" dfc[\"n_tx_total\"] / dfc[\"n_months\"],\n",
" 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\"],\n",
" np.nan\n",
")\n",
"dfc[\"entry_rate_per_isin\"] = np.where(\n",
" dfc[\"n_isin_total\"] > 0,\n",
" dfc[\"entry_count\"] / dfc[\"n_isin_total\"],\n",
" 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\"],\n",
" np.nan\n",
")\n",
"dfc[\"aum_drawdown_last\"] = dfc[\"aum_drawdown_last\"].clip(0, 1)\n",
"\n",
"# Log-transforms sur variables de taille brute (non utilisées en clustering)\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",
"\n",
"# Retrait des outliers sur variables de taille\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",
"print(f\"After outlier removal: {len(dfc)} accounts\")\n",
"\n",
"# Regroupement géographique\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",
"df_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",
"reference_date = df_month[\"month\"].max()\n",
"df_last_active[\"months_since_last_tx\"] = (\n",
" (reference_date.to_period(\"M\") - df_last_active[\"last_active_month\"].dt.to_period(\"M\"))\n",
" .apply(lambda x: x.n)\n",
")\n",
"dfc = dfc.merge(df_last_active[[ID_COL, \"months_since_last_tx\"]], on=ID_COL, how=\"left\")\n",
"max_months = dfc[\"months_since_last_tx\"].max()\n",
"dfc[\"months_since_last_tx\"] = dfc[\"months_since_last_tx\"].fillna(max_months + 1)\n",
"\n",
"print(\"dfc shape:\", dfc.shape)"
]
},
{
"cell_type": "markdown",
"id": "d180b613",
"metadata": {},
"source": [
"## 3. Global Clustering (all active accounts)\n",
"\n",
"Baseline clustering on all accounts with sufficient history."
]
},
{
"cell_type": "markdown",
"id": "55ab41d3-20f6-4559-8e38-68420b4230b1",
"metadata": {},
"source": [
"**Feature set final retenu**\n",
"\n",
"- flow_freq : proportion de mois avec au moins une transaction — mesure la fréquence globale d'activité du compte\n",
"- gross_flow_to_aum : volume brut de flux rapporté à l'AUM moyen — mesure l'intensité des transactions indépendamment de leur direction, après clip p90 et log-transform\n",
"- n_isin_total : nombre total d'ISINs distincts détenus sur toute la période — capte l'étendue du portefeuille exploré\n",
"- avg_holding_months_per_isin : durée moyenne de détention par ISIN — capte la fidélité aux produits\n",
"- exit_rate_per_isin : nombre moyen de sorties complètes par ISIN — mesure la propension à liquider ses positions\n",
"- flow_direction_balance : ratio flux nets sur flux bruts — distingue les acheteurs nets des vendeurs nets sur l'ensemble de la période\n",
"- log_aum_qty_mean : logarithme de l'AUM moyen — seule variable de taille retenue, incluse pour distinguer des comportements identiques sur des niveaux d'engagement très différents\n",
"- months_since_last_tx : nombre de mois écoulés depuis la dernière transaction — variable de récence, la plus discriminante du set (ratio inter/total de 0.89)"
]
},
{
"cell_type": "code",
"execution_count": 197,
"id": "353c7d48-4644-4427-ac4b-02e3f3e31690",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow_freq — min=0.0000, max=1.0000, valeurs > 1 : 0\n",
"Accounts: 7179 | Features: 8\n",
"Points > 5 std after scaling: 0 (0.0%)\n",
"\n",
"Features with most extreme values (>5 std):\n",
"Series([], )\n",
"\n",
"K=4 | sil=0.2312 | db=1.5109\n",
"\n",
"=== Tailles des clusters ===\n",
" n_comptes pct\n",
"cluster_k4 \n",
"0 2708 37.7\n",
"1 1174 16.4\n",
"2 1476 20.6\n",
"3 1821 25.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",
"=== Médianes comportement ===\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 7.884 0.071 0.000 1.0 11.333 1.000 5.280 69.0\n",
"1 5.348 0.617 -0.006 12.0 28.924 0.667 8.768 3.0\n",
"2 1.159 0.043 -1.000 3.0 60.000 0.400 5.167 27.0\n",
"3 1.477 0.012 -1.000 3.0 12.000 0.714 3.407 127.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",
"=== Médianes allocation ===\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.767 0.000 0.000 0.000 0.000 0.000 0.000\n",
"1 0.284 0.207 0.155 0.152 0.011 0.018 0.002\n",
"2 0.000 0.372 0.227 0.255 0.000 0.000 0.000\n",
"3 0.000 0.326 0.099 0.169 0.000 0.000 0.000\n"
]
}
],
"source": [
"# ============================================================\n",
"# Feature selection & preprocessing \n",
"# ============================================================\n",
"base_features = [\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 = [c for c in base_features if c in dfc.columns]\n",
"\n",
"dfc_clean = dfc.copy()\n",
"\n",
"# --- Diagnostic préalable ---\n",
"vals_ff = dfc_clean[\"flow_freq\"].to_numpy(dtype=float)\n",
"print(f\"flow_freq — min={vals_ff.min():.4f}, max={vals_ff.max():.4f}, \"\n",
" f\"valeurs > 1 : {(vals_ff > 1).sum()}\")\n",
"\n",
"# --- Imputation des NaN par 0 ---\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",
"# --- Groupe 1 : clip MAD 3 sigma ---\n",
"for col in [\n",
" \"n_isin_total\",\n",
" \"exit_rate_per_isin\",\n",
" \"avg_holding_months_per_isin\",\n",
" \"months_since_last_tx\",\n",
"]:\n",
" if col not in dfc_clean.columns:\n",
" continue\n",
" vals = dfc_clean[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",
" dfc_clean[col] = np.clip(vals, med - 3*mad, med + 3*mad)\n",
" else:\n",
" dfc_clean[col] = np.clip(vals, 0, np.nanpercentile(vals, 95))\n",
"\n",
"# --- Groupe 2 : clip p90 puis log-transform ---\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",
"# --- Groupe 3 : log_aum_qty_mean — clip MAD 3 sigma ---\n",
"col = \"log_aum_qty_mean\"\n",
"if col in dfc_clean.columns:\n",
" vals = dfc_clean[col].to_numpy(dtype=float)\n",
" med = np.nanmedian(vals)\n",
" mad = np.nanmedian(np.abs(vals - med)) * 1.4826\n",
" dfc_clean[col] = np.clip(vals, med - 3*mad, med + 3*mad)\n",
"\n",
"# --- Groupe 4 : hhi_isin — clip p90 ---\n",
"col = \"hhi_isin\"\n",
"if col in dfc_clean.columns:\n",
" vals = dfc_clean[col].to_numpy(dtype=float)\n",
" dfc_clean[col] = np.clip(vals, 0, np.nanpercentile(vals, 90))\n",
"\n",
"# --- Construction de X ---\n",
"X_num = dfc_clean[all_features].copy()\n",
"X_num = X_num.loc[:, ~X_num.columns.duplicated()]\n",
"X_num = X_num.fillna(X_num.median())\n",
"\n",
"X_cat = pd.get_dummies(\n",
" dfc_clean[[\"country_grp\", \"region_grp\"]].fillna(\"Unknown\"), drop_first=True\n",
")\n",
"X = X_num.reset_index(drop=True)\n",
"\n",
"scaler = RobustScaler()\n",
"X_scaled = scaler.fit_transform(X)\n",
"\n",
"# --- Diagnostic ---\n",
"X_df = pd.DataFrame(X_scaled, columns=X.columns)\n",
"extreme = (X_df.abs() > 5).any(axis=1).sum()\n",
"print(f\"Accounts: {X.shape[0]} | Features: {X.shape[1]}\")\n",
"print(f\"Points > 5 std after scaling: {extreme} ({extreme/len(X_df):.1%})\")\n",
"\n",
"extreme_by_feat = (X_df.abs() > 5).sum().sort_values(ascending=False)\n",
"print(\"\\nFeatures with most extreme values (>5 std):\")\n",
"print(extreme_by_feat[extreme_by_feat > 0].to_string())\n",
"\n",
"# --- Clustering K=4 ---\n",
"RESULTS = {}\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_scaled)\n",
" RESULTS[k] = {\n",
" \"model\": km,\n",
" \"silhouette\": silhouette_score(X_scaled, dfc[f\"cluster_k{k}\"]),\n",
" \"davies_bouldin\": davies_bouldin_score(X_scaled, dfc[f\"cluster_k{k}\"]),\n",
" }\n",
" print(f\"\\nK={k} | sil={RESULTS[k]['silhouette']:.4f} | db={RESULTS[k]['davies_bouldin']:.4f}\")\n",
"\n",
"print(\"\\n=== Tailles des clusters ===\")\n",
"counts = dfc[\"cluster_k4\"].value_counts().sort_index()\n",
"props = counts / counts.sum() * 100\n",
"print(pd.DataFrame({\"n_comptes\": counts, \"pct\": props.round(1)}))\n",
"\n",
"# --- Heatmap comportement ---\n",
"profile_vars_behavior = [\n",
" \"gross_flow_to_aum\",\n",
" \"flow_freq\",\n",
" \"flow_direction_balance\",\n",
" \"n_isin_total\",\n",
" \"avg_holding_months_per_isin\",\n",
" \"exit_rate_per_isin\",\n",
" \"log_aum_qty_mean\",\n",
" \"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 — Comportement (K=4, robust z-score)\",\n",
" figsize=(14, 4)\n",
")\n",
"\n",
"print(\"\\n=== Médianes comportement ===\")\n",
"print(prof_behavior.round(3).to_string())\n",
"\n",
"# --- Heatmap allocation (post-clustering, descriptive uniquement) ---\n",
"profile_vars_allocation = [\n",
" c for c in [\n",
" \"share_asset_fixed_income\",\n",
" \"share_asset_diversified\",\n",
" \"share_asset_equity\",\n",
" \"share_fund_carmignac_patrimoine\",\n",
" \"share_fund_carmignac_investissement\",\n",
" \"share_fund_carmignac_sécurité\",\n",
" \"share_fund_carmignac_emergents\",\n",
" ]\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 post-clustering)\",\n",
" figsize=(12, 4)\n",
")\n",
"\n",
"print(\"\\n=== Médianes allocation ===\")\n",
"print(prof_allocation.round(3).to_string())"
]
},
{
"cell_type": "code",
"execution_count": 199,
"id": "85747735-d0b4-4aa7-9fc2-adf030f92286",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" n_comptes pct\n",
"cluster_k4 \n",
"0 2708 37.7\n",
"1 1174 16.4\n",
"2 1476 20.6\n",
"3 1821 25.4\n"
]
}
],
"source": [
"counts = dfc[\"cluster_k4\"].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": 204,
"id": "dc171be2-e066-4352-a0ea-32d7b7b046b0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" k inertia silhouette davies_bouldin\n",
" 2 20206.150896 0.422448 0.971224\n",
" 3 16684.870723 0.241253 1.542920\n",
" 4 14655.878131 0.231172 1.510868\n",
" 5 13189.616061 0.228827 1.408857\n",
" 6 11997.575028 0.223735 1.416454\n",
" 7 11089.241350 0.229848 1.419999\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=4 | sil=0.2312 | db=1.5109\n",
" n_comptes pct\n",
"cluster_k4 \n",
"0 2708 37.7\n",
"1 1174 16.4\n",
"2 1476 20.6\n",
"3 1821 25.4\n",
"\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",
"=== Médianes comportement 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 7.884 0.071 0.000 1.0 11.333 1.000 5.280 69.0\n",
"1 5.348 0.617 -0.006 12.0 28.924 0.667 8.768 3.0\n",
"2 1.159 0.043 -1.000 3.0 60.000 0.400 5.167 27.0\n",
"3 1.477 0.012 -1.000 3.0 12.000 0.714 3.407 127.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",
"=== Médianes 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.767 0.000 0.000 0.000 0.000 0.000 0.000\n",
"1 0.284 0.207 0.155 0.152 0.011 0.018 0.002\n",
"2 0.000 0.372 0.227 0.255 0.000 0.000 0.000\n",
"3 0.000 0.326 0.099 0.169 0.000 0.000 0.000\n",
"\n",
"=== Distribution par pays (top 10) ===\n",
"country_grp Belgium FRANCE France Italy Latam Luxembourg Other Spain Switzerland United Kingdom Us Offshore\n",
"cluster_k4 \n",
"0 1.5 0.1 17.1 5.0 12.5 6.1 3.0 5.0 3.6 6.6 39.4\n",
"1 3.9 8.3 32.1 12.7 0.8 4.6 17.7 6.7 4.5 8.3 0.3\n",
"2 0.9 0.5 69.6 2.8 10.0 1.4 3.9 1.6 2.2 6.7 0.5\n",
"3 2.0 0.0 53.0 4.2 2.9 5.1 7.2 2.1 7.3 10.8 5.3\n",
"\n",
"=== Distribution par région ===\n",
"region_grp Belgium FRANCE France Germany Italy LATAM Luxembourg Other Spain Switzerland United Kingdom\n",
"cluster_k4 \n",
"0 1.5 0.1 17.2 0.6 5.0 52.0 6.1 1.9 5.2 3.6 6.7\n",
"1 3.9 8.4 32.5 1.2 12.7 1.2 4.6 15.5 7.2 4.5 8.3\n",
"2 0.9 0.5 69.8 0.9 2.8 10.6 1.4 2.5 1.7 2.2 6.8\n",
"3 2.0 0.0 53.0 4.3 4.2 8.2 5.1 2.6 2.3 7.3 10.9\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1800x400 with 4 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# ============================================================\n",
"# K-selection diagnostics\n",
"# ============================================================\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_scaled)\n",
" rows.append({\n",
" \"k\": k,\n",
" \"inertia\": km.inertia_,\n",
" \"silhouette\": silhouette_score(X_scaled, labels),\n",
" \"davies_bouldin\": davies_bouldin_score(X_scaled, labels),\n",
" })\n",
"df_kdiag = pd.DataFrame(rows)\n",
"print(df_kdiag.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[\"k\"], df_kdiag[col], marker=\"o\")\n",
" ax.set_title(title)\n",
" ax.set_xlabel(\"K\")\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"# ============================================================\n",
"# Clustering K=4\n",
"# ============================================================\n",
"RESULTS = {}\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_scaled)\n",
" RESULTS[k] = {\n",
" \"model\": km,\n",
" \"silhouette\": silhouette_score(X_scaled, dfc[f\"cluster_k{k}\"]),\n",
" \"davies_bouldin\": davies_bouldin_score(X_scaled, dfc[f\"cluster_k{k}\"]),\n",
" }\n",
" print(f\"K={k} | sil={RESULTS[k]['silhouette']:.4f} | db={RESULTS[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)}))\n",
" print()\n",
"\n",
"# ============================================================\n",
"# Heatmap comportement\n",
"# ============================================================\n",
"prof_behavior = plot_heatmap(\n",
" dfc, profile_vars_behavior, \"cluster_k4\",\n",
" title=\"Cluster signatures — Comportement (K=4, robust z-score)\",\n",
" figsize=(14, 4)\n",
")\n",
"print(\"\\n=== Médianes comportement K=4 ===\")\n",
"print(prof_behavior.round(3).to_string())\n",
"\n",
"# ============================================================\n",
"# Heatmap allocation (descriptif post-clustering)\n",
"# ============================================================\n",
"prof_allocation = plot_heatmap(\n",
" dfc, profile_vars_allocation, \"cluster_k4\",\n",
" title=\"Cluster signatures — Allocation produits (K=4, descriptif post-clustering)\",\n",
" figsize=(12, 4)\n",
")\n",
"print(\"\\n=== Médianes allocation K=4 ===\")\n",
"print(prof_allocation.round(3).to_string())\n",
"\n",
"# ============================================================\n",
"# Description géographique post-clustering\n",
"# ============================================================\n",
"print(\"\\n=== Distribution par pays (top 10) ===\")\n",
"geo_country = pd.crosstab(\n",
" dfc[\"cluster_k4\"],\n",
" dfc[\"country_grp\"].fillna(\"Unknown\"),\n",
" normalize=\"index\"\n",
").round(3) * 100\n",
"print(geo_country.to_string())\n",
"\n",
"print(\"\\n=== Distribution par région ===\")\n",
"geo_region = pd.crosstab(\n",
" dfc[\"cluster_k4\"],\n",
" dfc[\"region_grp\"].fillna(\"Unknown\"),\n",
" normalize=\"index\"\n",
").round(3) * 100\n",
"print(geo_region.to_string())\n",
"\n",
"# Visualisation heatmap géographique\n",
"fig, axes = plt.subplots(1, 2, figsize=(18, 4))\n",
"\n",
"sns.heatmap(\n",
" geo_country,\n",
" cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
" ax=axes[0],\n",
" cbar_kws={\"label\": \"%\"}\n",
")\n",
"axes[0].set_title(\"Distribution par pays (% par cluster)\")\n",
"axes[0].set_xlabel(\"Pays\")\n",
"axes[0].set_ylabel(\"Cluster\")\n",
"\n",
"sns.heatmap(\n",
" geo_region,\n",
" cmap=\"Blues\", annot=True, fmt=\".1f\",\n",
" ax=axes[1],\n",
" cbar_kws={\"label\": \"%\"}\n",
")\n",
"axes[1].set_title(\"Distribution par région (% par cluster)\")\n",
"axes[1].set_xlabel(\"Région\")\n",
"axes[1].set_ylabel(\"Cluster\")\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 201,
"id": "50ecf35e-de7e-44ae-afee-404186c4d42c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" flow_freq gross_flow_to_aum n_isin_total \\\n",
"flow_freq 1.000 0.043 0.484 \n",
"gross_flow_to_aum 0.043 1.000 0.087 \n",
"n_isin_total 0.484 0.087 1.000 \n",
"avg_holding_months_per_isin 0.074 -0.019 0.027 \n",
"exit_rate_per_isin -0.066 -0.029 -0.103 \n",
"flow_direction_balance 0.182 0.007 0.038 \n",
"log_aum_qty_mean 0.522 -0.047 0.381 \n",
"months_since_last_tx -0.513 -0.012 -0.229 \n",
"\n",
" avg_holding_months_per_isin exit_rate_per_isin \\\n",
"flow_freq 0.074 -0.066 \n",
"gross_flow_to_aum -0.019 -0.029 \n",
"n_isin_total 0.027 -0.103 \n",
"avg_holding_months_per_isin 1.000 -0.257 \n",
"exit_rate_per_isin -0.257 1.000 \n",
"flow_direction_balance -0.163 0.093 \n",
"log_aum_qty_mean 0.140 0.024 \n",
"months_since_last_tx -0.306 0.159 \n",
"\n",
" flow_direction_balance log_aum_qty_mean \\\n",
"flow_freq 0.182 0.522 \n",
"gross_flow_to_aum 0.007 -0.047 \n",
"n_isin_total 0.038 0.381 \n",
"avg_holding_months_per_isin -0.163 0.140 \n",
"exit_rate_per_isin 0.093 0.024 \n",
"flow_direction_balance 1.000 0.298 \n",
"log_aum_qty_mean 0.298 1.000 \n",
"months_since_last_tx -0.119 -0.389 \n",
"\n",
" months_since_last_tx \n",
"flow_freq -0.513 \n",
"gross_flow_to_aum -0.012 \n",
"n_isin_total -0.229 \n",
"avg_holding_months_per_isin -0.306 \n",
"exit_rate_per_isin 0.159 \n",
"flow_direction_balance -0.119 \n",
"log_aum_qty_mean -0.389 \n",
"months_since_last_tx 1.000 \n"
]
}
],
"source": [
"# Test : corrélations entre features\n",
"corr_matrix = dfc[base_features].corr().round(3)\n",
"print(corr_matrix)"
]
},
{
"cell_type": "code",
"execution_count": 202,
"id": "273392b8-c60c-4b19-ab4e-760616d3c246",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"base_features: 8\n",
"share_asset: []\n",
"share_fund: []\n",
"X columns: 8\n",
"X_num columns: 8\n",
"X_cat columns: 20\n"
]
}
],
"source": [
"print(\"base_features:\", len(base_features))\n",
"print(\"share_asset:\", [c for c in all_features if c.startswith(\"share_asset_\")])\n",
"print(\"share_fund:\", [c for c in all_features if c.startswith(\"share_fund_\")])\n",
"print(\"X columns:\", X.shape[1])\n",
"print(\"X_num columns:\", X_num.shape[1])\n",
"print(\"X_cat columns:\", X_cat.shape[1])"
]
},
{
"cell_type": "code",
"execution_count": 203,
"id": "d42b5319-c66c-4a7f-aeac-d3044d07f499",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== share_fund_ disponibles ===\n",
"['share_fund_carmignac_court_terme', 'share_fund_carmignac_emergents', 'share_fund_carmignac_investissement', 'share_fund_carmignac_patrimoine', 'share_fund_carmignac_portfolio_credit', 'share_fund_carmignac_portfolio_flexible_b', 'share_fund_carmignac_portfolio_global_bon', 'share_fund_carmignac_portfolio_patrimoine', 'share_fund_carmignac_portfolio_sécurité', 'share_fund_carmignac_sécurité']\n",
"\n",
"=== share_asset_ disponibles ===\n",
"['share_asset_alternative', 'share_asset_diversified', 'share_asset_equity', 'share_asset_fixed_income', 'share_asset_private_assets']\n",
"share_fund_carmignac_court_terme: 0.000000\n",
"share_fund_carmignac_emergents: 0.000001\n",
"share_fund_carmignac_investissement: 0.000032\n",
"share_fund_carmignac_patrimoine: 0.011248\n",
"share_fund_carmignac_portfolio_credit: 0.000000\n",
"share_fund_carmignac_portfolio_flexible_b: 0.000000\n",
"share_fund_carmignac_portfolio_global_bon: 0.000000\n",
"share_fund_carmignac_portfolio_patrimoine: 0.000000\n",
"share_fund_carmignac_portfolio_sécurité: 0.000000\n",
"share_fund_carmignac_sécurité: 0.000080\n",
"share_asset_alternative: 0.000000\n",
"share_asset_diversified: 0.027594\n",
"share_asset_equity: 0.009158\n",
"share_asset_fixed_income: 0.130769\n",
"share_asset_private_assets: 0.000000\n"
]
}
],
"source": [
"# Identifier les share_fund_ et share_asset_ les plus discriminantes\n",
"top_share_funds = [c for c in dfc.columns if c.startswith(\"share_fund_\")]\n",
"top_share_assets = [c for c in dfc.columns if c.startswith(\"share_asset_\")]\n",
"\n",
"# Regarder lesquelles varient le plus entre clusters\n",
"print(\"=== share_fund_ disponibles ===\")\n",
"print(top_share_funds)\n",
"print(\"\\n=== share_asset_ disponibles ===\")\n",
"print(top_share_assets)\n",
"\n",
"# Variance inter-cluster pour chaque share variable\n",
"for col in top_share_funds + top_share_assets:\n",
" var_inter = dfc.groupby(\"cluster_k4\")[col].median().var()\n",
" print(f\"{col}: {var_inter:.6f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7de1a58e-37ec-4d13-9807-5b047ec6ff42",
"metadata": {},
"outputs": [],
"source": [
"import subprocess\n",
"subprocess.run([\"jupyter\", \"nbconvert\", \"--to\", \"html\", \"clustering_clean.ipynb\"])"
]
},
{
"cell_type": "code",
"execution_count": 206,
"id": "b56a53c8-c1eb-4117-a028-ba9b6c1c45af",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== Types d'assets disponibles ===\n",
"Product - Asset Type\n",
"Equity 2023860\n",
"Diversified 1377424\n",
"Fixed Income 948728\n",
"Alternative 214235\n",
"Private Assets 219\n",
"Name: count, dtype: int64\n",
"\n",
"df_month_asset shape: (1745996, 15)\n",
"df_client_asset shape: (13328, 20)\n",
"\n",
"Comptes par asset type:\n",
"Product - Asset Type\n",
"Diversified 4161\n",
"Fixed Income 3934\n",
"Equity 3901\n",
"Alternative 1319\n",
"Private Assets 13\n",
"Name: Registrar Account - ID, dtype: int64\n",
"\n",
"Asset types retenus (>= 50 comptes) : ['Alternative', 'Diversified', 'Equity', 'Fixed Income']\n",
"\n",
"============================================================\n",
"ASSET TYPE : Alternative\n",
"============================================================\n",
" k silhouette davies_bouldin\n",
" 2 0.4568 0.9959\n",
" 3 0.3416 1.1374\n",
" 4 0.2564 1.3750\n",
" 5 0.2815 1.2393\n",
" 6 0.2640 1.3549\n",
"→ K retenu : 2 (silhouette=0.4568)\n",
" n_comptes pct\n",
"cluster_alternative \n",
"0 311 23.6\n",
"1 1008 76.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",
"Médianes — 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.037 1.000 0.104 5.777 12.0 0.914 0.086\n",
"1 0.069 4.729 0.512 -0.072 5.067 66.0 0.000 1.000\n",
"\n",
"============================================================\n",
"ASSET TYPE : Diversified\n",
"============================================================\n",
" k silhouette davies_bouldin\n",
" 2 0.6029 0.6519\n",
" 3 0.5108 0.8195\n",
" 4 0.4847 0.9830\n",
" 5 0.4697 0.8759\n",
" 6 0.3327 1.1264\n",
"→ K retenu : 2 (silhouette=0.6029)\n",
" n_comptes pct\n",
"cluster_diversified \n",
"0 3368 80.9\n",
"1 793 19.1\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABOQAAAGGCAYAAADbxV7qAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA5kBJREFUeJzs3Xd8jef/x/HXyUKEECGIHXtTo1YpqVmjFKWovam9G5vW3rVj791vY5QaRdCqvap2UGJFIiGSnN8ffjnNkUSPCIfk/Xw87scj5z7XfZ3Pfc6d65zzOdcwGI1GIyIiIiIiIiIiIvJO2Fg7ABERERERERERkcRECTkREREREREREZF3SAk5ERERERERERGRd0gJORERERERERERkXdICTkREREREREREZF3SAk5ERERERERERGRd0gJORERERERERERkXdICTkREREREREREZF3SAk5ERERERERERGRd0gJORGRBKJy5coMGDDA2mG8Uxs2bCBPnjz4+flZOxSJR35+fuTJk4cNGzZYO5RYNW/enObNm5vtu3fvHt27d6d06dLkyZOHRYsWcfjwYfLkycPhw4fj7bGnT59Onjx5LC7frl07hgwZEm+P/yFr3rw5n3/+ubXDkCgaNWrEuHHjrB2GiIjIO6eEnIjIe+769et4eXlRpUoVChUqRPHixfnqq69YvHgxT58+fScxhISEMH369HhNKiREP/30E4sWLbJ2GO+dPHnymLb8+fNTqlQp6tevz6hRo/j777+tHV68GTt2LL/99hvt27dn3LhxVKhQwdohcfToUQ4cOEC7du1M+yKThNu2bTMrGxoaSocOHcibNy/r1q17o8f19fVl4MCBVKtWjSJFilClShUGDx7M3bt336jeD9Xs2bPZuXOntcN4L7Vr144VK1bg7+9v7VBERETeKTtrByAiIrHbs2cP3377LQ4ODtStW5fcuXPz/Plzjh49yvjx4/n7778ZOXLkW48jJCSEGTNm0LVrV0qXLv3WH89SdevWpVatWjg4OFg7FAD+97//cfHiRVq2bGntUN475cqVo27duhiNRoKCgjh//jybNm1i5cqV9OnTh1atWpnKuru7c/LkSezs3t+PKQsWLIi279ChQ1SpUoU2bdqY9mXPnp2TJ09ib2//LsMzWbBgAWXKlCFr1qyvLPf8+XO6d+/O3r17GTlyJF9+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
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.045 0.625 -0.577 5.064 80.0 0.000 1.000\n",
"1 0.085 0.218 1.000 -0.682 5.156 12.0 0.907 0.093\n",
"\n",
"============================================================\n",
"ASSET TYPE : Equity\n",
"============================================================\n",
" k silhouette davies_bouldin\n",
" 2 0.3701 1.3831\n",
" 3 0.4248 0.9505\n",
" 4 0.2858 1.3678\n",
" 5 0.2587 1.4438\n",
" 6 0.2779 1.3114\n",
"→ K retenu : 3 (silhouette=0.4248)\n",
" n_comptes pct\n",
"cluster_equity \n",
"0 2384 61.1\n",
"1 769 19.7\n",
"2 748 19.2\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABM4AAAGGCAYAAACDus3zAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA85RJREFUeJzs3XVYVGkbBvB7KBEpCRFQUVEQFVQWsQtdsUVdA7sDe9cOVESxCwxUVLATjEXFNXcVuxARXRuTku6Z7w8/ZhkBHYeYEe/fdZ1L55z3vPMcOLwz88wbApFIJAIRERERERERERFJUJJ3AERERERERERERIqIiTMiIiIiIiIiIqI8MHFGRERERERERESUBybOiIiIiIiIiIiI8sDEGRERERERERERUR6YOCMiIiIiIiIiIsoDE2dERERERERERER5YOKMiIiIiIiIiIgoD0ycERERERERERER5YGJMyIiysXBwQEzZsyQdxjF6siRI7C0tERERIS8QyEFNWPGDDg4OBTb823ZsgXt2rWDUCgstudUVJ6enrC0tERMTIy8Q6H/W7FiBXr27CnvMIiIiIocE2dERD+RV69ewdXVFa1bt4a1tTVsbW3Rp08f+Pr6IjU1tVhiSElJgaenJ65du1Ysz/ejOn78OHbs2CHvMBSOpaVlvpurq2uxxlKU93JiYiK2bt2KESNGQEnpv7drlpaWcHNzy1V+06ZNsLS0xMyZMwuUaPvw4QOmTJkCR0dH1KtXD3Z2dvjtt9/g7+8PkUgkc70/Kv4d5m/QoEF49OgRzp49K+9QiIiIipSKvAMgIqLiceHCBUycOBFqamro2rUrLCwskJGRgVu3bmH58uX4999/sXDhwiKPIyUlBV5eXhg3bhwaNGhQ5M8nra5du6Jjx45QU1OTdygAgBMnTuDJkycYPHiwvENROE2aNEHXrl1z7a9SpUqRPu/ChQslkkdFeS8fOnQImZmZ6NSp0zfLbt68GatXr0a3bt2waNEiiUTb94qNjcWHDx/Qrl07GBsbIzMzE5cvX8aMGTPw/Plz/P777zLX/SPi32H+DA0N0bp1a2zbtg2tW7eWdzhERERFhokzIqKfwOvXrzF58mSYmJjA19cX5cqVEx/r168fXr58iQsXLsgvwEKQnJwMDQ0Nmc9XVlaGsrJyIUakmFJSUlC6dGl5h1EglStXzjNxVtRUVVWL7bmOHDkCBwcHlCpV6qvltm7dipUrV8LJyQmLFy8uUNIMAGrUqIGdO3dK7Ovfvz9Gjx6NnTt3YuLEiYXyd5KZmQmhUKgwiWqSrQ1t3749Jk6ciNevX6NixYpFFBkREZF8cagmEdFPYOvWrUhOTsaiRYskkmbZzMzMMGjQoHzPz55f6Et5zQsWEhKCYcOGoUGDBrCxsYGDgwNmzpwJAIiIiECjRo0AAF5eXuIhdp6enuLznz59igkTJsDe3h7W1tbo3r17rqFA2c97/fp1zJ8/H40aNUKLFi2++jPYuXMnOnbsiDp16qB+/fro3r07jh8//tVrEQqF8PT0RNOmTVGnTh0MGDAA//77b6454LLPvXXrFjw8PNCwYUPUrVsXY8eOzTUn019//YWRI0eiadOmqF27Ntq0aYP169cjKytLXGbAgAG4cOEC3rx5I/4ZZc+tld9cbNeuXYOlpaXEsMEBAwagU6dOePDgAfr164c6depg1apVAID09HSsW7cOv/76K2rXro0WLVpg2bJlSE9Pl6j38uXLcHZ2hp2dHerVqwdHR0dxHYpu//79aNOmDWxsbPDbb7/h5s2bGDBgAAYMGCAu8z0/z5xznH3tXj58+DAsLS3x8OHDXDFt2rQJVlZW+PDhQ75xv379GuHh4WjcuPFXr2/79u1Yvnw5unTpAg8PjwInzb7G1NQUKSkpyMjI+O5zIyIiYGlpCR8fH+zYsQNt2rSBtbU1nj59CgAIDg5G3759UbduXdjZ2WHMmDHiY1+KjY3FxIkTYWtriwYNGsDd3R1paWm5nuvIkSO5zv2yrUlMTMSiRYvg4OCA2rVro1GjRhgyZAhCQ0MBfP3vMC8zZszIdxhxzufNS0ZGBry8vNC2bVtYW1ujQYMGcHZ2xuXLlyXKPX36FBMnTkTDhg1hY2MDR0dHrF69WqLMw4cPMXz4cNja2qJevXoYNGgQ7t69K1HmW23oxYsXxb+TevXqYeTIkXjy5EmuuLPvUQ7XJCKikow9zoiIfgLnz59HxYoVYWtrW6TPEx0djWHDhqFs2bIYOXIktLW1ERERgTNnzgAA9PT0MH/+fMyfPx+//vorfv31VwAQJ+WePHkCZ2dnGBkZYcSIEdDQ0MDJkycxduxYeHp6istnW7BgAfT09DB27FgkJyfnG9eBAwfg7u4OR0dHDBw4EGlpaQgPD8e9e/fQuXPnfM9buXIltm7dilatWqFZs2Z49OgRhg0bJvFBPSd3d3doa2tj3LhxePPmDXx9feHm5oY1a9aIy/j7+0NDQwNDhgyBhoYGrl69inXr1iExMRHTp08HAIwePRoJCQl4//69OOlYpkyZb/z08/bp0yeMGDECHTt2RJcuXaCvrw+hUIgxY8bg1q1b6NWrF8zNzfH48WP4+vrixYsX2LBhA4DPv49Ro0bB0tISEyZMgJqaGl6+fInbt2/LFEthSUtLy3OSeE1NTXEPpoMHD8LV1VWcOHj9+jXGjBkDHR0dGBsbFziGr93LFSpUgJubG44fP46aNWtKnHf8+HHY29vDyMgo37rv3LkDALnOzcnX1xdLlixBp06dsGTJkjyTZtJOpJ/z55YtNTUVycnJSE5Oxo0bN3DkyBHUrVsX6urqUtWZlyNHjiAtLQ29evWCmpoadHR0cOXKFYwYMQIVKlTAuHHjkJqail27dsHZ2RlHjhxBhQoVJOqYNGkSTE1N8ccff+Du3bvYuXMn4uPjsWzZsu+OZ968eTh9+jT69+8Pc3NzfPr0Cbdu3cLTp09Rq1at7/477N27tziZmu3vv//G8ePHoaen99VYvLy84O3tjZ49e8LGxgaJiYl48OABQkND0aRJEwDAo0eP0K9fP6ioqKB3794wNTXFq1evcO7cOUyePBnA57/Zfv36oUyZMhg+fDhUVFSwf/9+DBgwALt27UKdOnUknjevNjQgIAAzZsxA06ZNMWXKFKSkpGDv3r3o27cv/P39JX4nWlpaqFSpEm7fvs3hrEREVGIxcUZEVMIlJibiw4cPxTIHzZ07dxAXFwcfHx9YW1uL92d/qNPQ0ICjoyPmz58PS0vLXMPtFi1aBGNjYxw+fFj8Qb5v375wdnbGihUrciXOdHR0sGPHjm8OHbtw4QKqV6+OdevWSX0tUVFR4t4x69evF+/38vLKt/eIrq4utm3bBoFAAOBzj7WdO3ciISEBWlpaAD4n43ImH5ydneHq6oq9e/di8uTJUFNTQ5MmTeDn54f4+PgCD0mMjIzEggUL0KdPH/G+o0eP4sqVK9i5cyfs7OzE+6tXr4558+bh9u3bsLW1xeXLl5GRkYEtW7Z884N/cTp06BAOHTqUa/+qVavQsWNHZGRkYPXq1bCysoKfn5/4XqpWrRrmzp1bKImzb93Lbdq0wYkTJzB16lRxUuvhw4f4999/MWzYsK/W/ezZMwDIlTTKlt0LqlOnTli2bFm+9/+XSZz8eHh4oHv37hL7/Pz8sHLlSom6PDw8pKovP+/fv8eZM2ck7iUXFxfo6Ohg//790NXVBfD5Z9etWzd4enpi6dKlEnVUqFABGzduBPB5mLmmpib27NmDoUOHokaNGt8Vz8WLF9GrVy+J3qMjRowQ//97/w7r1auHevXqiR+/fPkSCxcuRJMmTST+/vJy4cIFtGjR4qvzTLq7u0MkEsHf3x8mJibi/VOmTBH/f82aNcjIyMDevXvFQyednJzQrl07LF++HLt27ZKo88s2NCkpCYsWLULPnj0lYunWrRvatWsHb2/vXDFWrFgR//7771evj4iI6EfGxBkRUQmXmJgIQPYeS98jOzl04cIF1KhR47vmhPr06ROuXr2KCRMmiGPO1rRpU3h6euLDhw8SPXV69eol1XxL2tr
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.025 3.296 0.576 -0.835 3.976 90.0 0.000 1.000\n",
"1 0.071 0.064 1.056 -0.935 4.554 12.0 0.975 0.025\n",
"2 0.646 3.610 3.588 -0.099 8.474 0.0 0.154 0.846\n",
"\n",
"============================================================\n",
"ASSET TYPE : Fixed Income\n",
"============================================================\n",
" k silhouette davies_bouldin\n",
" 2 0.6758 0.5130\n",
" 3 0.4226 0.8457\n",
" 4 0.4348 0.9977\n",
" 5 0.4601 0.9193\n",
" 6 0.4386 0.9478\n",
"→ K retenu : 2 (silhouette=0.6758)\n",
" n_comptes pct\n",
"cluster_fixed_income \n",
"0 3142 79.9\n",
"1 792 20.1\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABOQAAAGGCAYAAADbxV7qAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA6BVJREFUeJzs3XdUE8vbB/BvQhHpAoKKiooCNhQLNrw27L1hb6goCooNsWEXr2JBsYvYu4J6xd4Lluu1945YaQoIipB9//AlPwJEQyhR/H7OyTlkdnbyhGwmmyczOyJBEAQQERERERERERFRnhCrOgAiIiIiIiIiIqI/CRNyREREREREREREeYgJOSIiIiIiIiIiojzEhBwREREREREREVEeYkKOiIiIiIiIiIgoDzEhR0RERERERERElIeYkCMiIiIiIiIiIspDTMgRERERERERERHlISbkiIiIiIiIiIiI8hATckREv7jGjRvDy8tL1WHkqb1798La2hrh4eGqDoWyoU+fPujTp0+eP66XlxcaN26c54/7u1uzZg1atGgBiUSi6lBUbunSpbC2tkZ0dLSqQ6H/5+vri65du6o6DCIiohzDhBwRkYqEhYXB29sbTZo0QeXKlVGtWjV0794dGzZswJcvX/IkhsTERCxduhSXL1/Ok8f7XR04cADr169XdRi/HGtr60xv9erVU3VoCunTpw/atGmj6jB+CfHx8Vi7di0GDx4Msfh/p4fW1taYMWNGhvorV66EtbU1JkyYkK0E3tOnTzFv3jy0b98ednZ2cHBwgIuLC27fvq10m78z9jXy9evXDw8ePMCJEydUHQoREVGOUFd1AEREf6LTp09j5MiR0NTURPv27WFlZYVv377h2rVrmD9/Pp48eYKZM2fmehyJiYnw9/eHm5sbatWqleuPp6j27dujdevW0NTUVHUoAIB//vkHjx8/Rv/+/VUdyi+nXr16aN++vUyZlpYWACAgIEAVIZESdu/ejeTkZIUSlKtXr8aiRYvQsWNHzJ49WyaBp8zj7t69G82aNUPPnj0RFxeHHTt2oFu3bli7di3q1q2rdNu/I/Y18hUuXBhNmjTBunXr0KRJE1WHQ0RElG1MyBER5bFXr15h1KhRKFasGDZs2ABTU1Pptl69euHly5c4ffq06gLMAQkJCdDW1lZ6fzU1NaipqeVgRL+mxMREFCxY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
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.231 0.48 0.000 5.142 69.0 0.000 1.000\n",
"1 0.182 2.224 1.50 0.472 7.279 2.0 0.998 0.002\n",
"\n",
"============================================================\n",
"RÉSUMÉ — Clustering par asset type\n",
"============================================================\n",
" Alternative : K=2, sil=0.4568, n=1319\n",
" Diversified : K=2, sil=0.6029, n=4161\n",
" Equity : K=3, sil=0.4248, n=3901\n",
" Fixed Income : K=2, sil=0.6758, n=3934\n"
]
}
],
"source": [
"# ============================================================\n",
"# CLUSTERING PAR TYPE D'ASSET\n",
"# ============================================================\n",
"\n",
"# ── 0. Vérification des types d'assets disponibles ────────────────────────\n",
"print(\"=== Types d'assets disponibles ===\")\n",
"print(df_aum[ASSET_COL].value_counts())\n",
"\n",
"# ── 1. Construction du panel par asset type ───────────────────────────────\n",
"# Pour chaque compte x asset type, on agrège les features comportementales\n",
"\n",
"# Merge AUM avec flows pour avoir les flux par asset type\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\"],\n",
" how=\"left\"\n",
")\n",
"\n",
"# Panel mensuel par compte x asset type\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\"],\n",
" np.nan\n",
")\n",
"df_month_asset[\"sub_share\"] = np.where(\n",
" df_month_asset[\"gross_flow_qty\"] > 0,\n",
" df_month_asset[\"sub_qty\"] / df_month_asset[\"gross_flow_qty\"],\n",
" np.nan\n",
")\n",
"df_month_asset[\"aum_peak\"] = df_month_asset.groupby(\n",
" [ID_COL, ASSET_COL]\n",
")[\"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\"],\n",
" np.nan\n",
")\n",
"\n",
"print(\"\\ndf_month_asset shape:\", df_month_asset.shape)\n",
"\n",
"# ── 2. Feature engineering par compte x asset type ────────────────────────\n",
"# months_since_last_tx par asset\n",
"reference_date = df_month_asset[\"month\"].max()\n",
"\n",
"last_active_asset = (\n",
" df_month_asset[df_month_asset[\"active_month\"] == 1]\n",
" .groupby([ID_COL, ASSET_COL])[\"month\"]\n",
" .max()\n",
" .reset_index(name=\"last_active_month\")\n",
")\n",
"last_active_asset[\"months_since_last_tx_asset\"] = (\n",
" (reference_date.to_period(\"M\") - 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",
" flow_direction_mean = (\"flow_direction\", \"mean\"),\n",
" sub_share_mean = (\"sub_share\", \"mean\"),\n",
" aum_drawdown_last = (\"aum_drawdown\", \"last\"),\n",
" )\n",
")\n",
"\n",
"# Merge months_since_last_tx\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",
"max_months = df_client_asset[\"months_since_last_tx_asset\"].max()\n",
"df_client_asset[\"months_since_last_tx_asset\"] = (\n",
" df_client_asset[\"months_since_last_tx_asset\"].fillna(max_months + 1)\n",
")\n",
"\n",
"# Ratios protégés\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\"],\n",
" 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\"],\n",
" 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),\n",
" 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",
"\n",
"# Filtre qualité\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(\"df_client_asset shape:\", df_client_asset.shape)\n",
"print(\"\\nComptes par asset type:\")\n",
"print(df_client_asset.groupby(ASSET_COL)[ID_COL].nunique().sort_values(ascending=False))\n",
"\n",
"# ── 3. Sélection des asset types avec suffisamment de comptes ─────────────\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\"\\nAsset types retenus (>= {min_accounts} comptes) : {valid_assets}\")\n",
"\n",
"# ── 4. Features pour le clustering par asset ──────────────────────────────\n",
"asset_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_asset\",\n",
" \"aum_final_to_peak\",\n",
" \"aum_drawdown_last\",\n",
"]\n",
"\n",
"# ── 5. Clustering par asset type ──────────────────────────────────────────\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",
" # Preprocessing\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",
" 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_a = d[feats].fillna(d[feats].median()).to_numpy()\n",
" X_a_scaled = RobustScaler().fit_transform(X_a)\n",
"\n",
" # K-selection\n",
" best_k = 2\n",
" best_sil = -1\n",
" rows_k = []\n",
" max_k = min(6, len(df_a) // 50) # K max raisonnable selon taille\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 = 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",
" df_a[f\"cluster_{asset.lower().replace(' ','_')}\"] = km_final.fit_predict(X_a_scaled)\n",
" cluster_col = f\"cluster_{asset.lower().replace(' ','_')}\"\n",
"\n",
" # Tailles\n",
" counts = df_a[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 comportement\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\"\\nMédianes — {asset}:\")\n",
" print(prof.round(3).to_string())\n",
"\n",
" ASSET_RESULTS[asset] = {\n",
" \"df\": df_a,\n",
" \"cluster_col\": cluster_col,\n",
" \"k\": best_k,\n",
" \"silhouette\": best_sil,\n",
" \"profile\": prof,\n",
" }\n",
"\n",
"# ── 6. Résumé global ──────────────────────────────────────────────────────\n",
"print(\"\\n\" + \"=\"*60)\n",
"print(\"RÉSUMÉ — Clustering par asset type\")\n",
"print(\"=\"*60)\n",
"for asset, res in ASSET_RESULTS.items():\n",
" print(f\" {asset:20s} : K={res['k']}, sil={res['silhouette']:.4f}, \"\n",
" f\"n={len(res['df'])}\")"
]
},
{
"cell_type": "code",
"execution_count": 207,
"id": "78b9b46c-577c-4e00-80ef-07e4aeb807be",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Colonnes disponibles : ['Registrar Account - ID', 'cluster_k4', 'cluster_alternative', 'cluster_diversified', 'cluster_equity', 'cluster_fixed_income']\n",
"Shape : (7179, 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 x Alternative\n",
"============================================================\n",
"\n",
"% par cluster global (chaque ligne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 91.1 1.1 7.8\n",
"Global C1 48.7 10.7 40.5\n",
"Global C2 86.5 6.2 7.3\n",
"Global C3 93.2 0.9 5.9\n",
"\n",
"% par cluster asset (chaque colonne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 41.0 11.0 23.5\n",
"Global C1 9.5 47.9 52.7\n",
"Global C2 21.2 34.6 12.0\n",
"Global C3 28.2 6.5 11.8\n",
"\n",
"============================================================\n",
"Global x Diversified\n",
"============================================================\n",
"\n",
"% par cluster global (chaque ligne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 64.2 33.9 1.8\n",
"Global C1 21.1 61.7 17.2\n",
"Global C2 31.6 40.4 27.9\n",
"Global C3 40.9 54.0 5.1\n",
"\n",
"% par cluster asset (chaque colonne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 54.4 28.5 6.6\n",
"Global C1 7.8 22.5 26.7\n",
"Global C2 14.6 18.5 54.4\n",
"Global C3 23.3 30.5 12.3\n",
"\n",
"============================================================\n",
"Global x Equity\n",
"============================================================\n",
"\n",
"% par cluster global (chaque ligne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1 Asset C2\n",
"Global C0 70.6 25.9 3.2 0.2\n",
"Global C1 18.7 23.1 9.7 48.6\n",
"Global C2 37.4 32.8 29.5 0.3\n",
"Global C3 44.2 49.1 6.8 0.0\n",
"\n",
"% par cluster asset (chaque colonne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1 Asset C2\n",
"Global C0 54.8 29.9 11.6 0.9\n",
"Global C1 6.3 11.5 15.0 98.3\n",
"Global C2 15.8 20.6 57.2 0.9\n",
"Global C3 23.1 38.0 16.2 0.0\n",
"\n",
"============================================================\n",
"Global x Fixed Income\n",
"============================================================\n",
"\n",
"% par cluster global (chaque ligne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 34.5 61.5 4.0\n",
"Global C1 19.3 52.9 27.9\n",
"Global C2 65.4 21.2 13.3\n",
"Global C3 72.0 24.1 3.9\n",
"\n",
"% par cluster asset (chaque colonne somme à 100%) :\n",
" Non exposé Asset C0 Asset C1\n",
"Global C0 27.2 54.8 15.5\n",
"Global C1 6.6 20.4 46.4\n",
"Global C2 28.1 10.3 28.0\n",
"Global C3 38.2 14.4 10.1\n",
"\n",
"============================================================\n",
"Adjusted Rand Index — cohérence global x asset\n",
"============================================================\n",
"(1 = identiques, 0 = aléatoire, <0 = pire qu'aléatoire)\n",
"\n",
" Alternative : ARI=0.0278 (n=1166 comptes communs)\n",
" Diversified : ARI=0.0347 (n=3980 comptes communs)\n",
" Equity : ARI=0.1587 (n=3691 comptes communs)\n",
" Fixed Income : ARI=0.1106 (n=3743 comptes communs)\n",
"\n",
"============================================================\n",
"Exposition multi-asset par cluster global\n",
"============================================================\n",
"\n",
"Nombre moyen d'asset types par cluster global :\n",
"cluster_k4\n",
"0 1.40\n",
"1 2.92\n",
"2 1.79\n",
"3 1.50\n",
"Name: n_asset_types, dtype: float64\n",
"\n",
"Distribution du nombre d'asset types par cluster global :\n",
" 0 asset(s) 1 asset(s) 2 asset(s) 3 asset(s) 4 asset(s)\n",
"Global C0 0.4 73.9 14.8 7.3 3.5\n",
"Global C1 0.8 17.5 13.6 25.0 43.1\n",
"Global C2 0.0 49.2 29.3 14.8 6.7\n",
"Global C3 0.0 64.7 23.3 9.6 2.4\n"
]
}
],
"source": [
"# ============================================================\n",
"# CROISEMENT — clustering global x clustering par asset type\n",
"# ============================================================\n",
"\n",
"# ── 1. Merge des labels asset dans dfc ────────────────────────────────────\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(\"Colonnes disponibles :\", dfc_cross.columns.tolist())\n",
"print(\"Shape :\", dfc_cross.shape)\n",
"\n",
"# ── 2. Tables de contingence global x asset ───────────────────────────────\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",
"\n",
" if cluster_col not in dfc_cross.columns:\n",
" continue\n",
"\n",
" # Table de contingence normalisée par ligne (% par cluster global)\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",
"\n",
" # Renommer les colonnes\n",
" col_names = {\n",
" c: f\"Asset C{c}\" if c >= 0 else \"Non exposé\"\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",
"\n",
" sns.heatmap(\n",
" ct,\n",
" cmap=\"Blues\",\n",
" annot=True,\n",
" fmt=\".1f\",\n",
" ax=axes[i],\n",
" cbar_kws={\"label\": \"%\"},\n",
" vmin=0,\n",
" vmax=100,\n",
" )\n",
" axes[i].set_title(f\"Global x {asset} (% par cluster global)\")\n",
" axes[i].set_xlabel(f\"Cluster {asset}\")\n",
" axes[i].set_ylabel(\"Cluster Global\")\n",
"\n",
"plt.suptitle(\"Croisement Clustering Global x Clustering par Asset Type\",\n",
" fontsize=14, y=1.02)\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"# ── 3. Tables de contingence détaillées ───────────────────────────────────\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",
"\n",
" print(f\"\\n{'='*60}\")\n",
" print(f\"Global x {asset}\")\n",
" print(f\"{'='*60}\")\n",
"\n",
" # % par cluster global\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 \"Non exposé\"\n",
" for c in ct_row.columns]\n",
" print(\"\\n% par cluster global (chaque ligne somme à 100%) :\")\n",
" print(ct_row.to_string())\n",
"\n",
" # % par cluster asset\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 \"Non exposé\"\n",
" for c in ct_col.columns]\n",
" print(\"\\n% par cluster asset (chaque colonne somme à 100%) :\")\n",
" print(ct_col.to_string())\n",
"\n",
"# ── 4. Indice de Rand Ajusté — mesure de cohérence ────────────────────────\n",
"from sklearn.metrics import adjusted_rand_score\n",
"\n",
"print(\"\\n\" + \"=\"*60)\n",
"print(\"Adjusted Rand Index — cohérence global x asset\")\n",
"print(\"=\"*60)\n",
"print(\"(1 = identiques, 0 = aléatoire, <0 = pire qu'aléatoire)\\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",
"\n",
" # Garder seulement les comptes présents dans les deux clusterings\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",
"\n",
" ari = adjusted_rand_score(labels_global, labels_asset)\n",
" n_common = mask.sum()\n",
" print(f\" {asset:20s} : ARI={ari:.4f} (n={n_common} comptes communs)\")\n",
"\n",
"# ── 5. Profil des comptes présents dans plusieurs asset types ─────────────\n",
"print(\"\\n\" + \"=\"*60)\n",
"print(\"Exposition multi-asset par cluster global\")\n",
"print(\"=\"*60)\n",
"\n",
"# Compter le nombre d'asset types par compte\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(\"\\nNombre moyen d'asset types par cluster global :\")\n",
"print(dfc_cross.groupby(\"cluster_k4\")[\"n_asset_types\"].mean().round(2))\n",
"\n",
"print(\"\\nDistribution du nombre d'asset types par cluster global :\")\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(s)\" for c in ct_multi.columns]\n",
"print(ct_multi.to_string())"
]
},
{
"cell_type": "code",
2026-04-07 20:26:19 +02:00
"execution_count": 208,
2026-04-05 17:52:42 +02:00
"id": "b78c0c1c-86ff-4b65-b592-57caf37c15a5",
"metadata": {},
2026-04-07 20:26:19 +02:00
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== Fonds disponibles (top 20 par AUM) ===\n",
"Product - Fund\n",
"Carmignac Patrimoine 1.838372e+12\n",
"Carmignac Sécurité 1.053010e+12\n",
"Carmignac Investissement 5.300035e+11\n",
"Carmignac Portfolio Sécurité 2.562135e+11\n",
"Carmignac Portfolio Flexible Bond 2.223185e+11\n",
"Carmignac Portfolio Patrimoine 2.035761e+11\n",
"Carmignac Emergents 1.218833e+11\n",
"Carmignac Portfolio Global Bond 1.119945e+11\n",
"Carmignac Portfolio Credit 8.413277e+10\n",
"Carmignac Court Terme 8.224046e+10\n",
"Carmignac Portfolio Emerging Patrimoine 7.216283e+10\n",
"Carmignac Portfolio Grande Europe 6.404670e+10\n",
"Carmignac Portfolio Long-Short European Equities 6.162365e+10\n",
"Carmignac Portfolio Climate Transition 5.125412e+10\n",
"Carmignac Absolute Return Europe 4.100414e+10\n",
"Carmignac Credit 2027 3.937522e+10\n",
"Carmignac Portfolio Patrimoine Europe 3.880186e+10\n",
"Carmignac Portfolio Investissement 3.643094e+10\n",
"Carmignac Investissement Latitude 3.474053e+10\n",
"Carmignac Portfolio Emergents 2.942745e+10\n",
"\n",
"Fonds retenus (15) :\n",
" Carmignac Patrimoine : 6185 clients, AUM=1838371784989\n",
" Carmignac Sécurité : 2827 clients, AUM=1053009711661\n",
" Carmignac Investissement : 4984 clients, AUM=530003536916\n",
" Carmignac Portfolio Sécurité : 1266 clients, AUM=256213510896\n",
" Carmignac Portfolio Flexible Bond : 1433 clients, AUM=222318524868\n",
" Carmignac Portfolio Patrimoine : 1236 clients, AUM=203576125394\n",
" Carmignac Emergents : 4233 clients, AUM=121883253216\n",
" Carmignac Portfolio Global Bond : 2116 clients, AUM=111994458004\n",
" Carmignac Portfolio Credit : 1137 clients, AUM=84132771053\n",
" Carmignac Court Terme : 1333 clients, AUM=82240456223\n",
" Carmignac Portfolio Emerging Patrimoine : 1675 clients, AUM=72162829525\n",
" Carmignac Portfolio Grande Europe : 2952 clients, AUM=64046699189\n",
" Carmignac Portfolio Long-Short European Equities : 691 clients, AUM=61623651990\n",
" Carmignac Portfolio Climate Transition : 3099 clients, AUM=51254115016\n",
" Carmignac Absolute Return Europe : 1990 clients, AUM=41004139345\n",
"\n",
"df_month_fund shape: (3873987, 19)\n",
"df_client_fund shape: (20740, 23)\n",
"\n",
"Comptes par fund :\n",
"Product - Fund\n",
"Carmignac Patrimoine 3155\n",
"Carmignac Investissement 2194\n",
"Carmignac Emergents 1781\n",
"Carmignac Portfolio Global Bond 1718\n",
"Carmignac Sécurité 1624\n",
"Carmignac Portfolio Grande Europe 1388\n",
"Carmignac Portfolio Climate Transition 1280\n",
"Carmignac Portfolio Sécurité 1163\n",
"Carmignac Portfolio Patrimoine 1145\n",
"Carmignac Portfolio Emerging Patrimoine 1137\n",
"Carmignac Portfolio Flexible Bond 1089\n",
"Carmignac Portfolio Credit 1018\n",
"Carmignac Absolute Return Europe 914\n",
"Carmignac Portfolio Long-Short European Equities 607\n",
"Carmignac Court Terme 527\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.5061 0.8297 656\n",
" 3 0.4952 0.8445 571\n",
" 4 0.3581 1.2279 337\n",
" 5 0.3858 1.1136 276\n",
" 6 0.3209 1.1835 271\n",
"→ K retenu : 2 (silhouette=0.5061)\n",
" n_comptes pct\n",
"cluster_carmignac_patrimoine \n",
"0 2499 79.2\n",
"1 656 20.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",
"Médianes — 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.038 2.322 0.65 -0.879 4.746 83.0 0.000 1.000 0.0\n",
"1 0.085 0.127 1.00 -1.000 4.749 12.0 0.907 0.093 0.0\n",
"\n",
"============================================================\n",
"FUND : Carmignac Sécurité\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.5716 0.8488 309\n",
" 3 0.5540 0.8263 192\n",
" 4 0.4157 0.9818 96\n",
" 5 0.3493 1.1866 84\n",
" 6 0.2712 1.1710 81\n",
"→ K retenu : 2 (silhouette=0.5716)\n",
" n_comptes pct\n",
"cluster_carmignac_sécurité \n",
"0 309 19.0\n",
"1 1315 81.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",
"Médianes — 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.183 12.0 0.916 0.084 0.0\n",
"1 0.071 4.444 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.4310 1.0214 381\n",
" 3 0.4221 0.9168 363\n",
" 4 0.2891 1.2951 314\n",
" 5 0.3039 1.2235 216\n",
" 6 0.2898 1.2247 209\n",
"→ K retenu : 2 (silhouette=0.4310)\n",
" n_comptes pct\n",
"cluster_carmignac_investissement \n",
"0 381 17.4\n",
"1 1813 82.6\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",
"Médianes — 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.531 1.860 1.352 -0.468 7.592 0.0 0.123 0.877 0.029\n",
"1 0.025 1.067 0.738 -1.000 3.244 92.0 0.000 1.000 0.000\n",
"\n",
"============================================================\n",
"FUND : Carmignac Portfolio Sécurité\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.7984 0.3113 180\n",
" 3 0.6484 0.4722 180\n",
" 4 0.6613 0.5310 61\n",
" 5 0.7217 0.4865 61\n",
" 6 0.7004 0.6374 44\n",
"→ K retenu : 2 (silhouette=0.7984)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_sécurité \n",
"0 983 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",
"Médianes — 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.229 0.419 0.000 6.053 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.6092 0.7548 121\n",
" 3 0.3969 1.1736 75\n",
" 4 0.4146 0.9109 34\n",
" 5 0.3019 1.1830 33\n",
" 6 0.2697 1.1367 31\n",
"→ K retenu : 2 (silhouette=0.6092)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_flexible_b \n",
"0 968 88.9\n",
"1 121 11.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",
"Médianes — 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.924 0.568 -0.155 3.991 81.0 0.000 1.000 0.000\n",
"1 0.738 6.069 1.662 0.121 7.714 0.0 0.683 0.317 0.085\n",
"\n",
"============================================================\n",
"FUND : Carmignac Portfolio Patrimoine\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.5967 0.5513 251\n",
" 3 0.6520 0.5031 170\n",
" 4 0.6269 0.5990 99\n",
" 5 0.6057 0.7167 74\n",
" 6 0.5876 0.7718 66\n",
"→ K retenu : 3 (silhouette=0.6520)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_patrimoine \n",
"0 238 20.8\n",
"1 737 64.4\n",
"2 170 14.8\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABM8AAAGGCAYAAABseKbNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XlczPkfB/DXdFFKVERFclSOkiRnWrHOde86c+W+2XVUbG5h3RK5crMshRWy1rXkvpIc684RFd33fH9/9GvW6DBNk6nxej4e3wd9v5/v5/v+zmdmmnn3OUSCIAgooBkzZuDjx49YuXIlHB0dcfjwYairq2Ps2LFwcHDAjBkzClolERERERERERFRsSOSJ3kWHx+PCRMm4O7du0hMTETFihURFRUFOzs7bNiwATo6OkURKxERERERERER0VclV/Is27Vr1/DgwQMkJSWhbt26aNasmSJjIyIiIiIiIiIiUqpCJc+IiIiIiIiIiIhUmYa8J4aEhCAkJATR0dEQi8VSx7y9vQsdGBERERERERERkbLJlTzz8fHB2rVrUa9ePVSoUAEikUjRcRERERERERERESmdXMM2W7RogSlTpqBbt25FEBIREREREREREVHxoCbPSenp6bC3t1d0LERERERERERERMWKXMmzH3/8EUeOHFF0LERERERERERERMWKXHOepaamYt++fQgJCYGVlRU0NKSr8fDwUEhwREREREREREREyiRXz7MHDx7A2toaIpEIDx8+xL179yRbeHi4omMkIlJJLi4ucHd3V3YYX9XBgwdhZWWFiIgIZYdCKmTNmjWwsrJSdhgq486dO+jTpw/s7OxgZWVVoM92ub3GBwwYgAEDBhRFqEqjzPdvZf/uCAoKgqOjIxITE5UWQ36WLl2Kn376SdlhEBGRipGr59mOHTsUHQcRkcp48eIFNm3ahAsXLuDdu3fQ1NSEpaUlOnTogN69e6N06dJFHkNycjI2bdoER0dHNG7cuMivV1IdOXIE0dHRGDx4sLJDKXZSU1OxZ88eHD16FE+ePEFaWhpMTEzQvHlzDBgwABYWFsoOUeUcPHhQqve+lpaW5DEfM2YMjIyMFHat9evXo2bNmmjTpo3U/vT0dEyaNAlaWlrw8PBA6dKlYWJiorDrKsKAAQNw5coVyc/6+vqoUqUK+vbtix49ekBNTfa/Df/77784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
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.614 0.460 0.0 5.617 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 Emergents\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.4352 1.1739 509\n",
" 3 0.4730 0.8737 290\n",
" 4 0.4057 1.0207 140\n",
" 5 0.2767 1.3023 136\n",
" 6 0.2953 1.2352 136\n",
"→ K retenu : 3 (silhouette=0.4730)\n",
" n_comptes pct\n",
"cluster_carmignac_emergents \n",
"0 347 19.5\n",
"1 1144 64.2\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",
"Médianes — 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.027 1.000 -0.926 3.372 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 Global Bond\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.7574 0.3756 294\n",
" 3 0.8455 0.2185 202\n",
" 4 0.8210 0.3162 97\n",
" 5 0.8302 0.3349 94\n",
" 6 0.8284 0.3586 60\n",
"→ K retenu : 3 (silhouette=0.8455)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_global_bon \n",
"0 1246 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",
"Médianes — 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.375 0.000 4.430 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.7006 0.5926 107\n",
" 3 0.5971 0.6643 36\n",
" 4 0.4844 0.7914 17\n",
" 5 0.2672 1.1071 17\n",
" 6 0.2958 1.0764 17\n",
"→ K retenu : 2 (silhouette=0.7006)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_credit \n",
"0 107 10.5\n",
"1 911 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",
"Médianes — 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.483 0.684 0.098 5.963 25.0 0.0 1.0 0.000\n",
"\n",
"============================================================\n",
"FUND : Carmignac Court Terme\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.4719 0.9125 113\n",
" 3 0.4108 0.8594 109\n",
" 4 0.3615 1.1596 108\n",
" 5 0.3464 1.2502 57\n",
" 6 0.3987 1.0217 35\n",
"→ K retenu : 2 (silhouette=0.4719)\n",
" n_comptes pct\n",
"cluster_carmignac_court_terme \n",
"0 414 78.6\n",
"1 113 21.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",
"Médianes — 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.457 0.335 -0.023 1.945 87.0 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 Emerging Patrimoine\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.5594 1.0477 252\n",
" 3 0.6004 0.7581 133\n",
" 4 0.4796 0.9787 67\n",
" 5 0.4237 1.1134 30\n",
" 6 0.4481 1.0397 35\n",
"→ K retenu : 3 (silhouette=0.6004)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_emerging_p \n",
"0 852 74.9\n",
"1 152 13.4\n",
"2 133 11.7\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABM8AAAGGCAYAAABseKbNAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdcE+cfB/BPWCIyZIkMQUWDC1yI4qy4N2qrrXsrbutErXWj1roXdW+0KjiKq1ardaB1VESk1o0iyN4z9/uDH9HIMIRAGJ/363Uvzd1zz30vl0vIN88QCYIgoJA0atQIJ0+eRJUqVQrrEERERERERERERIVGrTArL8S8HBERERERERERUaEr1OQZERERERERERFRScbkGRERERERERERUS6YPCMiIiIiIiIiIspFoSbPRCJRYVZPRERERERERERUqDhhABERERERERERUS5EQiFmuP7++284ODhAS0ursA5BRERERERERERUaBRKnnl4eORcmUiEcuXKwdraGu3atUPFihULGh8REREREREREZHKKJQ8Gzx4MB4/fgyJRIJq1aoBAF68eAF1dXVUr14dL168gEgkwqFDh1CjRg2lB01ERERERERERFQUFBrzrF27dmjevDmuXbuGEydO4MSJE7h69SqaN2+Obt264erVq3B0dMy1hRoRUWFycXHBnDlzVB1GkTpx4gTs7OwQHBys6lCoFNm4cSPs7OxUHUap8fDhQ3z77bdo0KAB7OzsEBgYKPe+Od3jgwcPxuDBgwsj1FKlrD9PdnZ22LhxY5k7NgBs374dnTt3hkQiUVkMeZk2bRqmTJmi6jCIiEgOGorstHPnTuzevRu6urrSdXp6epg0aRJGjBiBoUOHYsKECRgxYoTSAiUiev36NXbs2IHr168jLCwMmpqaEIvF6NKlC/r37w9tbe1CjyEpKQk7duyAk5MTmjZtWujHK6lOnz6NiIgIDBs2TNWhFDspKSk4fPgwfvvtNzx//hypqamwsLBAixYtMHjwYGmLblKeEydOwN3dXfpYS0tL+pyPHz8eJiYmSjvWtm3bUKNGDbRv315mfVpaGqZOnQotLS24u7tDW1sbFhYWSjuuMgwePBi3b9/OcVu1atVw7ty5Io6o5HNxccHbt2+lj42MjFCtWjUMHz4cHTp0yFdd9+7dw/Xr1zF06FDo6+srO9RSJz4+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
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.156 0.500 -0.613 5.213 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.3724 1.1306 232\n",
" 3 0.3474 1.1547 218\n",
" 4 0.2732 1.3479 165\n",
" 5 0.2529 1.3471 147\n",
" 6 0.2597 1.3180 118\n",
"→ K retenu : 2 (silhouette=0.3724)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_grande_eur \n",
"0 232 16.7\n",
"1 1156 83.3\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABNwAAAGGCAYAAACg4ZwmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdYE1nbBvA7AaIiRQEFBbGDBRAbKrYVWV17W7E3UBQFe8GyWFBR1y7YFXtva8Gy9oZl7SiWxYKFVQQBKUrJfH/4kZcYwBDBQLx/15VLczJz5pmcTEienCISBEFADjk5OUEkEmX5+KlTp3JaJRERERERERERkUbQVmWnfv36yd1PTU3FgwcPcPHiRbi5ueVKYERERERERERERAVRriTc0m3duhUhISHfFRAREREREREREVFBJs7Nypo0aYLjx4/nZpVEREREREREREQFSq4m3I4dO4ZixYrlZpVEREREREREREQFikpDSjt27Ci3aIIgCHj//j2io6MxderUXAuOiIiIiIiIiIiooFEp4ebs7Cx3XyQSwcjICA4ODqhYsWKuBEZERERERERERFQQiQRBENQdBBERERERERERkaZQeQ638PBwLFq0CKNHj0ZUVBQA4Ny5c3jy5EmuBUdERERERERERFTQqJRwu3btGtq1a4e7d+/ixIkTSExMBAA8evQIy5Yty9UAiYiIiIiIiIiIChKVEm4LFizAyJEjERgYCB0dHVl5/fr1cfv27dyKjYgo33JycoK3t7e6w/ih9u3bB2tra7x69UrdoZAGWbZsGaytrdUdhsa4e/cuunfvDnt7e1hbWyM0NFTpfTO7xvv06YM+ffrkRagFCl+nuS8hIQENGjTAwYMH1R1Kpv79919Uq1YNjx8/VncoRERUQKm0aMLjx48xf/58hXIjIyN8+PDhu4MiIlKX8PBwrF27FpcuXcK7d++go6MDKysrtGrVCt26dUPhwoXzPIakpCSsXbsWDg4OqFevXp4fr6A6dOgQoqKi0L9/f3WHku98/vwZ27dvx5EjR/D06VMkJyejdOnSaNiwIfr06YPy5curO0SNs2/fPkycOFF2XyKRyJ7zoUOHwsTEJNeOtXLlSlSqVElhEauUlBSMHDkSEokEEydOROHChVG6dOlcO25uOn36NHbt2oW7d+8iLi4Ourq6qFy5Mpo3bw4XFxfo6emp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
"text/plain": [
"<Figure size 1400x400 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Médianes — 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.000 -0.034 7.587 0.0 0.398 0.602 0.034\n",
"1 0.029 1.860 0.593 -0.524 4.094 81.0 0.000 1.000 0.000\n",
"\n",
"============================================================\n",
"FUND : Carmignac Portfolio Long-Short European Equities\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.3490 1.3508 188\n",
" 3 0.3819 1.0101 118\n",
" 4 0.3969 0.9225 54\n",
" 5 0.3524 1.0438 54\n",
" 6 0.3727 1.0432 46\n",
"→ K retenu : 4 (silhouette=0.3969)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_long-short \n",
"0 111 18.3\n",
"1 335 55.2\n",
"2 107 17.6\n",
"3 54 8.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",
"Médianes — 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.056 3.823 0.412 -1.000 4.991 29.0 0.0 1.0 0.0\n",
"1 0.114 6.651 0.635 0.000 6.755 32.0 0.0 1.0 0.0\n",
"2 0.125 1.688 1.000 0.537 7.833 3.0 1.0 0.0 0.0\n",
"3 0.041 4.727 0.348 1.000 5.110 56.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.6659 0.6413 156\n",
" 3 0.5623 0.7455 57\n",
" 4 0.3956 1.0145 37\n",
" 5 0.2261 1.2444 37\n",
" 6 0.2237 1.2368 18\n",
"→ K retenu : 2 (silhouette=0.6659)\n",
" n_comptes pct\n",
"cluster_carmignac_portfolio_climate_tr \n",
"0 156 12.2\n",
"1 1124 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",
"Médianes — 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.496 0.584 -0.792 4.221 92.0 0.0 1.0 0.000\n",
"\n",
"============================================================\n",
"FUND : Carmignac Absolute Return Europe\n",
"============================================================\n",
" k silhouette davies_bouldin min_cluster_size\n",
" 2 0.4593 0.9283 146\n",
" 3 0.5112 0.7321 122\n",
" 4 0.4436 0.9496 72\n",
" 5 0.4150 1.0153 42\n",
" 6 0.3870 1.0866 28\n",
"→ K retenu : 3 (silhouette=0.5112)\n",
" n_comptes pct\n",
"cluster_carmignac_absolute_return_euro \n",
"0 180 19.7\n",
"1 122 13.3\n",
"2 612 67.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",
"Médianes — Carmignac Absolute Return 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_absolute_return_euro \n",
"0 0.052 0.298 1.000 -0.882 4.301 16.5 0.907 0.093 0.000\n",
"1 0.532 5.635 0.973 -0.020 7.317 35.5 0.000 1.000 0.047\n",
"2 0.038 2.433 0.488 -0.539 4.221 81.0 0.000 1.000 0.000\n",
"\n",
"============================================================\n",
"RÉSUMÉ — Clustering par fund\n",
"============================================================\n",
" Carmignac Patrimoine : K=2, sil=0.5061, n=3155\n",
" Carmignac Sécurité : K=2, sil=0.5716, n=1624\n",
" Carmignac Investissement : K=2, sil=0.4310, n=2194\n",
" Carmignac Portfolio Sécurité : K=2, sil=0.7984, n=1163\n",
" Carmignac Portfolio Flexible Bond : K=2, sil=0.6092, n=1089\n",
" Carmignac Portfolio Patrimoine : K=3, sil=0.6520, n=1145\n",
" Carmignac Emergents : K=3, sil=0.4730, n=1781\n",
" Carmignac Portfolio Global Bond : K=3, sil=0.8455, n=1718\n",
" Carmignac Portfolio Credit : K=2, sil=0.7006, n=1018\n",
" Carmignac Court Terme : K=2, sil=0.4719, n=527\n",
" Carmignac Portfolio Emerging Patrimoine : K=3, sil=0.6004, n=1137\n",
" Carmignac Portfolio Grande Europe : K=2, sil=0.3724, n=1388\n",
" Carmignac Portfolio Long-Short European : K=4, sil=0.3969, n=607\n",
" Carmignac Portfolio Climate Transition : K=2, sil=0.6659, n=1280\n",
" Carmignac Absolute Return Europe : K=3, sil=0.5112, n=914\n",
"\n",
"============================================================\n",
"ARI — cohérence clustering global x fund\n",
"============================================================\n",
" Carmignac Patrimoine : ARI=0.0241 (n=3002)\n",
" Carmignac Sécurité : ARI=0.0121 (n=1478)\n",
" Carmignac Investissement : ARI=0.0427 (n=2055)\n",
" Carmignac Portfolio Sécurité : ARI=0.0813 (n=1047)\n",
" Carmignac Portfolio Flexible Bond : ARI=-0.0456 (n=945)\n",
" Carmignac Portfolio Patrimoine : ARI=0.0116 (n=1030)\n",
" Carmignac Emergents : ARI=0.0158 (n=1641)\n",
" Carmignac Portfolio Global Bond : ARI=0.0797 (n=1585)\n",
" Carmignac Portfolio Credit : ARI=0.0087 (n=902)\n",
" Carmignac Court Terme : ARI=-0.0346 (n=424)\n",
" Carmignac Portfolio Emerging Patrimoine : ARI=-0.0337 (n=998)\n",
" Carmignac Portfolio Grande Europe : ARI=-0.0266 (n=1248)\n",
" Carmignac Portfolio Long-Short European : ARI=0.0524 (n=496)\n",
" Carmignac Portfolio Climate Transition : ARI=-0.0461 (n=1143)\n",
" Carmignac Absolute Return Europe : ARI=-0.0319 (n=788)\n"
]
}
],
"source": [
"# ============================================================\n",
"# CLUSTERING PAR FUND FAMILY (top fonds)\n",
"# ============================================================\n",
"\n",
"# ── 0. Vérification des fonds disponibles ─────────────────────────────────\n",
"print(\"=== Fonds disponibles (top 20 par 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",
"# ── 1. Sélection des top fonds ────────────────────────────────────────────\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\"\\nFonds retenus ({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",
"# ── 2. Construction du panel par compte x fund ────────────────────────────\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",
"# Panel mensuel 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",
"# ── 3. Feature engineering par compte x 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",
"# ── 5. Clustering par fund ────────────────────────────────────────────────\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",
" # Tailles\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})\",\n",
" figsize=(14, 4)\n",
" )\n",
" print(f\"\\nMédianes — {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",
"# ── 6. Résumé global ──────────────────────────────────────────────────────\n",
"print(\"\\n\" + \"=\"*60)\n",
"print(\"RÉSUMÉ — Clustering par fund\")\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 — cohérence clustering global x fund\")\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": "code",
"execution_count": null,
"id": "7bc432da-3be4-4e39-93f9-9131a3548a14",
"metadata": {},
2026-04-05 17:52:42 +02:00
"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
}