Pandas: Styled Output
Last updated: 2026-08-26
The final step of data analysis is presentation — letting the numbers speak for themselves. Pandas Styler turns a DataFrame into a colorful report: high sales in green, low in red, progress bars embedded inline, maximum values highlighted. No need to hand-pick colors in Excel — Pandas can do it all on its own.
1. What You Will Learn
- ❶ The Styler object
- ❷ format formatting
- ❸ background_gradient heatmap highlighting
- ❹ bar inline bar charts
- ❺ highlight conditional marking
2. Beautifying Carol's Sales Report
(1) The Pain Point: A Plain Numeric Report Is Not Intuitive Enough
Carol's monthly sales report is nothing but numbers, and her boss can't tell what's high and what's low:
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'West'],
'sales': [5200, 3100, 4800, 2900],
'profit': [1560, 620, 1440, 435],
'margin': [0.30, 0.20, 0.30, 0.15]
})
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
(2) The Solution: One-Line Beautification with Styler
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: Styler basics (difficulty ⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'West'],
'sales': [5200, 3100, 4800, 2900],
'profit': [1560, 620, 1440, 435],
'margin': [0.30, 0.20, 0.30, 0.15]
})
# Chain styling methods
styled = (df.style
.format({'sales': '${:,.0f}', 'profit': '${:,.0f}', 'margin': '{:.1%}'})
.background_gradient(subset=['sales'], cmap='RdYlGn')
.highlight_max(subset=['profit'], color='lightgreen')
)
# styled in Jupyter shows the styled table
# styled.to_html('report.html') for export
print("Styled table created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
3. The Styler Object
(1) df.style Returns a Styler
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: Styler basic operations (difficulty ⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'product': ['A', 'B', 'C', 'D', 'E'],
'price': [29.99, 99.99, 149.99, 499.99, 9.99],
'stock': [50, 120, 80, 35, 200],
'rating': [4.5, 4.2, 3.8, 4.8, 4.0]
})
# df.style returns Styler (not DataFrame!)
styler = df.style
print(type(styler)) # pandas.io.formats.style.Styler
# Chain multiple styles
result = (df.style
.format({'price': '${:.2f}', 'rating': '{:.1f}/5.0'})
.set_caption('Product Catalog')
.set_properties(**{'text-align': 'center'}, subset=['price', 'rating'])
)
# result renders in Jupyter
# Styler does NOT modify original DataFrame
print(df['price'].dtype) # still float64, unchanged
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
4. format Formatting
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: format numeric formatting (difficulty ⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'salary': [75000.50, 92000.75, 68000.25],
'bonus': [0.15, 0.20, 0.10],
'performance': [4.567, 3.891, 4.123]
})
styled = df.style.format({
'salary': '${:,.2f}', # $75,000.50
'bonus': '{:.0%}', # 15%
'performance': '{:.1f}/5.0' # 4.6/5.0
})
# Format all numeric columns
styled2 = df.style.format(precision=2) # 2 decimal places for all
# Thousands separator
styled3 = df.style.format({'salary': '{:,.0f}'}) # 75,001
# Custom formatter function
styled4 = df.style.format({
'salary': lambda x: f'${x:,.0f}'
})
print("Formatted tables created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
5. background_gradient Heatmap Highlighting
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: background_gradient heatmap (difficulty ⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'West'],
'Q1': [5200, 3100, 4800, 2900],
'Q2': [5800, 3400, 5100, 3100],
'Q3': [6100, 3800, 5600, 3500],
'Q4': [6500, 4100, 5900, 3800]
}).set_index('region')
# Heatmap: RdYlGn (red-yellow-green)
styled = df.style.background_gradient(cmap='RdYlGn')
# Subset: only specific columns
styled2 = df.style.background_gradient(
subset=['Q1', 'Q4'],
cmap='Blues',
vmin=2000, vmax=7000
)
# Per-column gradient (each column scaled independently)
styled3 = df.style.background_gradient(cmap='YlOrRd', axis=0)
# Per-row gradient (each row scaled independently)
styled4 = df.style.background_gradient(cmap='YlOrRd', axis=1)
print("Heatmap styles created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
(1) Commonly Used cmaps
| cmap | Effect | Use Case |
|---|---|---|
| RdYlGn | Red → Yellow → Green | Higher is better |
| RdYlBu | Red → Yellow → Blue | Diverging |
| Blues | Light → Dark blue | Sequential increase |
| YlOrRd | Yellow → Orange → Red | Higher is worse |
| coolwarm | Blue → Red | Deviation |
6. bar Inline Bar Charts
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: bar inline bars (difficulty ⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'product': ['A', 'B', 'C', 'D', 'E'],
'sales': [5200, 3100, 4800, 2900, 6500],
'target': [5000, 4000, 5000, 3000, 6000]
})
# Bar inside cells
styled = df.style.bar(
subset=['sales'],
color='lightblue',
vmin=0, vmax=7000
)
# Achievement: bar shows % of target
df['achievement'] = (df['sales'] / df['target'] * 100).round(1)
styled2 = df.style.bar(
subset=['achievement'],
color=['#ff6b6b', '#51cf66'], # red < 100%, green >= 100%
vmin=0, vmax=150
)
print("Bar chart styles created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
7. highlight Conditional Marking
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: highlight_max/min/null (difficulty ⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'product': ['A', 'B', 'C', 'D', 'E'],
'price': [29.99, 99.99, 149.99, 499.99, 9.99],
'stock': [50, 120, 80, 35, np.nan],
'rating': [4.5, 4.2, 3.8, 4.8, 4.0]
})
# Highlight max value per column
styled = df.style.highlight_max(
subset=['price', 'stock', 'rating'],
color='lightgreen'
)
# Highlight min value
styled2 = df.style.highlight_min(
subset=['price', 'rating'],
color='lightcoral'
)
# Highlight NaN values
styled3 = df.style.highlight_null(color='yellow')
# Custom: highlight low stock (< 40)
def highlight_low_stock(val):
color = 'red' if val < 40 else ''
return f'background-color: {color}'
styled4 = df.style.applymap(highlight_low_stock, subset=['stock'])
# Note: applymap renamed to map in Pandas 2.x
# styled4 = df.style.map(highlight_low_stock, subset=['stock'])
print("Highlight styles created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
8. Complete Example: Styling a Sales Report
(5) ▶ The Styling Chain Workflow
graph LR
A[df.style] --> B[format formatting]
B --> C[background_gradient heatmap]
C --> D[bar inline bar chart]
D --> E[highlight conditional marking]
E --> F[set_caption title]
F --> G[to_html / to_excel export]
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
▶ Example
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
: Comprehensive report styling (difficulty ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Sales report styling
# format → gradient → bar → highlight → export
# ============================================
np.random.seed(42)
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'West', 'Central'],
'revenue': [520000, 310000, 480000, 290000, 410000],
'cost': [364000, 248000, 336000, 246500, 307500],
'profit': [156000, 62000, 144000, 43500, 102500],
'margin': [0.30, 0.20, 0.30, 0.15, 0.25],
'growth': [0.12, -0.05, 0.08, -0.10, 0.15],
'customers': [1200, 850, 1100, 680, 950]
}).set_index('region')
# Multi-style chain
styled = (df.style
.format({
'revenue': '${:,.0f}',
'cost': '${:,.0f}',
'profit': '${:,.0f}',
'margin': '{:.1%}',
'growth': '{:+.1%}',
'customers': '{:,}'
})
.background_gradient(subset=['margin'], cmap='RdYlGn', vmin=0, vmax=0.4)
.bar(subset=['customers'], color='lightblue', vmin=0)
.highlight_max(subset=['profit'], color='lightgreen')
.highlight_min(subset=['profit'], color='lightcoral')
.set_caption('2024 Regional Sales Report')
.set_properties(**{'text-align': 'right'}, subset=['revenue', 'cost', 'profit'])
)
# Export
# styled.to_html('report.html')
# styled.to_excel('report.xlsx', engine='openpyxl')
print("Full styled report created!")
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas preinstalled, so please install it on your machine (`pip install pandas`) and follow along hands-on. Actual values may vary slightly depending on the pandas version.
❓ FAQ
subset=['sales', 'profit']; ② a slice subset=pd.IndexSlice[:, 'Q1':'Q4']; ③ a conditional filter subset=pd.IndexSlice[df['sales'] > 3000, :]. A list of column names is the most common.styled.to_html(encoding='utf-8'). If you're writing to a file, use with open('report.html', 'w', encoding='utf-8') as f: f.write(styled.to_html()). Make sure the HTML file declares <meta charset="utf-8">.styled.to_excel('report.xlsx', engine='openpyxl'). Colors and formatting are preserved in Excel. However, some styles may not match exactly (for example, inline bar charts become conditional formatting in Excel). You'll need pip install openpyxl.df.style.format({...}).background_gradient(...).bar(...).highlight_max(...). They are applied from top to bottom, and a later style overrides any conflicting earlier style. Recommended order: format → gradient → bar → highlight → set_caption.📖 Summary
- df.style returns a Styler object, which only affects the display and does not modify the data
- format formats numbers: currency (${:,.0f}) / percentage ({:.0%}) / precision (precision)
- background_gradient heatmap highlighting: cmap picks the colors, subset picks the range, axis controls the direction
- bar inline bar charts: color is a single color or two colors, vmin/vmax control the range
- highlight_max/min/null conditional highlighting, map/apply for custom conditions
- Chaining: format → gradient → bar → highlight → caption
- to_html/to_excel export reports with styles
📝 Exercises
- Basic (difficulty ⭐): Create a DataFrame with salary/bonus columns, use format to format them as currency and percentage, and use highlight_max to mark the highest salary.
- Intermediate (difficulty ⭐⭐): Create a quarterly sales DataFrame (rows = regions, columns = Q1-Q4), and combine a background_gradient(cmap='RdYlGn') heatmap + bar inline bars + format thousands-separator formatting.
- Challenge (difficulty ⭐⭐⭐): Simulate a sales report for 5 regions (revenue/cost/profit/margin/growth), and complete a 5-step styling chain: multi-format format → gradient(margin) → bar(customers) → highlight_max(profit) → to_html export.
← Previous Lesson: Performance Optimization · Next Lesson: Project - Data Analysis →