Skill Tracer
Skills › Scientific Agent Skills › seaborn › patterns_and_troubleshooting.md

patterns_and_troubleshooting.md

156 lines · skills/seaborn/references/patterns_and_troubleshooting.md at 5f2bc03

Open on GitHub
1# Common Patterns and Troubleshooting
2
3Frequently needed plot recipes, then the errors seaborn most often raises and what they
4actually mean.
5

Common Patterns and Troubleshooting

Frequently needed plot recipes, then the errors seaborn most often raises and what they actually mean.

Raw lines 1 to 5
6## Common Patterns
7

Common Patterns

Raw lines 6 to 7
8### Exploratory Data Analysis
9
10```python
11# Quick overview of all relationships
12sns.pairplot(data=df, hue='target', corner=True)
14# Distribution exploration
15sns.displot(data=df, x='variable', hue='group',
16 kind='kde', fill=True, col='category')
18# Correlation analysis
19corr = df.select_dtypes(include='number').corr()
20sns.heatmap(corr, annot=True, cmap='coolwarm', center=0)
21```

Exploratory Data Analysis

# Quick overview of all relationships
sns.pairplot(data=df, hue='target', corner=True)

# Distribution exploration
sns.displot(data=df, x='variable', hue='group',
            kind='kde', fill=True, col='category')

# Correlation analysis
corr = df.select_dtypes(include='number').corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0)
Raw lines 8 to 22
23### Publication-Quality Figures
25```python
26sns.set_theme(style='ticks', context='paper', font_scale=1.1)
28g = sns.catplot(data=df, x='treatment', y='response',
29 col='cell_line', kind='box', height=3, aspect=1.2)
30g.set_axis_labels('Treatment Condition', 'Response (μM)')
31g.set_titles('{col_name}')
32sns.despine(trim=True)
34g.savefig('figure.pdf', dpi=300, bbox_inches='tight')
35```

Publication-Quality Figures

sns.set_theme(style='ticks', context='paper', font_scale=1.1)

g = sns.catplot(data=df, x='treatment', y='response',
                col='cell_line', kind='box', height=3, aspect=1.2)
g.set_axis_labels('Treatment Condition', 'Response (μM)')
g.set_titles('{col_name}')
sns.despine(trim=True)

g.savefig('figure.pdf', dpi=300, bbox_inches='tight')
Raw lines 23 to 36
37### Complex Multi-Panel Figures
39```python
40# Using matplotlib subplots with seaborn
41fig, axes = plt.subplots(2, 2, figsize=(12, 10))
43sns.scatterplot(data=df, x='x1', y='y', hue='group', ax=axes[0, 0])
44sns.histplot(data=df, x='x1', hue='group', ax=axes[0, 1])
45sns.violinplot(data=df, x='group', y='y', ax=axes[1, 0])
46sns.heatmap(df.pivot_table(values='y', index='x1', columns='x2'),
47 ax=axes[1, 1], cmap='viridis')
49plt.tight_layout()
50```

Complex Multi-Panel Figures

# Using matplotlib subplots with seaborn
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

sns.scatterplot(data=df, x='x1', y='y', hue='group', ax=axes[0, 0])
sns.histplot(data=df, x='x1', hue='group', ax=axes[0, 1])
sns.violinplot(data=df, x='group', y='y', ax=axes[1, 0])
sns.heatmap(df.pivot_table(values='y', index='x1', columns='x2'),
            ax=axes[1, 1], cmap='viridis')

plt.tight_layout()
Raw lines 37 to 51
52### Time Series with Confidence Bands
54```python
55# Lineplot aggregates; sd shows data spread, not a confidence interval
56sns.lineplot(data=timeseries, x='date', y='measurement',
57 hue='sensor', style='location', errorbar='sd')
59# For more control
60g = sns.relplot(data=timeseries, x='date', y='measurement',
61 col='location', hue='sensor', kind='line',
62 height=4, aspect=1.5, errorbar=('ci', 95))
63g.set_axis_labels('Date', 'Measurement (units)')
64```

Time Series with Confidence Bands

# Lineplot aggregates; sd shows data spread, not a confidence interval
sns.lineplot(data=timeseries, x='date', y='measurement',
             hue='sensor', style='location', errorbar='sd')

# For more control
g = sns.relplot(data=timeseries, x='date', y='measurement',
                col='location', hue='sensor', kind='line',
                height=4, aspect=1.5, errorbar=('ci', 95))
g.set_axis_labels('Date', 'Measurement (units)')
Raw lines 52 to 65
66## Troubleshooting

Troubleshooting

Raw lines 66 to 67
68### Issue: Legend Outside Plot Area
70Figure-level functions place legends outside by default. To move inside:
72```python
73g = sns.relplot(data=df, x='x', y='y', hue='category')
74sns.move_legend(g, "center right", bbox_to_anchor=(0.9, 0.5))
75```

Issue: Legend Outside Plot Area

Figure-level functions place legends outside by default. To move inside:

g = sns.relplot(data=df, x='x', y='y', hue='category')
sns.move_legend(g, "center right", bbox_to_anchor=(0.9, 0.5))
Raw lines 68 to 76
77### Issue: Overlapping Labels
79```python
80plt.xticks(rotation=45, ha='right')
81plt.tight_layout()
82```

Issue: Overlapping Labels

plt.xticks(rotation=45, ha='right')
plt.tight_layout()
Raw lines 77 to 83
84### Issue: Figure Too Small
86For figure-level functions:
87```python
88sns.relplot(data=df, x='x', y='y', height=6, aspect=1.5)
89```
91For axes-level functions:
92```python
93fig, ax = plt.subplots(figsize=(10, 6))
94sns.scatterplot(data=df, x='x', y='y', ax=ax)
95```

Issue: Figure Too Small

For figure-level functions:

sns.relplot(data=df, x='x', y='y', height=6, aspect=1.5)

For axes-level functions:

fig, ax = plt.subplots(figsize=(10, 6))
sns.scatterplot(data=df, x='x', y='y', ax=ax)
Raw lines 84 to 96
97### Issue: Colors Not Distinct Enough
99```python
100# Use a different palette
101sns.set_palette("bright")
103# Or specify number of colors
104palette = sns.color_palette("husl", n_colors=len(df['category'].unique()))
105sns.scatterplot(data=df, x='x', y='y', hue='category', palette=palette)
106```

Issue: Colors Not Distinct Enough

# Use a different palette
sns.set_palette("bright")

# Or specify number of colors
palette = sns.color_palette("husl", n_colors=len(df['category'].unique()))
sns.scatterplot(data=df, x='x', y='y', hue='category', palette=palette)
Raw lines 97 to 107
108### Issue: KDE Too Smooth or Jagged
110```python
111# Adjust bandwidth
112sns.kdeplot(data=df, x='x', bw_adjust=0.5) # Less smooth
113sns.kdeplot(data=df, x='x', bw_adjust=2) # More smooth
114```

Issue: KDE Too Smooth or Jagged

# Adjust bandwidth
sns.kdeplot(data=df, x='x', bw_adjust=0.5)  # Less smooth
sns.kdeplot(data=df, x='x', bw_adjust=2)    # More smooth
Raw lines 108 to 115
116### Preserve missing-observation gaps
118`lineplot` drops missing rows before drawing and can connect observations on either
119side. Reindex to the study's expected sampling schedule first when absent rows
120should also count as missing; do not assume every irregular interval is a gap.
121This offline example has one subject, one expected observation per integer time,
122and an explicit missing row. For several subjects, build runs independently
123within each subject (and condition) before assigning a unique segment ID.
125```python
126import numpy as np
127import pandas as pd
128import matplotlib.pyplot as plt
129import seaborn as sns
131series = pd.DataFrame({'time': [0, 1, 2, 3, 4], 'value': [1., 2., np.nan, 4., 5.]})
132valid = series['value'].notna()
133series['segment'] = (~valid).cumsum()
134fig, ax = plt.subplots()
135sns.lineplot(data=series.loc[valid], x='time', y='value', units='segment',
136 estimator=None, errorbar=None, marker='o', sort=True, ax=ax)
137ax.set(xlabel='Time', ylabel='Response', title='Gap retained at time 2')
138fig.savefig('missing-gap.png', dpi=150)
139```
141Segmenting is for raw observations. For aggregate trajectories and bands, compute
142the estimate and valid intervals with the intended sampling units, then split
143those output arrays at undefined or missing timepoints too. Never replace missing
144measurements with zero just to obtain a continuous curve.

Preserve missing-observation gaps

lineplot drops missing rows before drawing and can connect observations on either side. Reindex to the study's expected sampling schedule first when absent rows should also count as missing; do not assume every irregular interval is a gap. This offline example has one subject, one expected observation per integer time, and an explicit missing row. For several subjects, build runs independently within each subject (and condition) before assigning a unique segment ID.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

series = pd.DataFrame({'time': [0, 1, 2, 3, 4], 'value': [1., 2., np.nan, 4., 5.]})
valid = series['value'].notna()
series['segment'] = (~valid).cumsum()
fig, ax = plt.subplots()
sns.lineplot(data=series.loc[valid], x='time', y='value', units='segment',
             estimator=None, errorbar=None, marker='o', sort=True, ax=ax)
ax.set(xlabel='Time', ylabel='Response', title='Gap retained at time 2')
fig.savefig('missing-gap.png', dpi=150)

Segmenting is for raw observations. For aggregate trajectories and bands, compute the estimate and valid intervals with the intended sampling units, then split those output arrays at undefined or missing timepoints too. Never replace missing measurements with zero just to obtain a continuous curve.

Raw lines 116 to 145
146### Statistical units and normalization
148A CI at each x is not automatically a paired or longitudinal analysis. `units`
149with `estimator=None` draws individual lines; categorical `units` can request a
150multilevel bootstrap but does not fit a mixed model. Keep biological and technical
151replication separate and report group sizes. `common_norm=True` normalizes density
152across groups together; use `False` for each group's shape, and still disclose n.
154Reviewed against [lineplot](https://seaborn.pydata.org/generated/seaborn.lineplot.html),
155[error bars](https://seaborn.pydata.org/tutorial/error_bars.html), and the released
1560.13.2 drawing code; the gap recipe is checked through its actual line coordinates.

Statistical units and normalization

A CI at each x is not automatically a paired or longitudinal analysis. units with estimator=None draws individual lines; categorical units can request a multilevel bootstrap but does not fit a mixed model. Keep biological and technical replication separate and report group sizes. common_norm=True normalizes density across groups together; use False for each group's shape, and still disclose n.

Reviewed against lineplot, error bars, and the released 0.13.2 drawing code; the gap recipe is checked through its actual line coordinates.

Raw lines 146 to 156