How-to
Getting Started With Python Excel AutomationDeep dive

Plot Charts with matplotlib in Python in Excel

Draw matplotlib and seaborn charts inside a =PY cell, show them in the cell or floating over the grid, size them properly, and know when a native Excel chart is the better choice.

Excel's chart gallery covers the everyday cases well and runs out quickly after that: no small multiples, no regression bands, no distribution plots, and a lot of clicking to reproduce the same chart for twelve regions. Python in Excel brings matplotlib and seaborn into the workbook — both are imported by default as plt and sns — so a =PY cell can draw any chart those libraries can. The result arrives as an image, which changes how you size, place and refresh it. This guide from Using Python in Excel covers the whole path from table to finished chart.

How a Python chart gets from a table to the sheet The formula reads a table with xl, draws a matplotlib figure, and returns it as an image object. The image is then either shown floating over the grid or converted to an Excel value that sits inside the cell. xl(table) DataFrame in matplotlib draw the figure image object returned to the cell show it over cells, or in the cell the chart is a picture, redrawn in the cloud on every recalculation

Prerequisites

  • Excel with Python in Excel available and enabled.
  • A table named Sales with Date, Region, Rep and Amount columns.
  • Familiarity with matplotlib's figure-and-axes interface helps; everything here uses it explicitly rather than the implicit plt.plot state machine, which makes the figure that is returned unambiguous.

Step 1 — Draw a first chart and return the figure

Create a figure and axes, plot on the axes, and make the figure the last expression of the formula. A monthly revenue line is a good first chart because it needs a resample, which Excel's own charts cannot do.

Python
sales = xl("Sales[#All]", headers=True)
monthly = sales.set_index("Date")["Amount"].resample("MS").sum()

fig, ax = plt.subplots(figsize=(6, 3.2))
ax.plot(monthly.index, monthly.values, marker="o", linewidth=2)
ax.set_title("Revenue by month")
ax.set_ylabel("Amount")
ax.grid(axis="y", alpha=0.3)
fig.tight_layout()
fig

Returning fig explicitly is the habit worth forming. Plotting calls also leave a "current figure" behind that Excel can pick up, but a formula that builds two figures, or that ends with a line that is not the plot, can return something other than the chart you meant. fig.tight_layout() stops axis labels from being clipped at the image edges, which is otherwise the most common cosmetic defect.

Step 2 — Show the image over the grid or inside the cell

By default the formula returns an image object: the cell shows a small image icon and the plot is not yet visible at a useful size. There are two ways to display it.

  • Display Plot over Cells — right-click the cell and choose it from the menu, or press Ctrl+Alt+Shift+C. Excel places a copy of the image floating over the sheet, where you can drag it and resize it from its corners.
  • Excel Value — switch the output type from the Python output menu next to the formula bar (or with Ctrl+Alt+Shift+M). The image is then drawn inside the cell itself and scales with the cell, so widen the column, increase the row height or merge cells to make it bigger.
Two ways to display a Python chart Display Plot over Cells creates a floating copy that can be moved and resized freely. Converting to an Excel value draws the image inside the cell, where it resizes with the cell and moves with sorting and row changes. Display Plot over Cells floating copy of the image drag and resize freely Ctrl+Alt+Shift+C good for dashboards Excel Value (in cell) image drawn inside the cell sized by the cell or merge Ctrl+Alt+Shift+M good for tables of charts in-cell images line up with the grid; floating ones do not

In-cell images suit a layout where each row has its own small chart — a sparkline-style trend next to each region, for example — because the image stays attached to its cell. A floating copy suits a dashboard where one large chart should sit in a specific position.

Step 3 — Size and style for the grid

A cell-sized image is small, so charts need to be designed for that. Set figsize to the aspect ratio of the space the chart will occupy, raise the DPI modestly so text stays crisp when Excel scales the image, and strip anything that does not carry information.

Python
sales = xl("Sales[#All]", headers=True)
by_region = sales.groupby("Region")["Amount"].sum().sort_values()

fig, ax = plt.subplots(figsize=(4.5, 2.4), dpi=150)
bars = ax.barh(by_region.index, by_region.values, color="#5b5cf0")
ax.bar_label(bars, labels=[f"{v:,.0f}" for v in by_region.values], padding=3, fontsize=8)
ax.spines[["top", "right"]].set_visible(False)
ax.tick_params(labelsize=8)
ax.set_xlim(0, by_region.max() * 1.18)
ax.set_title("Revenue by region", fontsize=10, loc="left")
fig.tight_layout()
fig

Direct value labels with bar_label replace the axis ticks a reader would otherwise squint at, and set_xlim leaves room for the longest label. A consistent figsize and DPI across every chart in a workbook makes the images line up when they are placed side by side, in the same way a shared style does in Apply a Reusable Style Theme Across an Excel Report.

Step 4 — Use seaborn for statistical charts and small multiples

The charts Excel cannot draw are where Python in Excel is most worth its cost. seaborn builds a grid of panels — one per region — or a distribution with a fitted density in a single call, and its figure-level functions return an object whose .figure attribute is the matplotlib figure to return.

Python
sales = xl("Sales[#All]", headers=True)
sales["Month"] = sales["Date"].dt.to_period("M").dt.to_timestamp()
monthly = sales.groupby(["Region", "Month"], as_index=False)["Amount"].sum()

grid = sns.relplot(
    data=monthly, x="Month", y="Amount", col="Region",
    kind="line", marker="o", height=2.4, aspect=1.2,
)
grid.set_titles("{col_name}")
grid.set_axis_labels("", "Amount")
for ax in grid.axes.flat:
    ax.tick_params(axis="x", labelrotation=45, labelsize=7)
