{"spec_id":"icicle-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nicicle-basic: Basic Icicle Chart\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_rect,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_fill_manual,\n    scale_size_identity,\n    theme,\n    xlim,\n    ylim,\n)\n\n\nLetsPlot.setup_html()\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Hierarchical data: File system example\nhierarchy = [\n    {\"name\": \"root\", \"parent\": \"\", \"value\": 1000},\n    {\"name\": \"Documents\", \"parent\": \"root\", \"value\": 350},\n    {\"name\": \"Media\", \"parent\": \"root\", \"value\": 400},\n    {\"name\": \"Projects\", \"parent\": \"root\", \"value\": 250},\n    {\"name\": \"Work\", \"parent\": \"Documents\", \"value\": 200},\n    {\"name\": \"Personal\", \"parent\": \"Documents\", \"value\": 150},\n    {\"name\": \"Photos\", \"parent\": \"Media\", \"value\": 220},\n    {\"name\": \"Videos\", \"parent\": \"Media\", \"value\": 180},\n    {\"name\": \"Python\", \"parent\": \"Projects\", \"value\": 120},\n    {\"name\": \"Web\", \"parent\": \"Projects\", \"value\": 130},\n    {\"name\": \"Reports\", \"parent\": \"Work\", \"value\": 120},\n    {\"name\": \"Contracts\", \"parent\": \"Work\", \"value\": 80},\n    {\"name\": \"Letters\", \"parent\": \"Personal\", \"value\": 90},\n    {\"name\": \"Receipts\", \"parent\": \"Personal\", \"value\": 60},\n    {\"name\": \"2024\", \"parent\": \"Photos\", \"value\": 130},\n    {\"name\": \"2023\", \"parent\": \"Photos\", \"value\": 90},\n    {\"name\": \"Movies\", \"parent\": \"Videos\", \"value\": 100},\n    {\"name\": \"Clips\", \"parent\": \"Videos\", \"value\": 80},\n    {\"name\": \"DataViz\", \"parent\": \"Python\", \"value\": 70},\n    {\"name\": \"ML\", \"parent\": \"Python\", \"value\": 50},\n    {\"name\": \"Frontend\", \"parent\": \"Web\", \"value\": 75},\n    {\"name\": \"Backend\", \"parent\": \"Web\", \"value\": 55},\n]\n\n# Build tree structure\nname_to_node = {row[\"name\"]: row for row in hierarchy}\nchildren = {}\nfor row in hierarchy:\n    parent = row[\"parent\"]\n    if parent not in children:\n        children[parent] = []\n    if parent:\n        children[parent].append(row[\"name\"])\n\n# Calculate level for each node\nlevels = {}\nfor row in hierarchy:\n    level = 0\n    current = row[\"name\"]\n    while name_to_node[current][\"parent\"]:\n        level += 1\n        current = name_to_node[current][\"parent\"]\n    levels[row[\"name\"]] = level\n\nmax_level = max(levels.values())\n\n# Calculate rectangle positions (horizontal icicle: root at top)\nrects = []\nstack = [(\"root\", 0.0, 1.0)]\n\nwhile stack:\n    name, x_start, x_end = stack.pop()\n    level = levels[name]\n\n    rects.append(\n        {\n            \"name\": name,\n            \"xmin\": x_start,\n            \"xmax\": x_end,\n            \"ymin\": max_level - level,\n            \"ymax\": max_level - level + 1,\n            \"level\": level,\n        }\n    )\n\n    if name in children and children[name]:\n        child_names = children[name]\n        total_value = sum(name_to_node[c][\"value\"] for c in child_names)\n        x_end_temp = x_end\n\n        for child_name in reversed(child_names):\n            child_value = name_to_node[child_name][\"value\"]\n            child_width = (x_end - x_start) * (child_value / total_value)\n            child_x_start = x_end_temp - child_width\n            stack.append((child_name, child_x_start, x_end_temp))\n            x_end_temp = child_x_start\n\n# Create dataframe for rectangles\nrect_df = pd.DataFrame(rects)\nrect_df[\"level_str\"] = rect_df[\"level\"].astype(str)\n\n# Calculate center positions and dimensions for labels\nrect_df[\"x_center\"] = (rect_df[\"xmin\"] + rect_df[\"xmax\"]) / 2\nrect_df[\"y_center\"] = (rect_df[\"ymin\"] + rect_df[\"ymax\"]) / 2\nrect_df[\"width\"] = rect_df[\"xmax\"] - rect_df[\"xmin\"]\n\n# Show labels for rectangles with sufficient width\nrect_df[\"label_len\"] = rect_df[\"name\"].str.len()\nrect_df[\"show_label\"] = rect_df[\"width\"] > (rect_df[\"label_len\"] * 0.007 + 0.01)\nlabel_df = rect_df[rect_df[\"show_label\"]].copy()\n\n# Adjust font size based on level\nlabel_df[\"font_size\"] = label_df[\"level\"].map({0: 14, 1: 12, 2: 8, 3: 7})\n\n# Color palette by level using Okabe-Ito\ncolors = {\n    \"0\": IMPRINT[0],  # Level 0: bluish green\n    \"1\": IMPRINT[1],  # Level 1: vermillion\n    \"2\": IMPRINT[2],  # Level 2: blue\n    \"3\": IMPRINT[3],  # Level 3: reddish purple\n}\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_grid=element_blank(),\n    axis_title=element_blank(),\n    axis_text=element_blank(),\n    axis_ticks=element_blank(),\n    axis_line=element_blank(),\n    plot_title=element_text(size=24, color=INK),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(size=16, color=INK_SOFT),\n    legend_title=element_text(size=18, color=INK),\n)\n\n# Create plot\nplot = (\n    ggplot()\n    + geom_rect(\n        aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\", fill=\"level_str\"),\n        data=rect_df,\n        color=INK_SOFT,\n        size=1.5,\n        alpha=0.9,\n    )\n    + geom_text(aes(x=\"x_center\", y=\"y_center\", label=\"name\", size=\"font_size\"), data=label_df, color=INK)\n    + scale_fill_manual(values=colors, name=\"Hierarchy Level\")\n    + scale_size_identity()\n    + xlim(-0.02, 1.02)\n    + ylim(-0.1, max_level + 1.1)\n    + labs(title=\"icicle-basic · letsplot · anyplot.ai\")\n    + anyplot_theme\n    + ggsize(1600, 900)\n)\n\n# Save outputs\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}