{"spec_id":"chernoff-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nchernoff-basic: Chernoff Faces for Multivariate Data\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-15\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_fixed,\n    element_rect,\n    element_text,\n    facet_wrap,\n    geom_path,\n    geom_point,\n    geom_polygon,\n    ggplot,\n    labs,\n    scale_fill_manual,\n    theme,\n    theme_void,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\nnp.random.seed(42)\n\n# Data: Car performance metrics with more extreme outliers\ncar_data = {\n    \"observation_id\": [\"Compact A\", \"Compact B\", \"Sedan A\", \"Sedan B\", \"SUV A\", \"SUV B\"],\n    \"category\": [\"Compact\", \"Compact\", \"Sedan\", \"Sedan\", \"SUV\", \"SUV\"],\n    \"engine_power\": [100, 150, 180, 220, 280, 320],\n    \"fuel_efficiency\": [38, 30, 26, 22, 18, 15],\n    \"safety_rating\": [3.9, 4.5, 4.8, 4.2, 4.6, 4.9],\n    \"comfort_score\": [3.0, 3.6, 4.5, 4.1, 4.0, 4.4],\n}\nsample_df = pd.DataFrame(car_data)\n\n# Normalize data to 0-1 range for facial feature mapping\nnormalized = sample_df[[\"engine_power\", \"fuel_efficiency\", \"safety_rating\", \"comfort_score\"]].apply(\n    lambda col: (col - col.min()) / (col.max() - col.min() + 1e-10)\n)\n\nface_widths = 0.6 + normalized[\"engine_power\"] * 0.4\nface_heights = 0.8 + normalized[\"fuel_efficiency\"] * 0.4\neye_sizes = 0.08 + normalized[\"safety_rating\"] * 0.1\nmouth_curvatures = -0.3 + normalized[\"comfort_score\"] * 0.6\n\n# Build data for all faces\nall_data = []\n\nfor idx in range(len(sample_df)):\n    obs_id = sample_df[\"observation_id\"].iloc[idx]\n    category = sample_df[\"category\"].iloc[idx]\n\n    fw = face_widths.iloc[idx]\n    fh = face_heights.iloc[idx]\n    es = eye_sizes.iloc[idx]\n    mc = mouth_curvatures.iloc[idx]\n\n    # Face outline\n    theta = np.linspace(0, 2 * np.pi, 50)\n    fx = fw * np.cos(theta)\n    fy = fh * np.sin(theta)\n    for i in range(len(fx)):\n        all_data.append({\"observation_id\": obs_id, \"category\": category, \"part\": \"face\", \"x\": fx[i], \"y\": fy[i]})\n\n    # Left eye\n    theta = np.linspace(0, 2 * np.pi, 20)\n    ex = -fw * 0.35 + es * np.cos(theta)\n    ey = fh * 0.25 + es * np.sin(theta)\n    for i in range(len(ex)):\n        all_data.append({\"observation_id\": obs_id, \"category\": category, \"part\": \"left_eye\", \"x\": ex[i], \"y\": ey[i]})\n\n    # Right eye\n    ex = fw * 0.35 + es * np.cos(theta)\n    ey = fh * 0.25 + es * np.sin(theta)\n    for i in range(len(ex)):\n        all_data.append({\"observation_id\": obs_id, \"category\": category, \"part\": \"right_eye\", \"x\": ex[i], \"y\": ey[i]})\n\n    # Left pupil (larger for visibility)\n    all_data.append(\n        {\"observation_id\": obs_id, \"category\": category, \"part\": \"left_pupil\", \"x\": -fw * 0.35, \"y\": fh * 0.25}\n    )\n\n    # Right pupil (larger for visibility)\n    all_data.append(\n        {\"observation_id\": obs_id, \"category\": category, \"part\": \"right_pupil\", \"x\": fw * 0.35, \"y\": fh * 0.25}\n    )\n\n    # Mouth\n    x_mouth = np.linspace(-fw * 0.25, fw * 0.25, 20)\n    y_mouth = -fh * 0.35 + mc * (((x_mouth) / (fw * 0.25)) ** 2 - 1)\n    for i in range(len(x_mouth)):\n        all_data.append(\n            {\"observation_id\": obs_id, \"category\": category, \"part\": \"mouth\", \"x\": x_mouth[i], \"y\": y_mouth[i]}\n        )\n\n    # Nose\n    all_data.append({\"observation_id\": obs_id, \"category\": category, \"part\": \"nose\", \"x\": 0, \"y\": fh * 0.1})\n    all_data.append({\"observation_id\": obs_id, \"category\": category, \"part\": \"nose\", \"x\": 0, \"y\": -fh * 0.1})\n\n    # Left eyebrow\n    x_brow = np.linspace(-fw * 0.35 - es, -fw * 0.35 + es, 10)\n    y_brow = fh * 0.45 + 0.05 * (x_brow + fw * 0.35) / es\n    for i in range(len(x_brow)):\n        all_data.append(\n            {\"observation_id\": obs_id, \"category\": category, \"part\": \"left_eyebrow\", \"x\": x_brow[i], \"y\": y_brow[i]}\n        )\n\n    # Right eyebrow\n    x_brow = np.linspace(fw * 0.35 - es, fw * 0.35 + es, 10)\n    y_brow = fh * 0.45 - 0.05 * (x_brow - fw * 0.35) / es\n    for i in range(len(x_brow)):\n        all_data.append(\n            {\"observation_id\": obs_id, \"category\": category, \"part\": \"right_eyebrow\", \"x\": x_brow[i], \"y\": y_brow[i]}\n        )\n\nplot_df = pd.DataFrame(all_data)\n\n# Okabe-Ito palette\ncategory_colors = {\"Compact\": \"#009E73\", \"Sedan\": \"#C475FD\", \"SUV\": \"#4467A3\"}\n\nanyplot_theme = theme(\n    figure_size=(16, 9),\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    plot_title=element_text(size=24, weight=\"bold\", color=INK, ha=\"center\"),\n    plot_subtitle=element_text(size=16, color=INK_SOFT, ha=\"center\"),\n    legend_position=\"bottom\",\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_title=element_text(size=16, color=INK),\n    legend_text=element_text(size=14, color=INK_SOFT),\n    strip_text=element_text(size=14, color=INK, weight=\"bold\"),\n)\n\nplot = (\n    ggplot()\n    # Face outline (filled polygon)\n    + geom_polygon(\n        data=plot_df[plot_df[\"part\"] == \"face\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\", fill=\"category\"),\n        color=INK_SOFT,\n        size=1.5,\n    )\n    # Eyes (white/elevated filled)\n    + geom_polygon(\n        data=plot_df[plot_df[\"part\"] == \"left_eye\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        fill=ELEVATED_BG,\n        color=INK_SOFT,\n        size=0.8,\n    )\n    + geom_polygon(\n        data=plot_df[plot_df[\"part\"] == \"right_eye\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        fill=ELEVATED_BG,\n        color=INK_SOFT,\n        size=0.8,\n    )\n    # Pupils (larger for visibility)\n    + geom_point(data=plot_df[plot_df[\"part\"] == \"left_pupil\"], mapping=aes(x=\"x\", y=\"y\"), color=INK_SOFT, size=5)\n    + geom_point(data=plot_df[plot_df[\"part\"] == \"right_pupil\"], mapping=aes(x=\"x\", y=\"y\"), color=INK_SOFT, size=5)\n    # Mouth\n    + geom_path(\n        data=plot_df[plot_df[\"part\"] == \"mouth\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        color=INK_SOFT,\n        size=1.2,\n    )\n    # Nose\n    + geom_path(\n        data=plot_df[plot_df[\"part\"] == \"nose\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        color=INK_SOFT,\n        size=1,\n    )\n    # Eyebrows\n    + geom_path(\n        data=plot_df[plot_df[\"part\"] == \"left_eyebrow\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        color=INK_SOFT,\n        size=1.2,\n    )\n    + geom_path(\n        data=plot_df[plot_df[\"part\"] == \"right_eyebrow\"],\n        mapping=aes(x=\"x\", y=\"y\", group=\"observation_id\"),\n        color=INK_SOFT,\n        size=1.2,\n    )\n    # Facet by observation\n    + facet_wrap(\"~observation_id\", ncol=3)\n    # Colors (Okabe-Ito palette)\n    + scale_fill_manual(values=category_colors)\n    # Labels\n    + labs(\n        title=\"chernoff-basic · plotnine · anyplot.ai\",\n        subtitle=\"Car Performance: Power/Efficiency/Safety/Comfort mapped to facial features\",\n        fill=\"Category\",\n    )\n    # Theme\n    + theme_void()\n    + anyplot_theme\n    + coord_fixed(ratio=1)\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=300)\n"}