grid.figure.tight_layout()
grid.figure

A small-multiples chart like this replaces a dozen separate Excel charts, and it redraws automatically when the Sales table changes. For a distribution, sns.histplot(data=sales, x="Amount", hue="Region", kde=True) returns axes; take ax.figure from it in the same way.

Chart types by how well each tool handles them Simple bar and line charts are easy in native Excel charts. Small multiples, distribution plots with density curves and regression bands are either awkward or impossible natively and straightforward with seaborn. effort in native Excel charts, relative bar or line easy natively combo, 2 axes manageable small multiples use seaborn density, regression use seaborn reach for Python where the native gallery stops

Step 5 — Know when a native Excel chart is better

A Python chart is an image. That makes it the wrong tool in a few common situations: when readers need to hover for values or filter series, when the workbook must work offline or for people without Python in Excel, and when a chart should update instantly while someone types. In those cases, let Python compute and spill the numbers as Excel values, and build a native chart on the spilled range.

Python
sales = xl("Sales[#All]", headers=True)
(
    sales.pivot_table(index=sales["Date"].dt.to_period("M").astype(str),
                      columns="Region", values="Amount", aggfunc="sum", fill_value=0)
         .rename_axis("Month")
         .reset_index()
)

Set this formula's output to Excel value, select the spilled block, and insert a line chart. Python does the reshaping that would otherwise need a helper pivot table; Excel draws a chart that is interactive and needs no cloud round trip to repaint. The same "compute in Python, chart natively" split is how scripts produce charts in files, as in Add a Line Chart to an Excel Report with Python.

Step 6 — Build a row of trend images with a helper function

In-cell images become powerful when each row of a summary table carries its own small trend: region in column A, total in column B, and a tiny line chart in column C. Writing the same plotting code twelve times would be fragile, so define a helper once in an early cell and call it from one short formula per row.

Python
# Cell A1 (top of the first sheet) — shared data and a plotting helper
sales = xl("Sales[#All]", headers=True)
monthly_by_region = (
    sales.assign(Month=sales["Date"].dt.to_period("M").dt.to_timestamp())
         .groupby(["Region", "Month"])["Amount"].sum()
)

def trend(region: str):
    series = monthly_by_region.loc[region]
    fig, ax = plt.subplots(figsize=(2.4, 0.7), dpi=150)
    ax.plot(series.index, series.values, linewidth=1.6, color="#0f9488")
    ax.fill_between(series.index, series.values, alpha=0.15, color="#0f9488")
    ax.scatter(series.index[-1], series.values[-1], s=12, color="#0f9488", zorder=3)
    ax.axis("off")
    fig.tight_layout(pad=0.1)
    return fig

"helpers ready"
Python
# Cell C5 — one formula per row; A5 holds the region name as ordinary text
trend(xl("A5"))

Set each trend cell's output to Excel Value so the image sits inside it, then give the column a fixed width and the rows a fixed height so every chart has the same proportions. ax.axis("off") removes ticks and frames, which are unreadable at this size anyway, and the dot on the last point draws the eye to the current value. Because the helper reads from monthly_by_region, the data is aggregated once rather than once per row — and because the calculation order runs across each row and down, the helper in row 1 always exists before row 5 needs it.

Common pitfalls

SymptomCauseFix
Cell shows an image icon, no visible chartDefault image-object outputDisplay Plot over Cells, or switch to Excel Value
Labels cut off at the edgesFigure laid out without marginsCall fig.tight_layout() before returning
Wrong chart returnedFormula made several figures, or ended on another lineReturn the intended fig as the last expression
Text unreadable in a cell-sized imageDefault figure size and DPISmaller figsize, dpi=150, larger fonts
Floating chart shows old dataIt is a copy of the imageDisplay it again after recalculation, or use the in-cell image
seaborn call returns a grid, not a figureFigure-level functions return a FacetGridReturn grid.figure

Performance and scale notes

Every recalculation redraws the chart in the cloud and sends a new image back, so a sheet with many chart cells recalculates noticeably more slowly than one with a few. Aggregate before plotting — a line of twelve monthly points draws faster and reads better than 50,000 raw points — and switch to Partial calculation while editing a chart-heavy workbook. Remember too that the data behind any single calculation is capped at 100 MB, which a row-level scatter of a very large table can approach.

Conclusion

Build charts with explicit figures and axes, finish the formula with the figure, and choose the display that suits the layout: a floating copy for a dashboard, an in-cell image for a row-aligned table of charts. Design for the small space with deliberate size, DPI and direct labels. Use seaborn where Excel's gallery runs out — small multiples, distributions and statistical overlays — and when a chart must be interactive or work without Python, spill the numbers as Excel values and draw a native chart on them.

Frequently asked questions

Do I need to import matplotlib or seaborn? No. Python in Excel imports matplotlib.pyplot as plt and seaborn as sns by default, and lists those imports in the Initialization pane on the Formulas tab.

Can I call plt.show()? There is no window to show a plot in. Return the figure object as the last expression instead; that is what Excel turns into the image.

Can I copy a Python chart into PowerPoint? Yes. Display the plot over cells, then copy the floating image like any other picture. It is a static image, so it does not update in the presentation.

Can I use plotly or other interactive libraries? Interactive output does not carry into the grid, because Excel receives an image. Stick to matplotlib-based libraries for charts in cells.

More on python in excel

More on python in excel →