From b83db11abdc545e16eda617088ed00554e02e8f1 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Fri, 10 Apr 2026 14:09:39 +0300 Subject: [PATCH] Adds python support --- quarto/quarto-authoring/SKILL.md | 51 +++++- .../quarto-authoring/references/code-cells.md | 106 ++++++++++++ .../references/conversion-jupyter.md | 154 ++++++++++++++++++ quarto/quarto-authoring/references/figures.md | 104 +++++++++++- quarto/quarto-authoring/references/layout.md | 70 ++++++++ quarto/quarto-authoring/references/tables.md | 92 ++++++++++- .../references/yaml-front-matter.md | 43 ++++- 7 files changed, 610 insertions(+), 10 deletions(-) create mode 100644 quarto/quarto-authoring/references/conversion-jupyter.md diff --git a/quarto/quarto-authoring/SKILL.md b/quarto/quarto-authoring/SKILL.md index fe41707..ca4e61a 100644 --- a/quarto/quarto-authoring/SKILL.md +++ b/quarto/quarto-authoring/SKILL.md @@ -1,12 +1,11 @@ --- name: quarto-authoring description: > - Writing and authoring Quarto documents (.qmd), including code cell options, - figure and table captions, cross-references, callout blocks (notes, warnings, - tips), citations and bibliography, page layout and columns, Mermaid diagrams, - YAML metadata configuration, and Quarto extensions. Also covers converting and - migrating R Markdown (.Rmd), bookdown, blogdown, xaringan, and distill projects - to Quarto, and creating Quarto websites, books, presentations, and reports. + Writing and authoring Quarto documents (.qmd) with R (knitr) and Python (Jupyter) + engines. Covers code cells, figures, tables, cross-references, callouts, citations, + layout, Mermaid diagrams, YAML metadata, and extensions. Also covers migrating + R Markdown, bookdown, blogdown, xaringan, distill, and Jupyter notebooks to Quarto, + and creating websites, books, presentations, and reports. metadata: author: Mickaël Canouil (@mcanouil) version: "1.2" @@ -74,6 +73,7 @@ Only read the one matching the source format when the user explicitly asks to co - xaringan slides: [references/conversion-xaringan.md](references/conversion-xaringan.md) - distill article: [references/conversion-distill.md](references/conversion-distill.md) - blogdown site: [references/conversion-blogdown.md](references/conversion-blogdown.md) +- Jupyter notebook (.ipynb) to Quarto: [references/conversion-jupyter.md](references/conversion-jupyter.md) ## QMD Essentials @@ -124,6 +124,8 @@ Quarto uses the language's comment symbol + `|` for cell options. Options use ** - Mermaid: `%%|` - Graphviz/DOT: `//|` +R example (knitr engine): + ````markdown ```{r} #| label: fig-example @@ -134,6 +136,27 @@ plot(x, y) ``` ```` +Python example (Jupyter engine): + +````markdown +```{python} +#| label: fig-example +#| echo: false +#| fig-cap: "A scatter plot example." + +import matplotlib.pyplot as plt +plt.scatter(x, y) +plt.show() +``` +```` + +Quarto auto-detects the engine from the first executable cell. To force an engine, set in YAML: + +```yaml +engine: knitr # for R +engine: jupyter # for Python +``` + Common execution options: | Option | Description | Values | @@ -164,6 +187,8 @@ Labels must start with a type prefix. Reference with `@`: - Section: `sec-` prefix, e.g., `{#sec-intro}` → `@sec-intro` - Equation: `eq-` prefix, e.g., `{#eq-model}` → `@eq-model` +R: + ````markdown ```{r} #| label: fig-plot @@ -174,6 +199,20 @@ plot(1) See @fig-plot for the results. ```` +Python: + +````markdown +```{python} +#| label: fig-plot +#| fig-cap: "A caption for the plot." +import matplotlib.pyplot as plt +plt.plot([1]) +plt.show() +``` + +See @fig-plot for the results. +```` + Details: [references/cross-references.md](references/cross-references.md) ### Callout Blocks diff --git a/quarto/quarto-authoring/references/code-cells.md b/quarto/quarto-authoring/references/code-cells.md index 26e258e..deda571 100644 --- a/quarto/quarto-authoring/references/code-cells.md +++ b/quarto/quarto-authoring/references/code-cells.md @@ -68,6 +68,16 @@ library(ggplot2) ``` ```` +````markdown +```{python} +#| echo: false + +# This code runs but is not shown +import pandas as pd +import matplotlib.pyplot as plt +``` +```` + Show fenced code block with attributes: ````markdown @@ -96,6 +106,8 @@ Options for controlling figure output: ### Figure Example +R: + ````markdown ```{r} #| label: fig-analysis @@ -111,8 +123,26 @@ ggplot(data, aes(x, y)) + ``` ```` +Python: + +````markdown +```{python} +#| label: fig-analysis +#| fig-cap: "Analysis results showing the relationship between variables." +#| fig-alt: "Scatter plot with trend line showing positive correlation." +#| fig-width: 10 +#| fig-height: 6 +#| fig-align: center + +import seaborn as sns +sns.regplot(x=data["x"], y=data["y"]) +``` +```` + ### Multiple Figures +R: + ````markdown ```{r} #| label: fig-panels @@ -127,6 +157,25 @@ hist(y) ``` ```` +Python: + +````markdown +```{python} +#| label: fig-panels +#| fig-cap: "Multiple panel figure." +#| fig-subcap: +#| - "Distribution of X" +#| - "Distribution of Y" +#| layout-ncol: 2 + +import matplotlib.pyplot as plt +plt.hist(x) +plt.show() +plt.hist(y) +plt.show() +``` +```` + ## Table Options Options for controlling table output: @@ -140,6 +189,8 @@ Options for controlling table output: ### Table Example +R: + ````markdown ```{r} #| label: tbl-summary @@ -149,6 +200,29 @@ knitr::kable(summary_data) ``` ```` +Python (pandas — `output: asis` renders markdown table in all formats): + +````markdown +```{python} +#| label: tbl-summary +#| tbl-cap: "Summary statistics by group." +#| output: asis + +print(summary_df.to_markdown(index=False)) +``` +```` + +Python (pandas — plain `df` auto-displays as HTML table in HTML output): + +````markdown +```{python} +#| label: tbl-summary +#| tbl-cap: "Summary statistics by group." + +summary_df +``` +```` + ## Caching and Freeze Control caching of code cell results: @@ -161,6 +235,8 @@ Control caching of code cell results: ### Caching Example +R: + ````markdown ```{r} #| label: slow-computation @@ -171,6 +247,18 @@ result <- slow_function(data) ``` ```` +Python: + +````markdown +```{python} +#| label: slow-computation +#| cache: true + +# This expensive computation is cached +result = slow_function(data) +``` +```` + ### Project-Level Freeze In `_quarto.yml`: @@ -240,6 +328,8 @@ plot(1:10) Add annotations to explain code: +R: + ````markdown ```{r} #| code-annotations: hover @@ -255,6 +345,22 @@ mtcars |> # <1> 2. Filter to cars with MPG over 20 3. Select only the columns we need +Python: + +````markdown +```{python} +#| code-annotations: hover + +import pandas as pd # <1> +df = pd.read_csv("data.csv") # <2> +result = df[df["mpg"] > 20][["mpg", "cyl"]] # <3> +``` +```` + +1. Import pandas for data manipulation +2. Load the dataset from a CSV file +3. Filter rows and select columns of interest + Annotation styles: `hover`, `select`, `below`, `beside`. ## Filename Display diff --git a/quarto/quarto-authoring/references/conversion-jupyter.md b/quarto/quarto-authoring/references/conversion-jupyter.md new file mode 100644 index 0000000..03eb9f4 --- /dev/null +++ b/quarto/quarto-authoring/references/conversion-jupyter.md @@ -0,0 +1,154 @@ +# Converting Jupyter Notebooks to Quarto + +Quarto can render `.ipynb` files directly, or you can convert them to `.qmd` for better version control and editing. + +## Direct Rendering (No Conversion Required) + +Quarto renders `.ipynb` files as-is: + +```bash +quarto render notebook.ipynb +quarto render notebook.ipynb --to pdf +``` + +Cell outputs stored in the notebook are used unless `execute: enabled: true` forces re-execution. + +## Converting .ipynb to .qmd + +Use the Quarto CLI to convert: + +```bash +quarto convert notebook.ipynb +# produces notebook.qmd +``` + +This extracts cell source into `{python}` code blocks and converts markdown cells to prose. Cell outputs are discarded — Quarto will re-execute the code. + +## Key Differences: .ipynb vs .qmd + +| Feature | .ipynb | .qmd | +| ------- | ------ | ---- | +| Execution | Jupyter kernel | Quarto + Jupyter kernel | +| Cell options | Cell metadata JSON | `#|` hashpipe comments | +| Version control | JSON (noisy diffs) | Plain text (clean diffs) | +| Edit experience | Jupyter UI | Any text editor | +| Inline outputs | Stored in file | Re-computed on render | + +## Cell Option Migration + +Jupyter cell metadata becomes hashpipe options in .qmd: + +**Before (.ipynb cell metadata):** + +```json +{ + "tags": ["remove-input"], + "jupyter": { + "source_hidden": true + } +} +``` + +**After (.qmd):** + +```python +#| echo: false +``` + +### Common Metadata Mappings + +| Jupyter / nbconvert tag | Quarto hashpipe option | +| ----------------------- | ---------------------- | +| `remove-input` / `hide-input` | `#| echo: false` | +| `remove-output` / `hide-output` | `#| output: false` | +| `remove-cell` | `#| include: false` | +| `raises-exception` | `#| error: true` | + +## YAML Front Matter + +Add YAML front matter at the top of the .qmd (not needed in .ipynb, which uses notebook metadata): + +```yaml +--- +title: "My Analysis" +author: "Jane Doe" +date: today +format: html +execute: + echo: true + warning: false +jupyter: python3 +--- +``` + +For .ipynb, equivalent settings go in the notebook's metadata: + +```json +{ + "kernelspec": {"name": "python3", ...}, + "quarto": { + "title": "My Analysis", + "execute": {"echo": true} + } +} +``` + +## Specifying the Jupyter Kernel + +In .qmd, set the kernel in YAML: + +```yaml +jupyter: python3 # default Python 3 kernel +jupyter: ir # R kernel (IRkernel) +jupyter: myenv # a named venv/conda env kernel +``` + +Or use the `engine` key: + +```yaml +engine: jupyter +jupyter: python3 +``` + +## Controlling Re-execution + +By default Quarto re-executes all cells. To use stored outputs from the notebook: + +```yaml +execute: + enabled: false # use stored outputs, don't re-run +``` + +Or per-format: + +```yaml +format: + html: + execute: + enabled: false +``` + +## freeze for Collaborative Projects + +In `_quarto.yml`, freeze Jupyter outputs so they are only re-run when source changes: + +```yaml +execute: + freeze: auto +``` + +## Workflow Recommendation + +For new Python-focused Quarto documents, prefer `.qmd` over `.ipynb`: + +- Better Git diffs (no binary output blobs) +- Hashpipe options are cleaner than JSON cell metadata +- Works with any editor, not just Jupyter UI + +Use `.ipynb` when: the notebook is primarily interactive exploration, shared with non-Quarto users, or heavily uses Jupyter widgets. + +## Resources + +- [Quarto Jupyter Reference](https://quarto.org/docs/computations/jupyter.html) +- [Using Jupyter in Quarto](https://quarto.org/docs/tools/jupyter-lab.html) +- [Freeze / Caching](https://quarto.org/docs/projects/code-execution.html) diff --git a/quarto/quarto-authoring/references/figures.md b/quarto/quarto-authoring/references/figures.md index 80e2f45..dab3457 100644 --- a/quarto/quarto-authoring/references/figures.md +++ b/quarto/quarto-authoring/references/figures.md @@ -66,6 +66,8 @@ Alt text differs from caption - it describes the image content for accessibility Figures generated from code use hashpipe options: +### R Examples + ````markdown ```{r} #| label: fig-scatter @@ -79,21 +81,82 @@ plot(x, y) ``` ```` -### Python Example +````markdown +```{r} +#| label: fig-ggplot +#| fig-cap: "ggplot2 scatter with trend line." +#| fig-width: 8 +#| fig-height: 6 + +library(ggplot2) +ggplot(data, aes(x, y)) + + geom_point() + + geom_smooth(method = "lm") +``` +```` + +### Python Examples + +Matplotlib: ````markdown ```{python} #| label: fig-histogram #| fig-cap: "Distribution of values." -#| fig-width: 10 +#| fig-alt: "Histogram with 30 bins showing right-skewed distribution." +#| fig-width: 8 #| fig-height: 6 import matplotlib.pyplot as plt plt.hist(data, bins=30) +plt.xlabel("Value") +plt.ylabel("Count") plt.show() ``` ```` +Seaborn: + +````markdown +```{python} +#| label: fig-scatter +#| fig-cap: "Scatter plot with regression line." +#| fig-alt: "Scatter plot showing positive correlation between x and y." +#| fig-width: 8 +#| fig-height: 6 + +import seaborn as sns +sns.regplot(x=df["x"], y=df["y"], data=df) +``` +```` + +Plotnine (ggplot2 for Python): + +````markdown +```{python} +#| label: fig-plotnine +#| fig-cap: "ggplot-style scatter plot." +#| fig-width: 8 +#| fig-height: 6 + +from plotnine import ggplot, aes, geom_point, geom_smooth +(ggplot(df, aes("x", "y")) + geom_point() + geom_smooth(method="lm")) +``` +```` + +Plotly (interactive HTML figures): + +````markdown +```{python} +#| label: fig-interactive +#| fig-cap: "Interactive scatter plot." + +import plotly.express as px +fig = px.scatter(df, x="x", y="y", color="group") +fig.show() +``` +```` + ### Figure Options | Option | Description | Example | @@ -127,6 +190,8 @@ See @fig-comparison, particularly @fig-first. ### From Code +R: + ````markdown ```{r} #| label: fig-panels @@ -141,6 +206,25 @@ hist(y) ``` ```` +Python (each `plt.show()` call produces one subfigure): + +````markdown +```{python} +#| label: fig-panels +#| fig-cap: "Panel figure." +#| fig-subcap: +#| - "Distribution of X" +#| - "Distribution of Y" +#| layout-ncol: 2 + +import matplotlib.pyplot as plt +plt.hist(x) +plt.show() +plt.hist(y) +plt.show() +``` +```` + ## Figure Layouts For arranging multiple figures, use layout divs. See [layout.md](layout.md) for full layout options. @@ -188,6 +272,8 @@ fig-cap-location: top ### Per Figure +R: + ````markdown ```{r} #| label: fig-example @@ -198,6 +284,20 @@ plot(1:10) ``` ```` +Python: + +````markdown +```{python} +#| label: fig-example +#| fig-cap: "Caption on top." +#| fig-cap-location: top + +import matplotlib.pyplot as plt +plt.plot(range(10)) +plt.show() +``` +```` + Options: `top`, `bottom`, `margin`. ## Lightbox diff --git a/quarto/quarto-authoring/references/layout.md b/quarto/quarto-authoring/references/layout.md index 1ba3b0e..617543a 100644 --- a/quarto/quarto-authoring/references/layout.md +++ b/quarto/quarto-authoring/references/layout.md @@ -106,6 +106,8 @@ This appears in the right margin. ### Figures in Margin +R: + ````markdown ```{r} #| column: margin @@ -115,6 +117,19 @@ plot(1:10) ``` ```` +Python: + +````markdown +```{python} +#| column: margin +#| fig-cap: "Margin figure." + +import matplotlib.pyplot as plt +plt.plot(range(10)) +plt.show() +``` +```` + Or for markdown images: ````markdown @@ -125,6 +140,8 @@ Or for markdown images: ### Tables in Margin +R: + ````markdown ```{r} #| column: margin @@ -134,6 +151,18 @@ knitr::kable(small_data) ``` ```` +Python: + +````markdown +```{python} +#| column: margin +#| tbl-cap: "Margin table." +#| output: asis + +print(small_df.to_markdown(index=False)) +``` +```` + ### Mixed Content ````markdown @@ -152,6 +181,8 @@ Control output placement from code cells: ### Column Option +R: + ````markdown ```{r} #| column: page @@ -161,12 +192,26 @@ plot(1:100) ``` ```` +Python: + +````markdown +```{python} +#| column: page + +import matplotlib.pyplot as plt +plt.plot(range(100)) +plt.show() +``` +```` + Options: `body`, `body-outset`, `page`, `page-inset`, `screen`, `screen-inset`, `margin`. ### Figure Column Target figure outputs specifically: +R: + ````markdown ```{r} #| fig-column: margin @@ -175,10 +220,24 @@ plot(1:10) ``` ```` +Python: + +````markdown +```{python} +#| fig-column: margin + +import matplotlib.pyplot as plt +plt.plot(range(10)) +plt.show() +``` +```` + ### Table Column Target table outputs: +R: + ````markdown ```{r} #| tbl-column: page @@ -187,6 +246,17 @@ knitr::kable(wide_data) ``` ```` +Python: + +````markdown +```{python} +#| tbl-column: page +#| output: asis + +print(wide_df.to_markdown(index=False)) +``` +```` + ## Caption Location ### In Margin diff --git a/quarto/quarto-authoring/references/tables.md b/quarto/quarto-authoring/references/tables.md index d56fdb9..f72212a 100644 --- a/quarto/quarto-authoring/references/tables.md +++ b/quarto/quarto-authoring/references/tables.md @@ -201,13 +201,54 @@ gt(data) |> ### Python with pandas +Auto-display as HTML (HTML output only): + ````markdown ```{python} #| label: tbl-pandas #| tbl-cap: "Data summary." import pandas as pd -df.to_markdown() +summary_df +``` +```` + +Render as markdown table (works in HTML, PDF, and Word): + +````markdown +```{python} +#| label: tbl-pandas-md +#| tbl-cap: "Data summary." +#| output: asis + +import pandas as pd +print(summary_df.to_markdown(index=False)) +``` +```` + +### Python with polars + +````markdown +```{python} +#| label: tbl-polars +#| tbl-cap: "Polars data summary." +#| output: asis + +import polars as pl +df = pl.read_csv("data.csv") +print(df.to_pandas().to_markdown(index=False)) +``` +```` + +### Python with Great Tables (gt for Python) + +````markdown +```{python} +#| label: tbl-gt +#| tbl-cap: "Styled table." + +from great_tables import GT +GT(summary_df).tab_header(title="My Table") ``` ```` @@ -230,6 +271,8 @@ tbl-cap-location: top ### Per Table +R: + ````markdown ```{r} #| label: tbl-data @@ -240,6 +283,19 @@ knitr::kable(data) ``` ```` +Python: + +````markdown +```{python} +#| label: tbl-data +#| tbl-cap: "Data." +#| tbl-cap-location: bottom +#| output: asis + +print(df.to_markdown(index=False)) +``` +```` + Options: `top`, `bottom`, `margin`. ## Subtables @@ -275,6 +331,8 @@ See @tbl-panel, including @tbl-first. ### From Code +R: + ````markdown ```{r} #| label: tbl-multi @@ -289,6 +347,24 @@ knitr::kable(detail_df) ``` ```` +Python (each `print()` with `output: asis` produces one subtable): + +````markdown +```{python} +#| label: tbl-multi +#| tbl-cap: "Multiple tables." +#| tbl-subcap: +#| - "Summary" +#| - "Details" +#| layout-ncol: 2 +#| output: asis + +print(summary_df.to_markdown(index=False)) +print("\n\n") +print(detail_df.to_markdown(index=False)) +``` +```` + ## Bootstrap Styling (HTML) Add Bootstrap classes for styling: @@ -341,6 +417,8 @@ Same as figures: For tables spanning multiple pages (PDF): +R: + ````markdown ```{r} #| label: tbl-long @@ -350,6 +428,18 @@ knitr::kable(long_data, longtable = TRUE) ``` ```` +Python (use `output: asis` so the markdown table is recognized across pages): + +````markdown +```{python} +#| label: tbl-long +#| tbl-cap: "Long table." +#| output: asis + +print(long_df.to_markdown(index=False)) +``` +```` + ## Cross-Referencing Tables are referenced with `tbl-` prefix: diff --git a/quarto/quarto-authoring/references/yaml-front-matter.md b/quarto/quarto-authoring/references/yaml-front-matter.md index 7233c52..d28c024 100644 --- a/quarto/quarto-authoring/references/yaml-front-matter.md +++ b/quarto/quarto-authoring/references/yaml-front-matter.md @@ -218,6 +218,29 @@ format: progress: true ``` +## Execution Engine + +Quarto auto-detects the engine from the first executable cell. Override explicitly: + +```yaml +engine: knitr # R (knitr) +engine: jupyter # Python, Julia, or other Jupyter kernels +``` + +Specify the Jupyter kernel explicitly when needed: + +```yaml +engine: jupyter +jupyter: python3 +``` + +Or use a specific virtual environment: + +```yaml +engine: jupyter +jupyter: myenv # name of a venv/conda env with a registered kernel +``` + ## Execution Options ```yaml @@ -370,7 +393,7 @@ params: show_advanced: true ``` -Use in document: +Use in R: ````markdown ```{r} @@ -380,6 +403,24 @@ data <- read.csv(params$data_file) ``` ```` +Use in Python: + +````markdown +```{python} +#| label: read-data + +import pandas as pd +data = pd.read_csv(params["data_file"]) +threshold = params["threshold"] +``` +```` + +Render with custom parameters: + +```bash +quarto render report.qmd -P data_file:new_data.csv -P threshold:0.8 +``` + ## Include Files ```yaml