{"spec_id":"biplot-pca","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nbiplot-pca: PCA Biplot with Scores and Loading Vectors\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 83/100 | Updated: 2026-05-17\n\"\"\"\n\nimport matplotlib.pyplot as plt\nfrom sklearn.datasets import load_iris\nfrom sklearn.decomposition import PCA\nfrom sklearn.preprocessing import StandardScaler\n\n\n# Load and prepare data\niris = load_iris()\nX = iris.data\ny = iris.target\nfeature_names = [\"Sepal Length\", \"Sepal Width\", \"Petal Length\", \"Petal Width\"]\ntarget_names = iris.target_names\n\n# Standardize features\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X)\n\n# Perform PCA\npca = PCA(n_components=2)\nscores = pca.fit_transform(X_scaled)\nloadings = pca.components_.T  # Transpose to get (n_features, n_components)\nexplained_variance = pca.explained_variance_ratio_ * 100\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9))\n\n# Define colors for each class\ncolors = [\"#306998\", \"#FFD43B\", \"#E74C3C\"]\n\n# Plot observation scores (points) by group\nfor i, (target, color) in enumerate(zip(target_names, colors, strict=True)):\n    mask = y == i\n    ax.scatter(\n        scores[mask, 0],\n        scores[mask, 1],\n        c=color,\n        s=150,\n        alpha=0.7,\n        label=target.capitalize(),\n        edgecolors=\"white\",\n        linewidth=0.5,\n    )\n\n# Scale loadings for visibility - use smaller scale to keep arrows within plot\nscale_factor = 2.5\n\n# Plot loading arrows and labels\narrow_color = \"#2C3E50\"\nfor i, feature in enumerate(feature_names):\n    x_arrow = loadings[i, 0] * scale_factor\n    y_arrow = loadings[i, 1] * scale_factor\n\n    ax.annotate(\n        \"\",\n        xy=(x_arrow, y_arrow),\n        xytext=(0, 0),\n        arrowprops={\"arrowstyle\": \"-|>\", \"color\": arrow_color, \"lw\": 2.5, \"mutation_scale\": 15},\n    )\n\n    # Position text with custom offsets to avoid overlap\n    # Manual adjustments for Iris dataset features\n    offsets = {\n        \"Sepal Length\": (0.3, 0.25),\n        \"Sepal Width\": (-0.1, 0.3),\n        \"Petal Length\": (0.35, -0.25),\n        \"Petal Width\": (0.35, 0.3),\n    }\n    dx, dy = offsets.get(feature, (0.3, 0.3))\n    text_x = x_arrow + dx\n    text_y = y_arrow + dy\n\n    # Adjust alignment based on position\n    ha = \"left\" if x_arrow >= 0 else \"right\"\n    va = \"bottom\" if y_arrow >= 0 else \"top\"\n\n    ax.text(text_x, text_y, feature, fontsize=14, ha=ha, va=va, fontweight=\"bold\", color=arrow_color)\n\n# Draw reference lines at origin\nax.axhline(y=0, color=\"gray\", linestyle=\"--\", linewidth=1, alpha=0.5)\nax.axvline(x=0, color=\"gray\", linestyle=\"--\", linewidth=1, alpha=0.5)\n\n# Labels and title\nax.set_xlabel(f\"PC1 ({explained_variance[0]:.1f}%)\", fontsize=20)\nax.set_ylabel(f\"PC2 ({explained_variance[1]:.1f}%)\", fontsize=20)\nax.set_title(\"biplot-pca · matplotlib · pyplots.ai\", fontsize=24)\n\n# Styling\nax.tick_params(axis=\"both\", labelsize=16)\nax.legend(fontsize=16, loc=\"upper right\", framealpha=0.9)\nax.grid(True, alpha=0.3, linestyle=\"--\")\n\n# Set axis limits with some padding\nax.set_xlim(-3.5, 4)\nax.set_ylim(-3, 3)\n\nplt.tight_layout()\nplt.savefig(\"plot.png\", dpi=300, bbox_inches=\"tight\")\n"}