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.

⚠️ Note: The code below must be run in a local Python environment.

1. What You Will Learn


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:

PYTHON
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]
})
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐)

PYTHON
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
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

100%
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]
TEXT 📖 Display only
> **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

TEXT 📖 Display only
> **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 ⭐⭐⭐)

PYTHON
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!")
TEXT 📖 Display only
> **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

Q Does Styler modify the data?
A No — Styler only affects the display; the data in the original DataFrame stays unchanged. df.style is a "view-layer" decoration that never touches the "data layer." After formatting, df['salary'] is still float64, not a string. You can safely create multiple differently styled Stylers from the same DataFrame.
Q How do I choose a cmap for background_gradient?
A If higher is better, use RdYlGn (red → green); if higher is worse, use YlOrRd (yellow → red); for diverging deviation, use coolwarm (blue → red); for sequential increase, use Blues. The principle for choosing a cmap: the color intuition should match the business meaning. Try the effect in Jupyter before deciding.
Q How do I specify a range with subset?
A Three ways: ① a list of column names 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.
Q Garbled Chinese characters in to_html?
A Specify the encoding: 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">.
Q Can styles be exported to Excel?
A Yes — 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.
Q What's the difference between applymap and map?
A In Pandas 2.x, Styler.applymap was renamed to Styler.map. The functionality is the same — applying a style function to each cell. apply works by row/column (the axis parameter), while map works by cell. In newer versions, just use map.
Q How do I chain multiple styles together?
A Styler methods return a Styler object, so chaining is naturally supported: 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


📝 Exercises

  1. 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.
  2. 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.
  3. 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 →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