Skip to main content
rule

seaborn — Cursor rule: plotting guidance and installation

This rule guides Seaborn charts in Python, from data shape and styling to plotting large datasets. It fits projects that need consistent, reproducible visualizations.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/seaborn.mdc
Open on GitHub

What seaborn does

The rule targets **/*.py files. It recommends keeping shared plot settings in styles.py, using figure-level functions for faceted layouts, and shaping data in long form, with one observation per row. Functions that create plots should also include type hints.

For large datasets, the guidance favors sampling or aggregation and warns that too many hue categories can clutter legends and slow rendering. If pandas becomes a processing bottleneck, it suggests considering polars or duckdb. Reproducibility guidance covers virtual environments and pinned dependencies in requirements.txt; critical charts can be checked with visual regression tests. The examples use seaborn, matplotlib, pandas, and numpy, so account for those libraries in the project environment.

What it helps with

  • centralize chart styling in styles.py
  • reshape data into long form for Seaborn
  • create faceted layouts with figure-level functions
  • reduce overplotting in large datasets
  • catch unintended chart changes with visual tests

Example prompts for the agent

  • Create a Seaborn chart with facets for category and color groups from group.
  • Reshape this data into long form and plot it as a bar chart.
  • Add type hints to the histogram function and group the plot with hue_col.

How to install seaborn

Manually: put the files in place

.cursor/rules/seaborn.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about seaborn

How do I install the seaborn rule in Cursor?

Add the rule file to your project at .cursor/rules/seaborn.mdc.

What does the seaborn rule do?

It sets guidance for Seaborn chart styling, data layout, figure composition, and plotting large datasets. It also covers dependency pinning and visual regression tests.

Which libraries appear in the seaborn examples?

The examples use seaborn, matplotlib, pandas, and numpy. The rule recommends pinning dependency versions in requirements.txt.

Source text

seaborn.mdc · CC0-1.0
---
description: This guide defines best practices for using Seaborn in Python for data visualization, emphasizing modern, reproducible, and performant approaches in AI/ML pipelines.
globs: **/*.py
---
# seaborn Best Practices

Seaborn is the definitive library for statistical data visualization in Python. This guide outlines our team's mandatory best practices to ensure consistent, reproducible, and high-quality plots in all AI/ML projects.

## 1. Code Organization & Project-Wide Styling

Always configure Seaborn's global style settings once per project. Centralize this in a dedicated `styles.py` module and import it. This guarantees a uniform visual language across all outputs.

❌ BAD: Ad-hoc styling in every script/notebook
```python
# my_script.py
import seaborn as sns
import matplotlib.pyplot as plt

sns.set_theme(style="darkgrid", palette="viridis") # In every file!
# ... plotting code
```

✅ GOOD: Centralized and imported styling
```python
# styles.py
import seaborn as sns
import matplotlib.pyplot as plt

def apply_seaborn_defaults():
    """Applies project-wide Seaborn and Matplotlib styling."""
    sns.set_theme(
        context="talk",      # Readable text for presentations/reports
        style="whitegrid",   # Clean background with light grid
        palette="deep"       # Perceptually uniform, colorblind-friendly
    )
    plt.rcParams["figure.figsize"] = (8, 5)    # Consistent figure size
    plt.rcParams["figure.dpi"] = 150           # On-screen clarity

# my_script.py
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from .styles import apply_seaborn_defaults # Adjust import path as needed

apply_seaborn_defaults()
# ... plotting code
```

## 2. Prefer Figure-Level Functions

For complex layouts, especially with faceting, always use Seaborn's figure-level functions (`relplot`, `catplot`, `pairplot`, `lmplot`). They handle figure creation, axis management, and legends automatically, ensuring consistency.

❌ BAD: Manual `subplots` with axes-level functions for faceting
```python
# Hard to manage multiple subplots, shared axes, and legends manually
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
sns.scatterplot(data=df[df['category'] == 'A'], x='x', y='y', ax=axes[0])
sns.scatterplot(data=df[df['category'] == 'B'], x='x', y='y', ax=axes[1])
plt.tight_layout()
plt.show()
```

✅ GOOD: `relplot` for automatic faceting and consistent sizing
```python
import pandas as pd
import numpy as np

# Example data
data = {
    'x': np.random.rand(100),
    'y': np.random.rand(100) + np.random.rand(100),
    'category': np.random.choice(['A', 'B'], 100),
    'group': np.random.choice(['X', 'Y'], 100)
}
df = pd.DataFrame(data)

g = sns.relplot(
    data=df, x='x', y='y',
    col='category', # Facet by category
    hue='group',    # Color by group
    kind='scatter',
    height=4, aspect=1.2
)
g.set_axis_labels("Feature X", "Feature Y")
g.set_titles("Category: {col_name}")
plt.tight_layout()
plt.show()
```

## 3. Data Format: Long-Form is King

Seaborn thrives on "tidy" or "long-form" data. Ensure your Pandas DataFrames are structured with one observation per row and variables as columns. This allows Seaborn to infer groupings automatically, reducing boilerplate.

