{ "cells": [ { "cell_type": "markdown", "id": "ddd7bbc3-b7c1-4b3b-8ac0-24c37e8c6cc5", "metadata": {}, "source": [ "# Multiline Axis Labels\n", "\n", "Multiline axis labels allow better visualization of long categorical values. Instead of overcrowding the axis, labels can now be formatted across multiple lines, enhancing readability. \n", "\n", "* To create a multiline axis label, simply add \"\\n\" inside the string where you want the line break." ] }, { "cell_type": "code", "execution_count": 1, "id": "e83da232-c578-4c48-848e-53fb1199a51c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " <div id=\"fDlMFF\"></div>\n", " <script type=\"text/javascript\" data-lets-plot-script=\"library\">\n", " if(!window.letsPlotCallQueue) {\n", " window.letsPlotCallQueue = [];\n", " }; \n", " window.letsPlotCall = function(f) {\n", " window.letsPlotCallQueue.push(f);\n", " };\n", " (function() {\n", " var script = document.createElement(\"script\");\n", " script.type = \"text/javascript\";\n", " script.src = \"https://cdn.jsdelivr.net/gh/JetBrains/lets-plot@v4.6.0rc1/js-package/distr/lets-plot.min.js\";\n", " script.onload = function() {\n", " window.letsPlotCall = function(f) {f();};\n", " window.letsPlotCallQueue.forEach(function(f) {f();});\n", " window.letsPlotCallQueue = [];\n", " \n", " };\n", " script.onerror = function(event) {\n", " window.letsPlotCall = function(f) {}; // noop\n", " window.letsPlotCallQueue = [];\n", " var div = document.createElement(\"div\");\n", " div.style.color = 'darkred';\n", " div.textContent = 'Error loading Lets-Plot JS';\n", " document.getElementById(\"fDlMFF\").appendChild(div);\n", " };\n", " var e = document.getElementById(\"fDlMFF\");\n", " e.appendChild(script);\n", " })()\n", " </script>\n", " " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from lets_plot import *\n", "import pandas as pd\n", "import numpy as np\n", "\n", "LetsPlot.setup_html()" ] }, { "cell_type": "markdown", "id": "63f9d1ac-de82-4cda-85f1-3f3d8998eef0", "metadata": {}, "source": [ "## Quarters & Years" ] }, { "cell_type": "code", "execution_count": 2, "id": "8ea6aa2f-e6b9-42d1-acb0-8984bd93d2d6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Quarter_Year</th>\n", " <th>Type</th>\n", " <th>Amount</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Q1-2022</td>\n", " <td>Income</td>\n", " <td>11564</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Q1-2022</td>\n", " <td>Expenses</td>\n", " <td>9924</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Q2-2022</td>\n", " <td>Income</td>\n", " <td>11365</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Q2-2022</td>\n", " <td>Expenses</td>\n", " <td>10432</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " Quarter_Year Type Amount\n", "0 Q1-2022 Income 11564\n", "1 Q1-2022 Expenses 9924\n", "2 Q2-2022 Income 11365\n", "3 Q2-2022 Expenses 10432" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a DataFrame with artificial income and expenses\n", "np.random.seed(42)\n", "years = [2022, 2023, 2024]\n", "quarters = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\n", "\n", "data = []\n", "for year in years:\n", " base_income = np.random.randint(5000, 15000) # Base income\n", " base_expenses = base_income * np.random.uniform(0.6, 0.9) # Expenses as 60-90% of income\n", " \n", " for quarter in quarters:\n", " income = base_income + np.random.randint(-2000, 2000) # Add variation\n", " expenses = base_expenses + np.random.randint(-1500, 1500)\n", " \n", " quarter_year = f\"{quarter}-{year}\"\n", " data.append({\"Quarter_Year\": quarter_year, \"Type\": \"Income\", \"Amount\": round(income)})\n", " data.append({\"Quarter_Year\": quarter_year, \"Type\": \"Expenses\", \"Amount\": round(expenses)})\n", "\n", "df = pd.DataFrame(data)\n", "df.head(4)" ] }, { "cell_type": "code", "execution_count": 3, "id": "18870e14-f8f7-4bac-a914-ca9c188b93a2", "metadata": {}, "outputs": [], "source": [ "# Multiline labels for X-axis\n", "x_labels_mapping = [f\"{q}\\n{year}\" if q == \"Q1\" else q for year in years for q in quarters]" ] }, { "cell_type": "code", "execution_count": 4, "id": "9a735efd-24e3-450b-890d-f0eb076e17a5", "metadata": {}, "outputs": [ { "data": { "text/html": [ " <div id=\"LTa8WW\"></div>\n", " <script type=\"text/javascript\" data-lets-plot-script=\"plot\">\n", " \n", " (function() {\n", " // ----------\n", " \n", " const forceImmediateRender = false;\n", " const responsive = false;\n", " \n", " let sizing = {\n", " width_mode: \"MIN\",\n", " height_mode: \"SCALED\",\n", " width: null, \n", " height: null \n", " };\n", " \n", " const preferredWidth = document.body.dataset.letsPlotPreferredWidth;\n", " if (preferredWidth !== undefined) {\n", " sizing = {\n", " width_mode: 'FIXED',\n", " height_mode: 'SCALED',\n", " width: parseFloat(preferredWidth)\n", " };\n", " }\n", " \n", " const containerDiv = document.getElementById(\"LTa8WW\");\n", " let fig = null;\n", " \n", " function renderPlot() {\n", " if (fig === null) {\n", " const plotSpec = {\n", "\"data\":{\n", "\"Quarter_Year\":[\"Q1-2022\",\"Q1-2022\",\"Q2-2022\",\"Q2-2022\",\"Q3-2022\",\"Q3-2022\",\"Q4-2022\",\"Q4-2022\",\"Q1-2023\",\"Q1-2023\",\"Q2-2023\",\"Q2-2023\",\"Q3-2023\",\"Q3-2023\",\"Q4-2023\",\"Q4-2023\",\"Q1-2024\",\"Q1-2024\",\"Q2-2024\",\"Q2-2024\",\"Q3-2024\",\"Q3-2024\",\"Q4-2024\",\"Q4-2024\"],\n", "\"Type\":[\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\",\"Income\",\"Expenses\"],\n", "\"Amount\":[11564.0,9924.0,11365.0,10432.0,12439.0,9260.0,11508.0,9124.0,11749.0,10515.0,12313.0,7726.0,10263.0,8365.0,10969.0,9111.0,10904.0,7996.0,11133.0,6131.0,13334.0,5693.0,12249.0,6419.0]\n", "},\n", "\"mapping\":{\n", "\"x\":\"Quarter_Year\",\n", "\"y\":\"Amount\",\n", "\"color\":\"Type\"\n", "},\n", "\"data_meta\":{\n", "\"series_annotations\":[{\n", "\"type\":\"str\",\n", "\"column\":\"Quarter_Year\"\n", "},{\n", "\"type\":\"str\",\n", "\"column\":\"Type\"\n", "},{\n", "\"type\":\"int\",\n", "\"column\":\"Amount\"\n", "}]\n", "},\n", "\"ggtitle\":{\n", "\"text\":\"Quarterly Income vs Expenses\"\n", "},\n", "\"guides\":{\n", "\"x\":{\n", "\"title\":\"Quarters\"\n", "},\n", "\"y\":{\n", "\"title\":\"Amount ($)\"\n", "}\n", "},\n", "\"theme\":{\n", "\"axis_text_x\":{\n", "\"angle\":30.0,\n", "\"blank\":false\n", "}\n", "},\n", "\"kind\":\"plot\",\n", "\"scales\":[{\n", "\"aesthetic\":\"x\",\n", "\"labels\":[\"Q1\\n2022\",\"Q2\",\"Q3\",\"Q4\",\"Q1\\n2023\",\"Q2\",\"Q3\",\"Q4\",\"Q1\\n2024\",\"Q2\",\"Q3\",\"Q4\"],\n", "\"discrete\":true,\n", "\"reverse\":false\n", "}],\n", "\"layers\":[{\n", "\"geom\":\"line\",\n", "\"mapping\":{\n", "},\n", "\"data_meta\":{\n", "},\n", "\"size\":1.5,\n", "\"data\":{\n", "}\n", "},{\n", "\"geom\":\"point\",\n", "\"mapping\":{\n", "},\n", "\"data_meta\":{\n", "},\n", "\"size\":4.0,\n", "\"data\":{\n", "}\n", "}],\n", "\"metainfo_list\":[],\n", "\"spec_id\":\"1\"\n", "};\n", " window.letsPlotCall(function() { fig = LetsPlot.buildPlotFromProcessedSpecs(plotSpec, containerDiv, sizing); });\n", " } else {\n", " fig.updateView({});\n", " }\n", " }\n", " \n", " const renderImmediately = \n", " forceImmediateRender || (\n", " sizing.width_mode === 'FIXED' && \n", " (sizing.height_mode === 'FIXED' || sizing.height_mode === 'SCALED')\n", " );\n", " \n", " if (renderImmediately) {\n", " renderPlot();\n", " }\n", " \n", " if (!renderImmediately || responsive) {\n", " // Set up observer for initial sizing or continuous monitoring\n", " var observer = new ResizeObserver(function(entries) {\n", " for (let entry of entries) {\n", " if (entry.contentBoxSize && \n", " entry.contentBoxSize[0].inlineSize > 0) {\n", " if (!responsive && observer) {\n", " observer.disconnect();\n", " observer = null;\n", " }\n", " renderPlot();\n", " if (!responsive) {\n", " break;\n", " }\n", " }\n", " }\n", " });\n", " \n", " observer.observe(containerDiv);\n", " }\n", " \n", " // ----------\n", " })();\n", " \n", " </script>" ], "text/plain": [ "<lets_plot.plot.core.PlotSpec at 0x1668b8d00>" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot(df, aes(x=\"Quarter_Year\", y=\"Amount\", color=\"Type\")) + \\\n", " geom_line(size=1.5) + \\\n", " geom_point(size=4) + \\\n", " scale_x_discrete(labels=x_labels_mapping) + \\\n", " labs(title=\"Quarterly Income vs Expenses\",\n", " x=\"Quarters\",\n", " y=\"Amount ($)\") + \\\n", " theme(axis_text_x=element_text(angle=30))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.21" } }, "nbformat": 4, "nbformat_minor": 5 }