IPYNB to Markdown Converter
Turn a .ipynb file into one Markdown file for a README, a docs site or a blog: code in fenced blocks, outputs under it, and charts embedded so nothing else needs copying. Made in your browser.
Open a Jupyter notebook
Drop a .ipynb file here, choose one or paste it. No Jupyter, no install, no upload.
# Bike rides and rain: a short walkthrough
A week of **made-up** numbers from a small town's bike-share scheme. It shows what a notebook can hold: Markdown, code, a table, a chart, maths, a warning and, at the end, an error.
```python
import pandas as pd
import matplotlib.pyplot as plt
```
```python
rides = pd.DataFrame({
"day": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
"trips": [412, 438, 395, 451, 288, 230],
"rain_mm": [0.0, 1.2, 4.5, 0.0, 9.8, 14.1],
}).set_index("day")
rides
```
<div>
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right">
<th></th>
<th>trips</th>
<th>rain_mm</th>
</tr>
<tr>
<th>day</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>Mon</th>
<td>412</td>
<td>0.0</td>
</tr>
<tr>
<th>Tue</th>
<td>438</td>
<td>1.2</td>
</tr>
<tr>
<th>Wed</th>
<td>395</td>
<td>4.5</td>
</tr>
<tr>
<th>Thu</th>
<td>451</td>
<td>0.0</td>
</tr>
<tr>
<th>Fri</th>
<td>288</td>
<td>9.8</td>
</tr>
<tr>
<th>Sat</th>
<td>230</td>
<td>14.1</td>
</tr>
</tbody>
</table>
</div>
## How busy was the week?
The mean number of trips per day is
$$\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i$$
where $n$ is the number of days and $x_i$ is the number of trips on day $i$.
```python
mean = rides["trips"].mean()
print(f"Mean trips per day: {mean:.1f}")
print(f"Busiest day: {rides['trips'].idxmax()}")
```
```
Mean trips per day: 369.0
Busiest day: Thu
```
```python
import warnings
if rides["rain_mm"].max() > 10:
warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
```
```
/tmp/ipykernel_4120/2715331896.py:4: UserWarning: Saturday had over 10 mm of rain; the gauge reads high in storms.
warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
```
## Rain and riding
Fewer people rode on wet days. Pearson’s correlation coefficient measures how closely two columns move together, from −1 to 1:
$$r = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_i (x_i - \bar{x})^2}\,\sqrt{\sum_i (y_i - \bar{y})^2}}$$
```python
fig, ax = plt.subplots(figsize=(6.4, 3.6))
ax.bar(rides.index, rides["trips"], color="#4878d0")
ax.set_ylabel("Trips")
ax.set_title("Bike-share trips per day")
plt.show()
```

