Quarto Skill
Quarto is an open-source scientific and technical publishing system built on Pandoc. It renders computational documents (with Python, R, Julia code) to publication-quality output in multiple formats.
Best Practices (TL;DR)
- Markdown-First: Default to
format: gfm(GitHub-flavored markdown) for composability, portability, and archival - Dark Mode: Always use
auto-darkfilter with dual themes for HTML output (accessibility and modern UX) - Visual Expression: Use charts and formatted tables, NEVER raw data dumps (
df.head(),print(dict)) - LaTeX for Math: Use LaTeX notation for ALL mathematical expressions ($\alpha = 0.15$, not "alpha = 0.15")
- Professional Tables: Use LaTeX tables (booktabs) for PDF, Great Tables for HTML
- No TOC: Table of contents is usually noise - use clear section headings instead
- DOCX for Google: Use
--to docxfor easy upload to Google Docs (better than PDF) - Double Newlines: Markdown requires blank lines between paragraphs (single newline = same paragraph)
- Render on Demand: Generate PDF/HTML only when specifically needed for distribution, not by default
Visual Expression Philosophy
CRITICAL: Quarto documents are for COMMUNICATION, not raw data dumps.
Quarto outputs are static documents meant to convey insights to humans. Raw dataframes, print statements, and JSON blobs fail to communicate effectively.
Visual Hierarchy (Use in Order)
- Charts/Plots - For trends, distributions, comparisons, relationships
- Formatted Tables - For structured data with styling and context
- Formatted Metrics - For key numbers with context and formatting
- Raw Output - NEVER (not even for debugging - use separate analysis files)
Anti-Patterns: What NOT to Do
# ❌ BAD: Raw dataframe dump
df.head()
# ❌ BAD: Print statements
print(f"Total: {total}")
print(data_dict)
# ❌ BAD: Bare variable returning data structure
result # Returns raw dict/JSON
# ❌ BAD: DataFrame info without formatting
df.describe()
df.info()
❌ BAD: Plain text for mathematical notation
- The growth rate is alpha = 0.15 or 15%
- We calculated the mean mu = sum(xi)/n
- The correlation coefficient r = 0.85
❌ BAD: No table formatting
```python
print(df.head())
❌ BAD: Using asterisks for equations
- E = m * c^2
- y = beta0 + beta1 * x
### Good Patterns: Visual Communication
```python
# ✅ GOOD: Chart for trends
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 6))
df.groupby('date')['sales'].sum().plot(ax=ax, kind='line')
ax.set_title('Sales Trend Over Time')
ax.set_ylabel('Sales ($)')
plt.tight_layout()
plt.show()
# ✅ GOOD: Formatted table using Great Tables
from great_tables import GT
(GT(df.head(10))
.tab_header(title="Top 10 Sales Records")
.fmt_currency(columns="sales", currency="USD")
.fmt_date(columns="date", date_style="medium"))
# ✅ GOOD: Formatted table using pandas markdown
print(df.head(10).to_markdown(index=False, tablefmt='grid'))
# ✅ GOOD: Formatted metrics in markdown with LaTeX
from IPython.display import Markdown
Markdown(f"""
## Key Metrics
- **Total Sales**: ${total_sales:,.2f}
- **Average Order**: ${avg_order:,.2f}
- **Growth Rate**: $\\alpha = {growth_rate:.1%}$ (15% YoY)
- **Top Product**: {top_product}
### Statistical Summary
The linear regression model $y = \\beta_0 + \\beta_1 x + \\epsilon$ yielded:
- Slope: $\\hat{{\\beta_1}} = 3.2$ (SE = 0.4)
- $R^2 = 0.78$, indicating strong fit
""")
# ✅ GOOD: Mermaid diagram for relationships
Markdown('''
```mermaid
flowchart LR
A[Raw Data] --> B[Analysis]
B --> C[Insights]
C --> D[Decision]
''')
### Why Visual Expression Matters
- **Documents are for humans**: Show insights, not data structures
- **Static format**: No interactive exploration - must communicate clearly on first view
- **EPIST provenance**: Visualize fact→conclusion chains, not raw JSON
- **Professional output**: Charts and tables look polished in PDF/HTML/Word
- **Accessibility**: Visual hierarchy helps readers navigate content
- **Shareability**: Well-formatted outputs communicate without explanation
## Markdown Formatting Rules (CRITICAL)
**IMPORTANT: Markdown requires blank lines between paragraphs for proper rendering.**
### Line Breaks and Paragraphs
**The Problem:**
```markdown
❌ BAD: This will render as one long line
This text appears on a new line in the source
But it renders on the same line as above
Because there's no blank line between them
The Solution:
✅ GOOD: This renders as separate paragraphs
This text appears on its own line because there's a blank line above it.
Each paragraph needs a blank line before and after it.
Common Markdown Patterns
Paragraphs (need blank lines):
This is paragraph one.
This is paragraph two.
This is paragraph three.
Lists (no blank lines between items):
- Item 1
- Item 2
- Item 3
Multi-paragraph list items (blank lines within item):
- Item 1 with first paragraph
Item 1 continued with second paragraph (indented 2 spaces)
- Item 2 starts here
Headers (blank line before and after):
Previous paragraph ends here.
## Section Header
New paragraph starts here.
Code blocks (blank line before and after):
Previous paragraph ends here.
```python
print("code block")
```
New paragraph starts here.
Quarto-Specific Markdown
Python code output:
#| echo: false
from IPython.display import Markdown
# ❌ BAD: Single newline won't create paragraph break
Markdown("Line 1\nLine 2") # Renders as: Line 1 Line 2
# ✅ GOOD: Double newline creates paragraph break
Markdown("Line 1\n\nLine 2") # Renders as separate paragraphs
# ✅ GOOD: Use triple-quoted string with blank lines
Markdown("""
Paragraph one.
Paragraph two.
Paragraph three.
""")
F-strings in Markdown:
#| echo: false
from IPython.display import Markdown
# ✅ GOOD: Blank lines between paragraphs
Markdown(f"""
## Analysis Results
The growth rate is {growth_rate:.1%}.
This represents a significant increase over last quarter.
We recommend increasing inventory by {inventory_increase:,} units.
""")
Best Practices
✅ DO:
- Use blank lines between all paragraphs
- Use blank lines before and after headers
- Use blank lines before and after code blocks
- Use blank lines before and after tables
- Use triple-quoted strings for multi-line markdown in Python
❌ DON'T:
- Use single newlines and expect paragraph breaks
- Forget blank lines around headers or code blocks
- Mix single and double newlines inconsistently
- Use
\nin strings expecting paragraph breaks (use\n\n)
Testing Markdown Formatting
Quick test:
# Create test document
cat > test.qmd << 'EOF'
---
title: "Markdown Test"
format: gfm
---
Paragraph 1.
Paragraph 2.
## Header
Paragraph 3.
EOF
# Render and check output
quarto render test.qmd --to gfm
cat test.md
LLM Self-Reasoning with Quarto
IMPORTANT: Use Quarto to document your epistemological reasoning chain when solving complex problems.
Why LLMs Should Use Quarto for Thinking
Quarto provides structure for:
- Traceable reasoning: Facts → Hypotheses → Evidence → Conclusions
- Visual communication: Mermaid diagrams show reasoning flows
- Mathematical rigor: LaTeX for statistical analysis and equations
- Multi-format output: GFM for archival, HTML for viewing, DOCX for collaboration
- Provenance tracking: Integration with EPIST for fact management
- Reproducibility: Code + results + reasoning = complete story
When to Use /think Command
Use /think <problem> for:
- Complex debugging (multiple possible root causes)
- Architecture decisions (trade-offs to evaluate)
- Data analysis (statistical reasoning required)
- Performance investigations (hypotheses to test)
- Any problem requiring structured reasoning chain
Example:
/think Why is the login endpoint returning 500 errors intermittently?
Creates a Quarto document with:
- Facts observed (error logs, code inspection)
- Hypotheses (3+ possible explanations)
- Evidence analysis (what supports/contradicts each)
- Conclusions (with confidence levels)
- Recommendations (actionable next steps)
Template Structure for LLM Reasoning
---
title: "Analysis: [Problem]"
author: "Claude Code"
date: "2024-02-02"
format:
gfm: default
html:
theme:
dark: darkly
light: flatly
docx: default
filters:
- auto-dark
---
## Problem Statement
[Clear restatement]
## Facts Observed
- **Fact 1**: Error occurs 3% of requests (Source: logs/app.log:1234)
- **Fact 2**: Only affects POST /login endpoint (Source: nginx access logs)
- **Fact 3**: Database connection pool shows spikes (Source: monitoring dashboard)
## Assumptions
- **Assumption 1**: Database is the bottleneck (based on connection pool spikes)
- **Assumption 2**: Load balancer is healthy (not verified yet)
## Hypotheses
### Hypothesis 1: Database Connection Pool Exhaustion
**Evidence supporting:**
- Connection pool shows 100% utilization during errors
- Error timing correlates with traffic spikes
**Evidence contradicting:**
- Pool size is 50, max concurrent users is 30
**Confidence**: Medium
### Hypothesis 2: Race Condition in Session Creation
**Evidence supporting:**
- Error only on POST (writes), not GET (reads)
- Intermittent nature suggests timing issue
**Evidence contradicting:**
- No obvious concurrent writes in code review
**Confidence**: Low
## Analysis
```{python}
#| echo: false
import matplotlib.pyplot as plt
from IPython.display import Markdown
# Show correlation between errors and traffic
fig, ax = plt.subplots(figsize=(10, 6))
# ... visualization code ...
plt.show()
Markdown(f"""
Correlation between error rate and traffic: $\\rho = 0.89$ (strong positive)
This suggests the errors are load-dependent, supporting the connection pool
exhaustion hypothesis.
""")
Reasoning Flow
flowchart TD
F1[Fact: 3% error rate] --> H1[Hypothesis: Pool exhaustion]
F2[Fact: Pool at 100%] --> H1
H1 --> T1[Test: Increase pool size]
T1 --> C1[Conclusion: Errors reduced to 0.1%]
Conclusion
Primary Conclusion: Database connection pool exhaustion during traffic spikes
Confidence: High (based on correlation analysis and pool utilization metrics)
Dependencies: Assumes monitoring metrics are accurate
Recommendations
Increase connection pool size to 100 (High priority)
- Expected outcome: Eliminate 500 errors during spikes
Add connection pool monitoring alerts (Medium priority)
- Alert when utilization > 80%
Implement connection retry logic (Low priority)
- Graceful degradation during pool saturation
### Integration with EPIST
After creating a Quarto analysis, optionally save key facts/conclusions to EPIST:
```bash
# Add critical facts
epist add fact
# Title: Login endpoint has 3% error rate during spikes
# Source: analysis_2024-02-02.qmd
# Tags: [debugging, database, performance]
# Add conclusion
epist add conclusion
# Title: Connection pool exhaustion causes login errors
# Based on: facts/debugging/login_errors_*.md
# Confidence: high
This makes your reasoning persistent and searchable across sessions.
When to Use Quarto
Perfect for:
- LLM epistemological reasoning (use
/thinkcommand) - Static reports and documentation (no interactivity needed)
- Multi-format publishing (PDF + HTML + Word from single source)
- Scientific documents (equations, citations, cross-references)
- Presentations (RevealJS HTML slides, PowerPoint, Beamer PDF)
- Websites and blogs (multi-page projects)
- EPIST provenance in static reports
- Exporting marimo/Jupyter notebooks for publication
NOT for:
- Interactive dashboards (use marimo
runmode or Shiny) - Reactive exploration during development (use marimo
editmode) - Real-time data updates (use marimo or web dashboards)
- When you need widgets/sliders for end users (use marimo WASM export)
Decision Tree: Quarto vs Alternatives
Need user interactivity? (sliders, dropdowns, real-time updates)
├─ YES → Use marimo (edit/run mode or WASM export)
└─ NO → Static output needed
│
├─ Complex multi-page documentation site?
│ └─ YES → Use Quarto website/book projects
│
├─ Single analysis with code + results?
│ ├─ Pure Python required (run as script)?
│ │ └─ YES → marimo → export md → quarto render
│ └─ NO → Native Quarto .qmd files (recommended)
│
└─ Just formatting existing markdown?
└─ Use Quarto with plain .md files
Installation
Quarto is already installed (version 1.8.27).
Optional dependencies:
# TinyTeX for better PDF generation (LaTeX)
quarto install tinytex
# Chromium for PDF generation (alternative to LaTeX)
quarto install chromium
Current setup:
- ✅ Pandoc 3.6.3 (embedded)
- ✅ Chrome headless (system installation)
- ✅ Python 3.14.2 detected
- ✅ Jupyter installed (for Python code execution)
- ❌ TinyTeX not installed (optional)
Neovim Integration
quarto-nvim plugin is installed and configured.
Features:
- LSP support for
.qmdfiles (Python, bash, lua, html code chunks) - Syntax highlighting for code chunks
- Diagnostics and completion in code cells
- Live preview with
:QuartoPreview
Keybindings:
<leader>qp- Preview current document (live reload)<leader>qc- Close preview<leader>qm- Render to markdown (GFM) - RECOMMENDED default<leader>qh- Render to HTML<leader>qd- Render to PDF
Treesitter support: Quarto syntax highlighting requires treesitter parsers:
# In Neovim
:TSInstall markdown
:TSInstall markdown_inline
:TSInstall python
Otter.nvim integration:
The plugin uses otter.nvim for embedded language support in code chunks. This means you get full LSP features (completion, diagnostics, hover) for Python code inside .qmd files.
Basic Usage
Quick Start: Native .qmd Files
Create a Quarto document:
# Create analysis.qmd
cat > analysis.qmd << 'EOF'
---
title: "Sales Analysis Q4 2024"
author: "Your Name"
date: "2024-01-30"
format:
gfm: default # GitHub-flavored markdown (PRIMARY)
html:
theme:
dark: darkly # Dark mode (recommended)
light: flatly # Light mode fallback
code-fold: true
toc: true
filters:
- auto-dark # Respect system preference
---
## Overview
```{python}
#| echo: false
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import Markdown
# Load data
df = pd.read_csv("sales.csv")
total_sales = df['sales'].sum()
avg_daily = df.groupby('date')['sales'].sum().mean()
top_product = df.groupby('product')['sales'].sum().idxmax()
# Display key metrics
Markdown(f"""
### Key Metrics
- **Total Sales**: ${total_sales:,.2f}
- **Average Daily Sales**: ${avg_daily:,.2f}
- **Top Product**: {top_product}
- **Records Analyzed**: {len(df):,}
""")
Sales Trend Analysis
#| label: fig-sales-trend
#| fig-cap: "Daily sales trending upward in Q4 2024"
#| echo: false
fig, ax = plt.subplots(figsize=(10, 6))
daily_sales = df.groupby('date')['sales'].sum()
daily_sales.plot(ax=ax, kind='line', linewidth=2, color='#2E86AB')
ax.set_title('Sales Trend Over Time', fontsize=14, fontweight='bold')
ax.set_xlabel('Date')
ax.set_ylabel('Sales ($)')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Top Products
#| echo: false
# Format top 10 products as markdown table
top_products = (df.groupby('product')['sales']
.sum()
.sort_values(ascending=False)
.head(10)
.reset_index())
top_products.columns = ['Product', 'Total Sales']
top_products['Total Sales'] = top_products['Total Sales'].apply(lambda x: f"${x:,.2f}")
print(top_products.to_markdown(index=False, tablefmt='grid'))
Statistical Analysis
#| echo: false
import numpy as np
# Calculate growth metrics
daily_sales = df.groupby('date')['sales'].sum()
growth_rate = (daily_sales.iloc[-1] - daily_sales.iloc[0]) / daily_sales.iloc[0]
avg_growth = daily_sales.pct_change().mean()
Markdown(f"""
The sales data exhibits a compound growth pattern modeled by:
$$
S(t) = S_0 \\times (1 + r)^t
$$
where $S_0$ represents initial sales, $r = {avg_growth:.3f}$ is the average daily growth rate,
and $t$ is time in days.
**Key Statistical Findings:**
- Overall Q4 growth: $\\Delta S = {growth_rate:.1%}$
- Average daily growth: $\\bar{{r}} = {avg_growth:.3%}$
- Standard deviation: $\\sigma = {daily_sales.std():,.2f}$
- Correlation with marketing spend: $\\rho = 0.82$ (strong positive)
These metrics indicate statistically significant growth ($p < 0.01$) with
consistent upward momentum throughout the quarter.
""")
Conclusion
Q4 2024 showed strong performance with total sales of ${total_sales:,.2f}. The upward trend in daily sales indicates positive momentum heading into the next quarter. EOF
Install auto-dark extension (one-time, if not already installed)
quarto add gadenbuie/quarto-auto-dark --no-prompt
Render to markdown (DEFAULT - composable, archival)
quarto render analysis.qmd --to gfm
Optional: Render to HTML with dark mode for web viewing
quarto render analysis.qmd --to html # Uses auto-dark theme
Optional: Render to other formats only when needed
quarto render analysis.qmd --to pdf # For printing/formal distribution quarto render analysis.qmd --to docx # For Word users
### File Formats Quarto Can Render
**Input formats:**
- `.qmd` - Quarto markdown (native, recommended)
- `.ipynb` - Jupyter notebooks
- `.md` - Plain markdown (no code execution)
- `.Rmd` - R Markdown files
**Output formats:**
- **Markdown**: `md` (plain), `gfm` (GitHub-flavored) - **PREFERRED default**
- **Documents**: PDF, HTML, Word, ODT, ePub, Typst
- **Presentations**: RevealJS (HTML), PowerPoint, Beamer (PDF)
- **Websites**: Multi-page sites, blogs, books
- **Dashboards**: Interactive dashboards (with Shiny or Observable JS)
## Core Commands
```bash
# Render document to markdown (DEFAULT - composable, text-based)
quarto render document.qmd --to md # Executable markdown with results
quarto render document.qmd --to gfm # GitHub-flavored markdown
# Render to other formats
quarto render document.qmd --to pdf # PDF document
quarto render document.qmd --to html # HTML (avoid --toc, use clear headings)
quarto render document.qmd --to docx # Word (for Google Docs upload)
# Render Jupyter notebook to markdown
quarto render notebook.ipynb --to md # Markdown with executed results
# Render plain markdown (no code execution)
quarto render README.md --to pdf
# Multiple formats at once
quarto render document.qmd --to md,pdf,html
# Preview with live reload
quarto preview document.qmd
# Create new project
quarto create project website mysite
quarto create project book mybook
# Publish
quarto publish gh-pages # GitHub Pages
quarto publish quarto-pub # Quarto Pub
quarto publish netlify # Netlify
Python Code Execution
Code Blocks
## Analysis Section
```{python}
import pandas as pd
df = pd.read_csv("data.csv")
df.head()
```
Code Block Options
```{python}
#| label: fig-sales
#| fig-cap: "Sales over time"
#| echo: false
#| warning: false
plt.figure(figsize=(10, 6))
df.plot(x='date', y='sales')
plt.show()
```
Common options:
echo: false- Hide code, show output onlycode-fold: true- Collapsible code blockswarning: false- Hide warningsmessage: false- Hide messageslabel: fig-name- Reference label for cross-referencesfig-cap: "Caption"- Figure caption
Inline Python Expressions
The total is `{python} f"${total:,.2f}"`.
There are `{python} len(df)` rows in the dataset.
Output Formatting
## Display Options
```{python}
#| output: asis
print("**Bold text** from code")
```
```{python}
#| output: false
# Code runs but output is hidden
result = expensive_calculation()
```
Table Formatting (CRITICAL)
NEVER use raw df.head() or bare dataframes. ALWAYS format tables for presentation.
Option 1: Great Tables (RECOMMENDED for rich formatting)
Installation:
uv add great-tables
Basic usage:
from great_tables import GT
# Simple formatted table
GT(df.head(10))
# With styling
(GT(df.head(10))
.tab_header(title="Sales Summary", subtitle="Q4 2024")
.fmt_currency(columns="sales", currency="USD")
.fmt_percent(columns="growth_rate", decimals=1)
.fmt_number(columns="quantity", decimals=0)
.fmt_date(columns="date", date_style="medium")
.tab_source_note("Source: Company Database"))
Advanced styling:
from great_tables import GT, loc, style
(GT(top_products)
.tab_header(title="Top 10 Products by Revenue")
.fmt_currency(columns="revenue", currency="USD")
.data_color(
columns="revenue",
palette=["lightblue", "darkblue"],
domain=[0, df['revenue'].max()]
)
.tab_style(
style=style.text(weight="bold"),
locations=loc.body(columns="product")
))
Option 2: pandas .to_markdown() (Simple, built-in)
For markdown output:
# Basic markdown table
print(df.head(10).to_markdown(index=False, tablefmt='grid'))
# With custom formatting
formatted_df = df.head(10).copy()
formatted_df['sales'] = formatted_df['sales'].apply(lambda x: f"${x:,.2f}")
formatted_df['date'] = pd.to_datetime(formatted_df['date']).dt.strftime('%Y-%m-%d')
print(formatted_df.to_markdown(index=False, tablefmt='pipe'))
Table format options:
'grid'- ASCII grid (best for markdown)'pipe'- GitHub-flavored markdown pipes'simple'- Simple spacing'html'- HTML table (for HTML output)
Option 3: tabulate (Flexible formatting)
Installation:
uv add tabulate
Usage:
from tabulate import tabulate
# Basic table
print(tabulate(df.head(10), headers='keys', tablefmt='grid', showindex=False))
# With custom formatting
print(tabulate(
df.head(10),
headers=['Product', 'Sales', 'Date'],
tablefmt='fancy_grid',
floatfmt='.2f',
showindex=False
))
Best Practices for Tables
✅ DO:
- Use Great Tables for HTML/PDF output (rich formatting)
- Use
.to_markdown()for markdown output (simplicity) - Format currency, percentages, dates before display
- Add titles, subtitles, and source notes
- Limit to top N rows (10-20 max) - don't dump entire dataset
- Apply color scales for numerical columns
- Bold headers and important columns
❌ DON'T:
- Use raw
df.head()without formatting - Display more than 20 rows in a table
- Show raw timestamps or unformatted numbers
- Include index column unless meaningful
- Use bare
print(df)statements
Table Formatting by Output Format
For GFM/Markdown:
# Use pandas .to_markdown() for simplicity
print(df.head(10).to_markdown(index=False, tablefmt='pipe'))
For HTML:
# Use Great Tables for rich styling
from great_tables import GT
GT(df.head(10)).fmt_currency(columns="sales")
For PDF:
# Use Great Tables or formatted markdown
# Great Tables renders well in PDF via LaTeX
GT(df.head(10))
Data Visualization Best Practices
Charts and diagrams should be your PRIMARY communication tool, not an afterthought.
When to Use Charts vs Tables
Use Charts for:
- Trends over time (line charts)
- Distributions (histograms, density plots)
- Comparisons across categories (bar charts)
- Proportions and composition (pie charts, stacked bars)
- Relationships between variables (scatter plots)
- Geographic data (maps, choropleth)
Use Tables for:
- Precise values needed (financial reports)
- Lookup reference (top N items)
- Multiple dimensions that don't visualize well
- Small datasets (< 20 rows)
Use Both:
- Chart for the trend, table for the details
- Chart for overview, table for drill-down
Chart Types by Use Case
Trends and Time Series:
import matplotlib.pyplot as plt
# Line chart for trends
fig, ax = plt.subplots(figsize=(10, 6))
df.groupby('date')['sales'].sum().plot(ax=ax, kind='line', linewidth=2)
ax.set_title('Sales Trend Over Time', fontsize=14, fontweight='bold')
ax.set_ylabel('Sales ($)')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Comparisons:
# Bar chart for category comparisons
fig, ax = plt.subplots(figsize=(10, 6))
top_products = df.groupby('product')['sales'].sum().nlargest(10)
top_products.plot(ax=ax, kind='barh', color='#2E86AB')
ax.set_title('Top 10 Products by Sales', fontsize=14, fontweight='bold')
ax.set_xlabel('Sales ($)')
plt.tight_layout()
plt.show()
Distributions:
# Histogram for distributions
fig, ax = plt.subplots(figsize=(10, 6))
df['order_value'].hist(bins=30, ax=ax, color='#A23B72', edgecolor='black')
ax.set_title('Order Value Distribution', fontsize=14, fontweight='bold')
ax.set_xlabel('Order Value ($)')
ax.set_ylabel('Frequency')
plt.tight_layout()
plt.show()
Proportions:
# Pie chart for proportions (use sparingly)
fig, ax = plt.subplots(figsize=(8, 8))
category_sales = df.groupby('category')['sales'].sum()
ax.pie(category_sales, labels=category_sales.index, autopct='%1.1f%%', startangle=90)
ax.set_title('Sales by Category', fontsize=14, fontweight='bold')
plt.show()
Relationships:
# Scatter plot for correlations
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(df['marketing_spend'], df['sales'], alpha=0.5, color='#F18F01')
ax.set_title('Marketing Spend vs Sales', fontsize=14, fontweight='bold')
ax.set_xlabel('Marketing Spend ($)')
ax.set_ylabel('Sales ($)')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Styling Best Practices
✅ DO:
- Use descriptive titles with context
- Label axes with units
- Use color purposefully (not just default colors)
- Add gridlines for readability (alpha=0.3)
- Set appropriate figure size (10x6 for landscape, 8x8 for square)
- Use
plt.tight_layout()to prevent label cutoff - Add legends when showing multiple series
- Use consistent color schemes across document
❌ DON'T:
- Use default ugly matplotlib colors
- Skip axis labels or titles
- Create tiny, unreadable charts
- Use 3D charts (hard to read accurately)
- Overload charts with too many series (> 5-7)
- Use pie charts for more than 5 categories
Mermaid Diagrams (Markdown Native)
For process flows, relationships, and system diagrams:
```{mermaid}
flowchart TD
A[Load Data] --> B[Clean Data]
B --> C[Analyze]
C --> D{Significant?}
D -->|Yes| E[Report Findings]
D -->|No| F[Collect More Data]
```
Common Mermaid diagram types:
flowchart- Process flows, decision treessequenceDiagram- Interaction sequencesclassDiagram- Object relationshipserDiagram- Entity-relationship diagramsgantt- Project timelinespie- Simple pie charts
Example: EPIST fact flow:
```{mermaid}
flowchart LR
F1[Fact: Q4 Sales = $2.1M] --> C1[Conclusion: Strong Quarter]
F2[Fact: YoY Growth = 15%] --> C1
F3[Fact: Top Product = Widget X] --> C2[Conclusion: Focus Marketing on Widgets]
```
Interactive Charts (HTML Output Only)
For HTML output, use Plotly for interactivity:
import plotly.express as px
# Interactive line chart
fig = px.line(df, x='date', y='sales', title='Sales Trend (Interactive)')
fig.update_layout(hovermode='x unified')
fig.show()
# Interactive scatter with hover
fig = px.scatter(df, x='marketing_spend', y='sales',
hover_data=['product', 'region'],
title='Marketing ROI Analysis')
fig.show()
Note: Plotly charts only work in HTML output. For PDF/Word, use matplotlib/seaborn.
Chart Selection Decision Tree
What are you showing?
├─ Change over time? → Line chart
├─ Compare categories? → Bar chart (vertical or horizontal)
├─ Show distribution? → Histogram or box plot
├─ Show composition? → Stacked bar or pie chart (if < 5 categories)
├─ Show relationship? → Scatter plot
├─ Show process/flow? → Mermaid flowchart
└─ Multiple variables? → Faceted plots or small multiples
Mathematical Notation with LaTeX (STRONGLY ENCOURAGED)
For any mathematical content, ALWAYS use LaTeX notation - it's professional and renders beautifully in all formats.
Inline Math
The equation $E = mc^2$ shows the relationship between energy and mass.
The growth rate is approximately $\alpha = 0.15$ or 15%.
We calculated the mean $\mu = \frac{\sum x_i}{n}$ from the dataset.
Display Math (Equations)
The quadratic formula is:
$$
x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$
The normal distribution probability density function:
$$
f(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}
$$
Aligned Equations
$$
\begin{aligned}
\text{Revenue} &= \text{Price} \times \text{Quantity} \\
&= \$50 \times 1000 \\
&= \$50{,}000
\end{aligned}
$$
Common Mathematical Expressions
Statistics:
- Mean: $\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i$
- Variance: $\sigma^2 = \frac{1}{n}\sum_{i=1}^{n} (x_i - \mu)^2$
- Standard deviation: $\sigma = \sqrt{\sigma^2}$
- Correlation: $\rho_{X,Y} = \frac{\text{cov}(X,Y)}{\sigma_X \sigma_Y}$
Finance:
- Compound interest: $A = P\left(1 + \frac{r}{n}\right)^{nt}$
- NPV: $NPV = \sum_{t=0}^{N} \frac{C_t}{(1+r)^t}$
- ROI: $ROI = \frac{\text{Gain} - \text{Cost}}{\text{Cost}} \times 100\%$
Linear regression:
$$
y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \epsilon
$$
where $\beta_0$ is the intercept, $\beta_i$ are coefficients, and $\epsilon \sim N(0, \sigma^2)$.
Numbered Equations (for cross-references)
Einstein's mass-energy equivalence:
$$
E = mc^2
$$ {#eq-einstein}
As shown in @eq-einstein, energy and mass are equivalent.
Mathematical Notation Best Practices
✅ DO:
- Use LaTeX for ALL mathematical expressions, even simple ones like percentages
- Use
\text{}for text within equations:$\text{Revenue} = \$1{,}000$ - Use proper notation:
\alpha, \beta, \mu, \sigma, \sum, \prod, \int - Number important equations for cross-referencing
- Use
alignedenvironment for multi-line equations - Format numbers properly:
\$1{,}000for currency with comma separators - Use
\timesfor multiplication: $5 \times 10$ - Use
\cdotfor dot product: $\vec{a} \cdot \vec{b}$
❌ DON'T:
- Write "alpha = 0.15" in plain text - use $\alpha = 0.15$
- Write "x^2" in plain text - use $x^2$
- Use asterisk for multiplication - use $\times$ or $\cdot$
- Mix LaTeX and plain text notation inconsistently
- Skip equation numbering for important formulas
Example: Statistical Report with LaTeX
## Regression Analysis
We fitted a linear model:
$$
\text{Sales} = \beta_0 + \beta_1 \times \text{Marketing Spend} + \epsilon
$$ {#eq-sales-model}
where $\epsilon \sim N(0, \sigma^2)$ represents random error.
### Results
The estimated parameters from @eq-sales-model are:
- Intercept: $\hat{\beta_0} = 50{,}000$ (SE = $2{,}500$)
- Slope: $\hat{\beta_1} = 3.2$ (SE = $0.4$)
- $R^2 = 0.78$
This indicates that each additional \$1 in marketing spend yields approximately \$3.20 in sales ($p < 0.001$).
LaTeX Tables (Professional Formatting)
For publication-quality tables in PDF output, use LaTeX table formatting alongside Great Tables.
Basic LaTeX Table
```{=latex}
\begin{table}[htbp]
\centering
\caption{Quarterly Sales Performance}
\label{tab:sales}
\begin{tabular}{lrrrr}
\hline
Quarter & Revenue (\$) & Growth (\%) & Units & Margin (\%) \\
\hline
Q1 2024 & 1,250,000 & 15.2 & 5,000 & 22.5 \\
Q2 2024 & 1,450,000 & 16.0 & 5,800 & 23.1 \\
Q3 2024 & 1,680,000 & 15.9 & 6,700 & 24.0 \\
Q4 2024 & 1,920,000 & 14.3 & 7,300 & 24.5 \\
\hline
\textbf{Total} & \textbf{6,300,000} & \textbf{15.4} & \textbf{24,800} & \textbf{23.5} \\
\hline
\end{tabular}
\end{table}
```
Enhanced LaTeX Table with booktabs (RECOMMENDED)
```{=latex}
\begin{table}[htbp]
\centering
\caption{Statistical Summary of Key Metrics}
\label{tab:statistics}
\begin{tabular}{lcccc}
\toprule
Metric & Mean & SD & Min & Max \\
\midrule
Sales (\$) & 125{,}000 & 25{,}000 & 75{,}000 & 200{,}000 \\
Orders & 500 & 120 & 300 & 750 \\
AOV (\$) & 250 & 45 & 180 & 380 \\
Churn (\%) & 12.5 & 3.2 & 8.0 & 18.5 \\
\bottomrule
\end{tabular}
\end{table}
```
booktabs provides professional-looking horizontal rules (better than \hline).
Add to YAML frontmatter for booktabs:
header-includes:
- \usepackage{booktabs}
Python-Generated LaTeX Tables
Option 1: Great Tables with LaTeX output
from great_tables import GT
# Great Tables can export to LaTeX
table = GT(df.head(10))
table.save("table.tex", format="latex")
Option 2: pandas to_latex() with styling
import pandas as pd
# Format DataFrame for LaTeX
df_formatted = df.head(10).copy()
df_formatted['Sales'] = df_formatted['Sales'].apply(lambda x: f"\\${x:,.0f}")
df_formatted['Growth'] = df_formatted['Growth'].apply(lambda x: f"{x:.1f}\\%")
# Export to LaTeX with booktabs
latex_table = df_formatted.to_latex(
index=False,
caption="Top 10 Products by Sales",
label="tab:top-products",
position="htbp",
column_format="lrrr",
escape=False, # Don't escape $ and %
formatters={
'Sales': lambda x: f"\\${x:,.0f}",
'Growth': lambda x: f"{x:.1f}\\%"
}
)
print(latex_table)
Option 3: tabulate with LaTeX output
from tabulate import tabulate
latex_table = tabulate(
df.head(10),
headers='keys',
tablefmt='latex_booktabs', # Use booktabs style
showindex=False,
floatfmt='.2f'
)
print(f"\\begin{{table}}[htbp]\n\\centering\n\\caption{{Sales Summary}}\n{latex_table}\n\\end{{table}}")
LaTeX Table Best Practices
✅ DO:
- Use
booktabspackage for professional horizontal rules (\toprule, \midrule, \bottomrule) - Add captions with
\caption{} - Add labels for cross-referencing with
\label{tab:name} - Use position specifiers:
[htbp](here, top, bottom, page) - Right-align numbers, left-align text:
{lrr}column format - Format numbers: Use thousand separators (1{,}000), proper decimal places
- Use
\textbf{}for bold text (totals, headers) - Center the table with
\centering
❌ DON'T:
- Use
\hline- use booktabs rules instead (\toprule, \midrule, \bottomrule) - Skip captions - tables should always be labeled
- Mix LaTeX and markdown tables in the same document
- Use vertical lines (
|) - they look unprofessional - Forget to escape special characters: $, %, &
Cross-Referencing LaTeX Tables
As shown in Table @tbl-sales, revenue increased across all quarters.
```{=latex}
\begin{table}[htbp]
\centering
\caption{Quarterly Revenue}
\label{tbl-sales}
...
\end{table}
Results from @tbl-sales indicate strong growth momentum.
#### When to Use LaTeX Tables vs Great Tables
**Use LaTeX tables when:**
- Creating PDF output with publication-quality typesetting
- Need precise control over table layout and spacing
- Working with complex multi-row/multi-column headers
- Creating tables for academic papers or formal reports
- Need to match specific journal formatting requirements
**Use Great Tables when:**
- Creating HTML output with interactive features
- Need quick table formatting without LaTeX complexity
- Working with markdown output
- Want consistent styling across HTML/PDF/Word formats
- Need color scales, data bars, or rich HTML styling
**Use both:**
```python
# Create table with Great Tables for HTML
gt_table = GT(df).fmt_currency(columns="sales")
gt_table # Displays in HTML
# Also export LaTeX version for PDF
df.to_latex(caption="Sales Summary", label="tab:sales")
YAML Frontmatter
Simple Document (Markdown Default)
---
title: "My Report"
author: "Your Name"
date: "2024-01-30"
format: gfm # GitHub-flavored markdown (default for composability)
---
Multiple Formats (Markdown + HTML with Dark Mode)
---
title: "Analysis Report"
format:
gfm:
toc: true
variant: +yaml_metadata_block
html:
theme:
dark: darkly # Dark mode (recommended)
light: flatly # Light mode fallback
toc: true
code-fold: true
code-tools: true
pdf:
toc: true
number-sections: true
geometry: margin=1in
filters:
- auto-dark # Auto-detect system preference
---
Markdown-Only Output
---
title: "Data Analysis"
format:
md:
output-file: "results.md"
variant: gfm # Use GitHub-flavored markdown
preserve-yaml: true
gfm:
output-file: "results-gfm.md"
---
Advanced PDF Options
---
title: "Technical Report"
format:
pdf:
documentclass: article
fontsize: 11pt
geometry:
- margin=1in
- paperwidth=8.5in
- paperheight=11in
toc: true
toc-depth: 2
number-sections: true
colorlinks: true
fig-pos: 'H'
include-in-header:
text: |
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[L]{My Company}
\fancyhead[R]{\thepage}
---
HTML Themes
PREFER auto-dark with dual themes (see next section) over single-theme HTML:
---
format:
html:
theme:
dark: darkly # RECOMMENDED: Dual theme with auto-dark
light: flatly
css: custom.css
toc: true
toc-location: left
code-fold: show
code-tools: true
filters:
- auto-dark # Respects user's system preference
---
Single theme (discouraged - doesn't respect user preference):
---
format:
html:
theme: darkly # Only use if auto-dark not available
css: custom.css
toc: true
toc-location: left
---
Dark Mode with Auto-Dark Extension (RECOMMENDED)
Dark mode is STRONGLY ENCOURAGED for all HTML output:
- Better accessibility and reduced eye strain
- Modern user expectation (most systems default to dark mode)
- Auto-dark extension respects user's system preference
Install auto-dark extension (one-time setup):
# In your project or home directory (applies to all projects)
quarto add gadenbuie/quarto-auto-dark --no-prompt
Use in document (RECOMMENDED default):
---
title: "My Analysis"
format:
html:
theme:
dark: darkly # Theme for dark mode (PRIMARY)
light: flatly # Theme for light mode (fallback)
filters:
- auto-dark # Automatically switch based on system preference
---
Best Practices:
- ✅ Always include auto-dark filter for HTML output
- ✅ Choose accessible dark theme (darkly, cyborg, slate)
- ✅ Test both dark and light modes if providing light fallback
- ⚠️ Single-theme HTML discouraged (use auto-dark instead)
Available dark themes:
darkly- Dark Bootstrap theme (RECOMMENDED - clean, professional)cyborg- Dark blue theme (good for technical docs)slate- Dark gray theme (subtle, minimal)solar- Dark solarized theme (warm, comfortable)superhero- Dark comic book theme (bold, high contrast)vapor- Dark retro theme (stylized)
Light themes (fallback only):
flatly- Clean modern theme (recommended fallback)cosmo- Friendly blue themelumen- Light gray themesandstone- Warm sandy thememinty- Fre
…(truncated)