{"id":"l-pda-5-2","courseId":"python-data-analysis","lang":"en","kind":"lesson","summary":"Choose the right chart for a question, draw it from a DataFrame with pandas .plot and matplotlib’s figure and axes, and label it so the chart states its finding on its own.","html":"<p>You have numbers that answer the head of growth’s question, and soon you will present them. A table of 24 monthly figures makes people squint; a line chart of the same figures shows the November spike before anyone reads an axis. Charts are not decoration added at the end. They are how most people will consume your analysis, and a good one is often the whole message.</p>\n<p>This lesson’s chart code is for your own Jupyter notebook: the Run blocks in this course show printed text, not images. The data preparation runs here as usual.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Choose the chart from the question</h2>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 220\" role=\"img\" aria-labelledby=\"l-pda-5-2-t1\"><defs><marker id=\"arrow-l-pda-5-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-pda-5-2-t1\">Four panels. How does it change over time: a line chart. Which group is bigger: sorted horizontal bars. How are values spread: a histogram. Do two things move together: a scatter plot.</title>\n  <rect class=\"d-box\" x=\"10\" y=\"10\" width=\"160\" height=\"160\" rx=\"10\"/>\n  <path class=\"d-arrow\" d=\"M30 130 L60 115 L90 120 L120 95 L150 50\"/>\n  <text class=\"d-label-strong\" x=\"90\" y=\"196\" text-anchor=\"middle\">change over time</text>\n  <text class=\"d-label-muted\" x=\"90\" y=\"214\" text-anchor=\"middle\">line</text>\n  <rect class=\"d-box\" x=\"185\" y=\"10\" width=\"160\" height=\"160\" rx=\"10\"/>\n  <rect class=\"d-box-primary\" x=\"200\" y=\"35\" width=\"130\" height=\"20\"/>\n  <rect class=\"d-box-primary\" x=\"200\" y=\"65\" width=\"100\" height=\"20\"/>\n  <rect class=\"d-box-primary\" x=\"200\" y=\"95\" width=\"75\" height=\"20\"/>\n  <rect class=\"d-box-primary\" x=\"200\" y=\"125\" width=\"40\" height=\"20\"/>\n  <text class=\"d-label-strong\" x=\"265\" y=\"196\" text-anchor=\"middle\">compare groups</text>\n  <text class=\"d-label-muted\" x=\"265\" y=\"214\" text-anchor=\"middle\">sorted bars</text>\n  <rect class=\"d-box\" x=\"360\" y=\"10\" width=\"160\" height=\"160\" rx=\"10\"/>\n  <rect class=\"d-box-accent\" x=\"375\" y=\"100\" width=\"22\" height=\"50\"/>\n  <rect class=\"d-box-accent\" x=\"399\" y=\"70\" width=\"22\" height=\"80\"/>\n  <rect class=\"d-box-accent\" x=\"423\" y=\"60\" width=\"22\" height=\"90\"/>\n  <rect class=\"d-box-accent\" x=\"447\" y=\"95\" width=\"22\" height=\"55\"/>\n  <rect class=\"d-box-accent\" x=\"471\" y=\"125\" width=\"22\" height=\"25\"/>\n  <text class=\"d-label-strong\" x=\"440\" y=\"196\" text-anchor=\"middle\">spread of values</text>\n  <text class=\"d-label-muted\" x=\"440\" y=\"214\" text-anchor=\"middle\">histogram</text>\n  <rect class=\"d-box\" x=\"535\" y=\"10\" width=\"160\" height=\"160\" rx=\"10\"/>\n  <circle class=\"d-dot\" cx=\"560\" cy=\"140\" r=\"4\"/><circle class=\"d-dot\" cx=\"580\" cy=\"120\" r=\"4\"/><circle class=\"d-dot\" cx=\"600\" cy=\"125\" r=\"4\"/>\n  <circle class=\"d-dot\" cx=\"615\" cy=\"95\" r=\"4\"/><circle class=\"d-dot\" cx=\"635\" cy=\"85\" r=\"4\"/><circle class=\"d-dot\" cx=\"650\" cy=\"60\" r=\"4\"/><circle class=\"d-dot\" cx=\"670\" cy=\"45\" r=\"4\"/>\n  <text class=\"d-label-strong\" x=\"615\" y=\"196\" text-anchor=\"middle\">relationship</text>\n  <text class=\"d-label-muted\" x=\"615\" y=\"214\" text-anchor=\"middle\">scatter</text>\n</svg><figcaption>Four questions, four chart types</figcaption></figure>\n<p>Most business charts are one of these four. Pie charts compare poorly beyond two or three slices; a sorted bar chart says the same thing more clearly. Charts with two different y-axes invite the reader to compare lines that are on unrelated scales, so prefer two charts stacked one above the other.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Prepare the data, then plot</h2>\n<p>A chart wants a small table: one row per point or bar, already sorted and labelled. Building it is ordinary pandas, and it runs here:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-run=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"python\" data-index=\"0\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pandas </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_items.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.read_csv(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"orders.csv\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">parse_dates</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"quantity\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"unit_price\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"discount\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.merge(items.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">as_index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">False</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum(), </span><span style=\"color:#953800;--shiki-dark:#FFA657\">on</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_id\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">validate</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"one_to_one\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders[orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"status\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"cancelled\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">monthly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> orders.set_index(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"order_date\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).resample(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"ME\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">by_year </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> monthly.groupby([monthly.index.year, monthly.index.month]).sum().unstack(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">level</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">by_year.index.name </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"month\"</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(by_year.round(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p><code dir=\"ltr\">unstack(level=0)</code> moves the year into the columns, giving one column per year and one row per month: exactly the shape for a chart with one line per year. Comparing years on the same January-to-December axis is how you see seasonality: if both lines rise in November, the November spike is a pattern, not an accident.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Draw it with pandas and matplotlib</h2>\n<p>pandas’ <code dir=\"ltr\">.plot</code> methods call matplotlib for you. Create the figure and axes yourself, so you can finish the chart properly:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> matplotlib.pyplot </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> plt</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig, ax </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> plt.subplots(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">figsize</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">by_year.plot(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">ax</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">marker</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"o\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_title(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"November is the peak in both years; 2025 runs about 2.7x 2024\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_xlabel(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Month\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_ylabel(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Revenue (USD)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.yaxis.set_major_formatter(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{x:,.0f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_xticks(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">range</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">13</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.legend(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Year\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig.tight_layout()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig.savefig(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"monthly_revenue.png\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">dpi</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">150</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">bbox_inches</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tight\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span></code></pre></div>\n<p><code dir=\"ltr\">plt.subplots()</code> returns a <strong>figure</strong> (the whole image) and an <strong>axes</strong> (one plotting area with its x and y axes). <code dir=\"ltr\">by_year.plot(ax=ax)</code> draws one line per column onto that axes, and every <code dir=\"ltr\">ax.set_...</code> call refines it. A format string passed to <code dir=\"ltr\">set_major_formatter</code> turns tick values into dollars with thousands separators. <code dir=\"ltr\">savefig</code> writes the file you paste into a slide; <code dir=\"ltr\">bbox_inches=&quot;tight&quot;</code> trims empty margins.</p>\n<p>For comparing categories, sort first and use horizontal bars so long labels stay readable:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># sales: the line-level table you built in Lesson 4.2</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">revenue_by_country </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sales.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"country\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].sum().sort_values()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig, ax </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> plt.subplots(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">figsize</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">7</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">bars </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> ax.barh(revenue_by_country.index, revenue_by_country.values)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.bar_label(bars, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">fmt</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"$</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{:,.0f}</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">padding</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_title(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"UAE and UK lead revenue; Jordan trails\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_xlabel(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Revenue 2024-2025 (USD)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig.tight_layout()</span></span></code></pre></div>\n<p>And for a distribution, a histogram of order values with the median marked:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">fig, ax </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> plt.subplots(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">figsize</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">7</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].plot.hist(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">bins</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">40</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">ax</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.axvline(orders[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"revenue\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].median(), </span><span style=\"color:#953800;--shiki-dark:#FFA657\">linestyle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"--\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">label</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"median $147\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.set_xlabel(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Order value (USD)\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax.legend()</span></span></code></pre></div>\n<p>For relationships, a scatter plot puts one numeric column on each axis. With thousands of points they pile up into a blob, so make them small and partly transparent, and it is often clearer to plot a summary instead, such as the purchase rate per number of pages viewed:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># assumes fig, ax = plt.subplots() and churn.csv / web_sessions.csv loaded as churn / sessions</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">churn.plot.scatter(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">x</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tenure_days\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">y</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"total_spent\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">s</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">alpha</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">ax</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">rate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sessions.groupby(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"pages_viewed\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"purchased\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].mean()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">rate.loc[:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">15</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].plot(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">ax</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ax, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">marker</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"o\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># one point per page count, up to 15</span></span></code></pre></div>\n<p>The first line is the pattern for any two numeric columns, here each customer’s tenure against their total spend from <code dir=\"ltr\">churn.csv</code>; the other two turn 6,000 sessions into fifteen readable points, each a conversion rate.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Check the chart before you share it</h2>\n<p>Charts hide mistakes well. Before a chart leaves your notebook, compare it with the table it came from: the number of bars or points, the first and last period (a partial month at the end looks like a collapse, as Lesson 4.4 warned), and one or two values you can read off and check against a printed number. Then look at it once as a stranger would: can someone who has never seen the data say what it shows in one sentence? If not, change the title, the sort order or the chart type, not the reader.</p>\n<p>You will also meet <strong>seaborn</strong>, a library built on matplotlib with good statistical defaults. Everything here carries over: it draws onto the same <code dir=\"ltr\">ax</code>, and the same rules about baselines and titles apply.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>A title that names the axes</span></p>\n<p>“Revenue by month” tells the reader what the axes already say. “November is the peak in both years” tells them what to see. Write the title as the sentence you would say while pointing at the chart, and keep the variable names for the axis labels.</p>\n</aside>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Fewer colours, more meaning</span></p>\n<p>Use one colour for everything, then a second, stronger colour for the one bar or line the reader should look at, such as Q4 2025. Default colour cycles are fine for exploring; for a presentation, colour should carry the message.</p>\n</aside>\n<p>In this lesson’s exercise you pick the right charts for a set of questions. Next you put everything together and answer the Q4 question end to end.</p>\n","toc":[{"id":"sec-1","text":"Choose the chart from the question","level":2},{"id":"sec-2","text":"Prepare the data, then plot","level":2},{"id":"sec-3","text":"Draw it with pandas and matplotlib","level":2},{"id":"sec-4","text":"Check the chart before you share it","level":2}],"takeaways":["Pick the chart from the question - line for change over time, sorted bars for comparing categories, histogram for a distribution, scatter for a relationship.","<code dir=\"ltr\">fig, ax = plt.subplots()</code> then <code dir=\"ltr\">df.plot(ax=ax)</code> gives you pandas’ convenience with matplotlib’s full control over titles, axes and saving.","Prepare a small, tidy table first (one row per bar or point), then plot it; most charting problems are data-shaping problems.","Bars start at zero, axes carry units, and the title states the finding rather than naming the variables.","<code dir=\"ltr\">fig.savefig(&quot;chart.png&quot;, dpi=150, bbox_inches=&quot;tight&quot;)</code> saves the chart for a report; charts are not shown in this course’s Run blocks."],"further":[{"title":"pandas - Chart visualization","url":"https://pandas.pydata.org/docs/user_guide/visualization.html"},{"title":"matplotlib - Quick start guide","url":"https://matplotlib.org/stable/users/explain/quick_start.html"},{"title":"matplotlib.axes.Axes.bar_label","url":"https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bar_label.html"}],"quiz":[{"q":"<p>You want to show that revenue per channel grew from 2024 to 2025. Which chart reads best?</p>","options":[{"text":"Two pie charts, one per year","why":"Pies show parts of one whole; comparing slice sizes across two pies is hard, and they hide the growth in the total."},{"text":"A histogram of order values","why":"A histogram shows a distribution of one variable; it says nothing about channels or years."},{"text":"A scatter plot of order value against order date","why":"Two thousand points show spread over time, but not the per-channel totals you want to compare."},{"text":"Grouped bars, one group per channel and one bar per year, starting at zero","why":"Correct. Bars compare amounts, grouping puts the two years side by side for each channel, and a zero baseline keeps lengths honest."}],"answer":3},{"q":"<p>What is the advantage of <code dir=\"ltr\">fig, ax = plt.subplots()</code> followed by <code dir=\"ltr\">monthly.plot(ax=ax)</code>, over calling <code dir=\"ltr\">monthly.plot()</code> alone?</p>","options":[{"text":"You hold the axes object, so you can set titles, labels, formatting and save the exact figure you configured.","why":"Correct. pandas draws onto the axes you pass, and you keep full matplotlib control over everything else."},{"text":"It is the only way to make a line chart.","why":"<code dir=\"ltr\">monthly.plot()</code> alone also draws a line chart; it just creates the figure for you."},{"text":"It makes the chart interactive.","why":"Interactivity depends on the environment and backend, not on how the axes were created."}],"answer":0},{"q":"<p>A bar chart of revenue by country has a y-axis starting at 60,000. What is the problem?</p>","options":[{"text":"The bars become too thin.","why":"The axis range does not change bar width."},{"text":"Bar lengths no longer match the values, so a 10% difference can look like a 300% difference.","why":"Correct. Readers compare bar lengths; cutting the baseline exaggerates differences. Line charts can zoom in, bars cannot."},{"text":"Countries with lower revenue disappear.","why":"Bars below 60,000 would be cut off or vanish, which is a symptom of the real problem, the misleading baseline."}],"answer":1}],"exercise":{"kind":"choice","multiple":true,"options":["line-years","pie-months","bars-quarters-zero","dual-axis","hist-orders","bars-quarters-cut"],"correct":["line-years","bars-quarters-zero"],"title":"Pick the charts for the Q4 briefing","prompt":"<p>The head of growth asks: <strong>“Is Q4 2025 unusual, or is it the normal seasonal peak on a growing business?”</strong> You have monthly and quarterly revenue for 2024 and 2025. Select every chart that would help answer that question honestly.</p>\n","hints":["The question is about change over time and about comparing the same season across two years.","Ask of each chart: does it keep bar lengths honest, and does it put Q4 2025 next to something it can be compared with?"],"explanation":"<p>A line chart with one line per year on a January-to-December axis shows at a glance that both years peak in November, so seasonality is visible, and the gap between the lines shows growth. Quarterly bars starting at zero make the size of each quarter honest and let Q4 2025 be compared with Q4 2024 and with the other quarters. Pie charts, truncated axes and dual axes all make the comparison harder or misleading, and a histogram of order values answers a different question (how big orders are, not how revenue changed).</p>\n","checkLabels":{},"optionText":{"line-years":{"text":"A line chart of monthly revenue with one line for 2024 and one for 2025, months January to December on the x-axis","why":"Good choice. Overlaying the years on the same calendar axis shows the shared November peak (seasonality) and the gap between years (growth) in one picture."},"pie-months":{"text":"A pie chart of 2025 revenue by month","why":"Twelve slices are impossible to compare precisely, and a pie hides time order and says nothing about 2024."},"bars-quarters-zero":{"text":"A bar chart of revenue for all eight quarters, y-axis starting at zero, with Q4 2025 highlighted","why":"Good choice. Bars from zero keep the sizes honest, the full sequence shows the growth trend, and highlighting Q4 2025 directs attention without distorting it."},"dual-axis":{"text":"One chart with 2025 revenue on a left axis and 2024 revenue on a separate right axis","why":"Two independent axes can be scaled to make the lines look like anything; readers will compare heights that are on different scales."},"hist-orders":{"text":"A histogram of Q4 2025 order values","why":"It shows how big Q4 orders were, which is useful later, but it cannot tell you whether the quarter was unusual compared with others."},"bars-quarters-cut":{"text":"A bar chart of the eight quarters with the y-axis starting at 40,000 to make the differences clearer","why":"Cutting the baseline exaggerates differences: Q1 2025 would look tiny next to Q4 2025, and three of the 2024 quarters, all below 40,000, would vanish entirely. Bars must start at zero."}}},"words":716,"readMinutes":3}