```python
round(rides["trips"].corr(rides["rain_mm"]), 3)
```
```
-0.979
```
### A day that isn’t there
There is no Sunday in the data, so asking for it raises a `KeyError`. This is what a traceback looks like:
```python
rides.loc["Sun"]
```
```
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[7], line 1
----> 1 rides.loc["Sun"]
File ~/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
3810 return self._engine.get_loc(casted_key)
3811 except KeyError as err:
-> 3812 raise KeyError(key) from err
KeyError: 'Sun'
```
### What next
- [x] Load a week of rides
- [x] Chart them
- [ ] Add the weekend’s weather from the town’s own gauge
> Made-up data, made for this example. Any resemblance to a real town is a coincidence.
Bike rides and rain: a short walkthrough
A week of made-up numbers from a small town's bike-share scheme. It shows what a notebook can hold: Markdown, code, a table, a chart, maths, a warning and, at the end, an error.
import pandas as pd
import matplotlib.pyplot as pltrides = pd.DataFrame({
"day": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
"trips": [412, 438, 395, 451, 288, 230],
"rain_mm": [0.0, 1.2, 4.5, 0.0, 9.8, 14.1],
}).set_index("day")
rides| trips | rain_mm | |
|---|---|---|
| day | ||
| Mon | 412 | 0.0 |
| Tue | 438 | 1.2 |
| Wed | 395 | 4.5 |
| Thu | 451 | 0.0 |
| Fri | 288 | 9.8 |
| Sat | 230 | 14.1 |
How busy was the week?
The mean number of trips per day is
where is the number of days and is the number of trips on day .
mean = rides["trips"].mean()
print(f"Mean trips per day: {mean:.1f}")
print(f"Busiest day: {rides['trips'].idxmax()}")Mean trips per day: 369.0 Busiest day: Thu
import warnings
if rides["rain_mm"].max() > 10:
warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")/tmp/ipykernel_4120/2715331896.py:4: UserWarning: Saturday had over 10 mm of rain; the gauge reads high in storms.
warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
Rain and riding
Fewer people rode on wet days. Pearson’s correlation coefficient measures how closely two columns move together, from −1 to 1:
fig, ax = plt.subplots(figsize=(6.4, 3.6))
ax.bar(rides.index, rides["trips"], color="#4878d0")
ax.set_ylabel("Trips")
ax.set_title("Bike-share trips per day")
plt.show()round(rides["trips"].corr(rides["rain_mm"]), 3)-0.979
A day that isn’t there
There is no Sunday in the data, so asking for it raises a KeyError. This is what a traceback looks like:
rides.loc["Sun"]KeyError: 'Sun'
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[7], line 1 ----> 1 rides.loc["Sun"] File ~/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key) 3810 return self._engine.get_loc(casted_key) 3811 except KeyError as err: -> 3812 raise KeyError(key) from err KeyError: 'Sun'
What next
- Load a week of rides
- Chart them
- Add the weekend’s weather from the town’s own gauge
Made-up data, made for this example. Any resemblance to a real town is a coincidence.
Your notebook is read on this device and never uploaded. Check it yourself
How this works: Method, 5 sources, Checked against 2 worked examples,
How this works
Method
The .ipynb file is read in the browser as the nbformat JSON it is: every cell, its last outputs and its attachments. Markdown is drawn with GitHub Flavored Markdown and maths with KaTeX, code is coloured by the notebook’s language, and each output shows its richest format, as JupyterLab chooses. All HTML from the notebook is sanitised first and nothing from other sites is loaded. PDF comes from the browser’s print engine; HTML, script and Markdown files are written in the page.
Sources
How it’s tested
2 worked examples for this page are checked by automated tests before every release: given the inputs, the tool must show the expected answer.
Privacy
On your device. Nothing you enter or open leaves this browser. About privacy labels
Changes
- First published: open a .ipynb file with its outputs, maths and colours, and save it as PDF, HTML, a script or Markdown.
A notebook as plain text
Markdown is how a notebook travels into places that aren’t Jupyter: a README on GitHub, a static docs site, a blog post, a note in Obsidian. The Markdown cells come across exactly as written, so headings, lists, links and maths between dollar signs stay the same, and every code cell becomes a fenced block tagged with the notebook’s language so it is highlighted wherever it lands.
Outputs follow their code. Printed text, results and tracebacks go in plain fenced blocks, with the terminal colour codes stripped. A pandas table goes in as HTML, which Markdown allows and GitHub draws as a table. Maths outputs become display maths between double dollar signs, and JSON outputs a json block.
Pictures are the one choice to make. jupyter nbconvert --to markdown writes each chart to its own file in a folder beside the .md, which you then have to keep together. This converter embeds them in the Markdown as data instead, so one file holds everything; VS Code, Obsidian, Typora and most static site generators show them. GitHub does not show embedded images, so for a README choose Leave pictures out and add the charts as files.
Interactive widgets and JavaScript charts can’t be written as Markdown, so they become a one-line note in italics.
Good to know
- nbconvert’s Markdown export writes pictures to a separate folder named after the notebook, {notebook_name}_files, by default. Source: nbconvert documentation: Configuration options
- In GitHub Flavored Markdown, the text after a code fence’s opening backticks is its info string, and its first word names the language for highlighting. Source: GitHub Flavored Markdown Spec
How to convert a Jupyter notebook to Markdown3 steps, all on your device
- Open the notebook. Drop the .ipynb file on the card or choose it. The Markdown appears below the notebook as you will get it.
- Choose what happens to pictures. Embed them in the file as data, so the .md is complete on its own, or leave them out for a light text file.
- Download or copy. Press Save .md, or copy the Markdown and paste it where it is going.
Frequently Asked Questions
Will the Markdown work on GitHub?
The text, code and tables will. GitHub doesn’t show images embedded as data, so for a README choose Leave pictures out and commit the charts as image files.
Is the maths kept?
Yes. Maths in Markdown cells is copied as written, and maths outputs become display maths between double dollar signs. GitHub, Obsidian, Jupyter Book and many static site generators draw them.
Can I leave the outputs out?
Yes. Turn off Include outputs and the file holds the Markdown and the code alone. Folding outputs on the page only changes the view, not the file.
Why do my tables appear as HTML?
pandas writes its tables as HTML, and Markdown allows HTML, so they are kept as they are rather than rebuilt. Scripts and styles in them are removed first.
“Jupyter” is a trademark of Project Jupyter, used here only to say which files this viewer opens. Binary Decimal is not affiliated with or endorsed by Project Jupyter.