{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "96d83982",
   "metadata": {},
   "source": [
    "# 多准则决策分析 — TOPSIS & AHP\n",
    "\n",
    "对应案例: [MCDA](/docs/case-studies/strategy/mcda-case/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e0d4ef7",
   "metadata": {},
   "source": [
    "## 1. 决策矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fdaea991",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np, pandas as pd, matplotlib.pyplot as plt\n",
    "np.random.seed(42)\n",
    "alternatives=['Supplier A','Supplier B','Supplier C','Supplier D','Supplier E']\n",
    "criteria=['Cost','Quality','Delivery','Reliability']\n",
    "# Each row = alternative, each col = criterion\n",
    "matrix=np.array([\n",
    "    [80,85,90,70],\n",
    "    [65,75,85,80],\n",
    "    [90,90,70,85],\n",
    "    [75,80,80,75],\n",
    "    [85,70,95,65]\n",
    "])\n",
    "weights=np.array([0.3,0.3,0.2,0.2])\n",
    "df=pd.DataFrame(matrix,index=alternatives,columns=criteria)\n",
    "print('Decision Matrix:')\n",
    "print(df)\n",
    "print(f'\\nWeights: {dict(zip(criteria,weights))}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f3d922a",
   "metadata": {},
   "source": [
    "## 2. TOPSIS 计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f349ea98",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Normalize\n",
    "norm=matrix/np.sqrt((matrix**2).sum(axis=0))\n",
    "# Weighted\n",
    "weighted=norm*weights\n",
    "# Ideal best/worst (cost is min, others max)\n",
    "ideal_best=np.array([weighted[:,0].min(),weighted[:,1].max(),weighted[:,2].max(),weighted[:,3].max()])\n",
    "ideal_worst=np.array([weighted[:,0].max(),weighted[:,1].min(),weighted[:,2].min(),weighted[:,3].min()])\n",
    "d_best=np.sqrt(((weighted-ideal_best)**2).sum(axis=1))\n",
    "d_worst=np.sqrt(((weighted-ideal_worst)**2).sum(axis=1))\n",
    "scores=d_worst/(d_best+d_worst)\n",
    "df['TOPSIS']=scores\n",
    "rank=df['TOPSIS'].rank(ascending=False).astype(int)\n",
    "df['Rank']=rank\n",
    "print(df.sort_values('TOPSIS',ascending=False).round(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c202f083",
   "metadata": {},
   "source": [
    "## 3. 可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54a73c5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig,ax=plt.subplots(figsize=(10,6))\n",
    "colors=['#2196F3' if r>1 else '#4CAF50' for r in df['Rank']]\n",
    "ax.barh(df.index,df['TOPSIS'],color=colors)\n",
    "ax.set_xlabel('TOPSIS Score'); ax.set_title('MCDA: Supplier Ranking')\n",
    "for i,(v,r) in enumerate(zip(df['TOPSIS'],df['Rank'])):\n",
    "    ax.text(v+0.01,i,f'Rank {r}',va='center')\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "472e9fb8",
   "metadata": {},
   "source": [
    "## 4. AHP 权重计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9215386",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Pairwise comparison matrix (1-9 scale)\n",
    "pairwise=np.array([\n",
    "    [1,1/3,2,3],\n",
    "    [3,1,4,5],\n",
    "    [1/2,1/4,1,2],\n",
    "    [1/3,1/5,1/2,1]\n",
    "])\n",
    "eigvals,eigvecs=np.linalg.eig(pairwise)\n",
    "ahp_weights=np.real(eigvecs[:,0]/eigvecs[:,0].sum())\n",
    "ci=(max(eigvals.real)-4)/3\n",
    "ri=0.9; cr=ci/ri\n",
    "print(f'AHP weights: {dict(zip(criteria,ahp_weights.round(3)))}')\n",
    "print(f'CI={ci:.3f}, CR={cr:.3f} ({\"Consistent\" if cr<0.1 else \"Inconsistent\"})')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "005405c1",
   "metadata": {},
   "source": [
    "---\n",
    "*更多分析见 [案例文档](/docs/case-studies/strategy/mcda-case/)*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