❌ BAD: Wide-form data requiring `melt` or manual iteration
```python
# Data where 'value_A' and 'value_B' are separate columns for the same metric
wide_df = pd.DataFrame({
    'id': [1, 2], 'value_A': [10, 20], 'value_B': [15, 25]
})
# To plot, you'd need:
melted_df = wide_df.melt(id_vars=['id'], var_name='metric', value_name='value')
sns.barplot(data=melted_df, x='metric', y='value')
plt.show() # Added plt.show()
```

✅ GOOD: Data already in long-form
```python
long_df = pd.DataFrame({
    'id': [1, 2, 1, 2],
    'metric': ['value_A', 'value_A', 'value_B', 'value_B'],
    'value': [10, 20, 15, 25]
})
sns.barplot(data=long_df, x='metric', y='value')
plt.show()
```

## 4. Performance for Large Datasets

For datasets with millions of rows, plotting every point can be slow and create over-plotting. Optimize for clarity and speed.

*   **Down-sample**: Plot a representative subset for initial exploration.
*   **Binning/Aggregation**: Summarize data before plotting (e.g., `histplot` with `bins`, `kdeplot`, `lineplot` on aggregated data).
*   **Limit `hue`**: Excessive `hue` categories lead to cluttered legends and slow rendering.
*   **Efficient Data Loading**: Use `polars` or `duckdb` for faster initial data processing if `pandas` is a bottleneck.

```python
# Assume large_df is a very large DataFrame
# ✅ GOOD: Down-sampling for quick exploration
sampled_df = large_df.sample(n=100_000, random_state=42)
sns.scatterplot(data=sampled_df, x='feature1', y='feature2', alpha=0.5)
plt.show()

# ✅ GOOD: Aggregating for trends
daily_avg = large_df.groupby('date')['value'].mean().reset_index()
sns.lineplot(data=daily_avg, x='date', y='value')
plt.show()
```

## 5. Type Hints for Clarity

Always use explicit type hints for functions that generate plots. This improves code readability, maintainability, and enables static analysis.

```python
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from typing import Optional, Union

def plot_feature_distribution(
    df: pd.DataFrame,
    feature_col: str,
    hue_col: Optional[str] = None,
    title: str = "Feature Distribution"
) -> plt.Axes:
    """
    Generates a histogram and KDE plot for a given feature.

    Args:
        df: Input DataFrame.
        feature_col: Name of the column to plot.
        hue_col: Optional column for grouping.
        title: Plot title.

    Returns:
        The Matplotlib Axes object.
    """
    fig, ax = plt.subplots(figsize=(10, 6))
    sns.histplot(data=df, x=feature_col, hue=hue_col, kde=True, ax=ax)
    ax.set_title(title)
    plt.tight_layout()
    return ax

# Example usage
tips = sns.load_dataset("tips")
plot_feature_distribution(tips, "total_bill", hue_col="sex", title="Total Bill Distribution by Sex")
plt.show()
```

## 6. Reproducibility: Virtual Environments & Packaging

Ensure full reproducibility of your plotting environment.

*   **Virtual Environments**: Always use `venv` or `conda` environments.
*   **`requirements.txt`**: Pin *all* dependencies (e.g., `seaborn`, `matplotlib`, `pandas`, `numpy`) to exact versions using `pip freeze > requirements.txt`.

```bash
# In your project root
python -m venv .venv
source .venv/bin/activate
pip install seaborn==0.13.2 matplotlib==3.8.2 pandas==2.1.4 numpy==1.26.2
pip freeze > requirements.txt
```

## 7. Testing: Visual Regression

For critical visualizations in production pipelines, implement visual regression tests. Compare hashes of generated PNGs or use a dedicated library (e.g., `pytest-mpl`) to detect unintended aesthetic changes caused by library updates or code modifications.

```python
# Example (conceptual)
# test_plots.py
import hashlib
import os
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from your_project.styles import apply_seaborn_defaults # Adjust import

def generate_plot_image(filename: str):
    apply_seaborn_defaults()
    df = sns.load_dataset("iris")
    sns.scatterplot(data=df, x="sepal_length", y="sepal_width", hue="species")
    plt.savefig(filename, bbox_inches="tight")
    plt.close() # Crucial to close figures after saving

def test_iris_scatter_plot_regression():
    expected_image_path = "tests/baseline_iris_scatter.png"
    generated_image_path = "tests/temp_iris_scatter.png"

    generate_plot_image(generated_image_path)

    with open(expected_image_path, "rb") as f_expected:
        expected_hash = hashlib.md5(f_expected.read()).hexdigest()
    with open(generated_image_path, "rb") as f_generated:
        generated_hash = hashlib.md5(f_generated.read()).hexdigest()

    assert expected_hash == generated_hash, "Iris scatter plot has changed visually!"
    os.remove(generated_image_path) # Clean up
```

## 8. Documentation: Clear Docstrings

Document every plotting function with a clear docstring. Describe the data source, plot type, purpose, statistical assumptions, parameters, return values, and any side effects (e.g., `plt.show()`). This makes code audit-friendly and maintainable.

```python
# See example in Section 5 for a well-documented plotting function.
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

IDE rules catalog →

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor