{"spec_id":"icicle-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nicicle-basic: Basic Icicle Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prioritize site-packages for module resolution to avoid shadowing\nsite_packages = [p for p in sys.path if \"site-packages\" in p]\nfor p in site_packages:\n    if p in sys.path:\n        sys.path.remove(p)\n    sys.path.insert(0, p)\n\nimport altair as alt\nimport pandas as pd\n\n\n# Theme tokens\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\n# Okabe-Ito palette for levels\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data: File system hierarchy with sizes (in MB)\ndata = [\n    # Root\n    {\"name\": \"root\", \"parent\": None, \"value\": 0},\n    # Level 1: Main folders\n    {\"name\": \"Documents\", \"parent\": \"root\", \"value\": 0},\n    {\"name\": \"Media\", \"parent\": \"root\", \"value\": 0},\n    {\"name\": \"Projects\", \"parent\": \"root\", \"value\": 0},\n    # Level 2: Documents subfolders\n    {\"name\": \"Reports\", \"parent\": \"Documents\", \"value\": 0},\n    {\"name\": \"Presentations\", \"parent\": \"Documents\", \"value\": 0},\n    # Level 2: Media subfolders\n    {\"name\": \"Images\", \"parent\": \"Media\", \"value\": 0},\n    {\"name\": \"Videos\", \"parent\": \"Media\", \"value\": 0},\n    # Level 2: Projects subfolders\n    {\"name\": \"WebApp\", \"parent\": \"Projects\", \"value\": 0},\n    {\"name\": \"DataScience\", \"parent\": \"Projects\", \"value\": 0},\n    # Level 3: Leaf nodes with sizes balanced for better visual representation\n    {\"name\": \"Q1_Report.pdf\", \"parent\": \"Reports\", \"value\": 120},\n    {\"name\": \"Q2_Report.pdf\", \"parent\": \"Reports\", \"value\": 95},\n    {\"name\": \"Annual_Review.pdf\", \"parent\": \"Reports\", \"value\": 150},\n    {\"name\": \"Sales_Deck.pptx\", \"parent\": \"Presentations\", \"value\": 85},\n    {\"name\": \"Strategy.pptx\", \"parent\": \"Presentations\", \"value\": 110},\n    {\"name\": \"photo_album.jpg\", \"parent\": \"Images\", \"value\": 180},\n    {\"name\": \"banner.png\", \"parent\": \"Images\", \"value\": 75},\n    {\"name\": \"tutorial.mp4\", \"parent\": \"Videos\", \"value\": 350},\n    {\"name\": \"demo.mp4\", \"parent\": \"Videos\", \"value\": 280},\n    {\"name\": \"frontend.js\", \"parent\": \"WebApp\", \"value\": 65},\n    {\"name\": \"backend.py\", \"parent\": \"WebApp\", \"value\": 120},\n    {\"name\": \"styles.css\", \"parent\": \"WebApp\", \"value\": 45},\n    {\"name\": \"analysis.ipynb\", \"parent\": \"DataScience\", \"value\": 95},\n    {\"name\": \"model.pkl\", \"parent\": \"DataScience\", \"value\": 180},\n]\n\ndf = pd.DataFrame(data)\n\n# Build tree structure\nname_to_idx = {row[\"name\"]: i for i, row in enumerate(data)}\nchildren = {row[\"name\"]: [] for row in data}\nfor row in data:\n    if row[\"parent\"]:\n        children[row[\"parent\"]].append(row[\"name\"])\n\n# Calculate levels (depth) iteratively\nlevels = {\"root\": 0}\nqueue = [\"root\"]\nwhile queue:\n    current = queue.pop(0)\n    for child in children[current]:\n        levels[child] = levels[current] + 1\n        queue.append(child)\n\nfor row in data:\n    row[\"level\"] = levels[row[\"name\"]]\n\n# Calculate cumulative values bottom-up\nmax_level = max(levels.values())\nfor level in range(max_level, -1, -1):\n    for row in data:\n        if row[\"level\"] == level:\n            if children[row[\"name\"]]:\n                row[\"total_value\"] = sum(data[name_to_idx[c]][\"total_value\"] for c in children[row[\"name\"]])\n            else:\n                row[\"total_value\"] = row[\"value\"]\n\n# Calculate x positions iteratively\npositions = {\"root\": (0, 1)}\nqueue = [\"root\"]\nwhile queue:\n    current = queue.pop(0)\n    x_start, x_end = positions[current]\n    child_list = children[current]\n    if child_list:\n        total = sum(data[name_to_idx[c]][\"total_value\"] for c in child_list)\n        if total > 0:\n            current_x = x_start\n            for child in child_list:\n                child_val = data[name_to_idx[child]][\"total_value\"]\n                child_width = (x_end - x_start) * child_val / total\n                positions[child] = (current_x, current_x + child_width)\n                current_x += child_width\n                queue.append(child)\n\nfor row in data:\n    row[\"x_start\"], row[\"x_end\"] = positions[row[\"name\"]]\n\n# Prepare data for Altair rectangles\nrect_data = []\nfor row in data:\n    if row[\"total_value\"] > 0:\n        rect_data.append(\n            {\n                \"name\": row[\"name\"],\n                \"x_start\": row[\"x_start\"],\n                \"x_end\": row[\"x_end\"],\n                \"y_start\": row[\"level\"],\n                \"y_end\": row[\"level\"] + 1,\n                \"level\": row[\"level\"],\n                \"value\": row[\"total_value\"],\n                \"parent\": row[\"parent\"] if row[\"parent\"] else \"None\",\n            }\n        )\n\nrect_df = pd.DataFrame(rect_data)\n\n# Create icicle chart with mark_rect\nchart = (\n    alt.Chart(rect_df)\n    .mark_rect(stroke=\"white\", strokeWidth=2)\n    .encode(\n        x=alt.X(\"x_start:Q\", axis=None, scale=alt.Scale(domain=[0, 1])),\n        x2=alt.X2(\"x_end:Q\"),\n        y=alt.Y(\n            \"y_start:Q\",\n            axis=alt.Axis(\n                title=\"Hierarchy Level\",\n                labelFontSize=18,\n                titleFontSize=22,\n                values=list(range(max_level + 2)),\n                format=\"d\",\n            ),\n            scale=alt.Scale(domain=[0, max_level + 1]),\n        ),\n        y2=alt.Y2(\"y_end:Q\"),\n        color=alt.Color(\n            \"level:O\",\n            scale=alt.Scale(domain=list(range(max_level + 1)), range=IMPRINT),\n            legend=alt.Legend(title=\"Level\", labelFontSize=16, titleFontSize=18, orient=\"right\"),\n        ),\n        tooltip=[\"name:N\", \"value:Q\", \"parent:N\", \"level:O\"],\n    )\n)\n\n# Add text labels for larger rectangles\ntext = (\n    alt.Chart(rect_df)\n    .mark_text(fontSize=14, color=INK, fontWeight=\"bold\", align=\"center\")\n    .encode(\n        x=alt.X(\"x_mid:Q\", scale=alt.Scale(domain=[0, 1])),\n        y=alt.Y(\"y_mid:Q\", scale=alt.Scale(domain=[0, max_level + 1])),\n        text=alt.Text(\"label:N\"),\n    )\n    .transform_calculate(\n        x_mid=\"(datum.x_start + datum.x_end) / 2\",\n        y_mid=\"(datum.y_start + datum.y_end) / 2\",\n        width=\"datum.x_end - datum.x_start\",\n        label=\"datum.width > 0.05 ? datum.name : ''\",\n    )\n)\n\n# Combine chart and text with theme-adaptive configuration\nicicle = (\n    (chart + text)\n    .properties(width=1600, height=900, background=PAGE_BG, title=\"icicle-basic · altair · anyplot.ai\")\n    .configure_title(fontSize=28, anchor=\"middle\", color=INK)\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save as PNG and HTML with theme-suffixed names\nicicle.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nicicle.save(f\"plot-{THEME}.html\")\n"}