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.
.cursor/rules/seaborn.mdcWhat 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
categoryand color groups fromgroup. - 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.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/seaborn.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/seaborn.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/seaborn.mdManually: put the files in place
.clinerules/seaborn.mdStep 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