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

examples.md

866 lines · skills/seaborn/references/examples.md at 5f2bc03

Open on GitHub
1# Seaborn Common Use Cases and Examples
2
3Seaborn 0.13.2 recipes reviewed 2026-10-01. These are illustrative templates: supply the named DataFrames and adapt the sampling design before using the statistics. Corrected contracts were exercised with tiny synthetic fixtures; notebook widget display and arbitrary scientific datasets were not validated. Import NumPy, pandas, Matplotlib, and Seaborn as below before using later snippets.
4

Seaborn Common Use Cases and Examples

Seaborn 0.13.2 recipes reviewed 2026-10-01. These are illustrative templates: supply the named DataFrames and adapt the sampling design before using the statistics. Corrected contracts were exercised with tiny synthetic fixtures; notebook widget display and arbitrary scientific datasets were not validated. Import NumPy, pandas, Matplotlib, and Seaborn as below before using later snippets.

Raw lines 1 to 4
5## Exploratory Data Analysis
6

Exploratory Data Analysis

Raw lines 5 to 6
7### Quick Dataset Overview
8
9```python
10import numpy as np
11import seaborn as sns
12import matplotlib.pyplot as plt
13import pandas as pd
15# Load data
16df = pd.read_csv('data.csv')
18# Pairwise relationships for all numeric variables
19sns.pairplot(df, hue='target_variable', corner=True, diag_kind='kde')
20plt.suptitle('Dataset Overview', y=1.01)
21plt.savefig('overview.png', dpi=300, bbox_inches='tight')
22```

Quick Dataset Overview

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

# Load data
df = pd.read_csv('data.csv')

# Pairwise relationships for all numeric variables
sns.pairplot(df, hue='target_variable', corner=True, diag_kind='kde')
plt.suptitle('Dataset Overview', y=1.01)
plt.savefig('overview.png', dpi=300, bbox_inches='tight')
Raw lines 7 to 23
24### Distribution Exploration
26```python
27# Multiple distributions across categories
28g = sns.displot(
29 data=df,
30 x='measurement',
31 hue='condition',
32 col='timepoint',
33 kind='kde',
34 fill=True,
35 height=3,
36 aspect=1.5,
37 col_wrap=3,
38 common_norm=False
40g.set_axis_labels('Measurement Value', 'Density')
41g.set_titles('{col_name}')
42```

Distribution Exploration

# Multiple distributions across categories
g = sns.displot(
    data=df,
    x='measurement',
    hue='condition',
    col='timepoint',
    kind='kde',
    fill=True,
    height=3,
    aspect=1.5,
    col_wrap=3,
    common_norm=False
)
g.set_axis_labels('Measurement Value', 'Density')
g.set_titles('{col_name}')
Raw lines 24 to 43
44### Correlation Analysis
46```python
47# Compute correlation matrix
48corr = df.select_dtypes(include='number').corr()
50# Create mask for upper triangle
51mask = np.triu(np.ones_like(corr, dtype=bool))
53# Plot heatmap
54fig, ax = plt.subplots(figsize=(10, 8))
55sns.heatmap(
56 corr,
57 mask=mask,
58 annot=True,
59 fmt='.2f',
60 cmap='coolwarm',
61 center=0,
62 square=True,
63 linewidths=1,
64 cbar_kws={'shrink': 0.8}
66plt.title('Correlation Matrix')
67plt.tight_layout()
68```

Correlation Analysis

# Compute correlation matrix
corr = df.select_dtypes(include='number').corr()

# Create mask for upper triangle
mask = np.triu(np.ones_like(corr, dtype=bool))

# Plot heatmap
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
    corr,
    mask=mask,
    annot=True,
    fmt='.2f',
    cmap='coolwarm',
    center=0,
    square=True,
    linewidths=1,
    cbar_kws={'shrink': 0.8}
)
plt.title('Correlation Matrix')
plt.tight_layout()
Raw lines 44 to 69
70## Scientific Publications

Scientific Publications

Raw lines 70 to 71
72### Multi-Panel Figure with Different Plot Types
74```python
75# Set publication style
76sns.set_theme(style='ticks', context='paper', font_scale=1.1)
77sns.set_palette('colorblind')
79# Create figure with custom layout
80fig = plt.figure(figsize=(12, 8))
81gs = fig.add_gridspec(2, 3, hspace=0.3, wspace=0.3)
83# Panel A: Time series
84ax1 = fig.add_subplot(gs[0, :2])
85sns.lineplot(
86 data=timeseries_df,
87 x='time',
88 y='expression',
89 hue='gene',
90 style='treatment',
91 markers=True,
92 dashes=False,
93 ax=ax1
95ax1.set_title('A. Gene Expression Over Time', loc='left', fontweight='bold')
96ax1.set_xlabel('Time (hours)')
97ax1.set_ylabel('Expression Level (AU)')
99# Panel B: Distribution comparison
100ax2 = fig.add_subplot(gs[0, 2])
101sns.violinplot(
102 data=expression_df,
103 x='treatment',
104 y='expression',
105 inner='box',
106 ax=ax2
108ax2.set_title('B. Expression Distribution', loc='left', fontweight='bold')
109ax2.set_xlabel('Treatment')
110ax2.set_ylabel('')
112# Panel C: Correlation
113ax3 = fig.add_subplot(gs[1, 0])
114sns.scatterplot(
115 data=correlation_df,
116 x='gene1',
117 y='gene2',
118 hue='cell_type',
119 alpha=0.6,
120 ax=ax3
122sns.regplot(
123 data=correlation_df,
124 x='gene1',
125 y='gene2',
126 scatter=False,
127 color='black',
128 ax=ax3
130ax3.set_title('C. Gene Correlation', loc='left', fontweight='bold')
131ax3.set_xlabel('Gene 1 Expression')
132ax3.set_ylabel('Gene 2 Expression')
134# Panel D: Heatmap
135ax4 = fig.add_subplot(gs[1, 1:])
136sns.heatmap(
137 sample_matrix,
138 cmap='RdBu_r',
139 center=0,
140 annot=True,
141 fmt='.1f',
142 cbar_kws={'label': 'Log2 Fold Change'},
143 ax=ax4
145ax4.set_title('D. Treatment Effects', loc='left', fontweight='bold')
146ax4.set_xlabel('Sample')
147ax4.set_ylabel('Gene')
149# Clean up
150sns.despine()
151plt.savefig('figure.pdf', dpi=300, bbox_inches='tight')
152plt.savefig('figure.png', dpi=300, bbox_inches='tight')
153```

Multi-Panel Figure with Different Plot Types

# Set publication style
sns.set_theme(style='ticks', context='paper', font_scale=1.1)
sns.set_palette('colorblind')

# Create figure with custom layout
fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(2, 3, hspace=0.3, wspace=0.3)

# Panel A: Time series
ax1 = fig.add_subplot(gs[0, :2])
sns.lineplot(
    data=timeseries_df,
    x='time',
    y='expression',
    hue='gene',
    style='treatment',
    markers=True,
    dashes=False,
    ax=ax1
)
ax1.set_title('A. Gene Expression Over Time', loc='left', fontweight='bold')
ax1.set_xlabel('Time (hours)')
ax1.set_ylabel('Expression Level (AU)')

# Panel B: Distribution comparison
ax2 = fig.add_subplot(gs[0, 2])
sns.violinplot(
    data=expression_df,
    x='treatment',
    y='expression',
    inner='box',
    ax=ax2
)
ax2.set_title('B. Expression Distribution', loc='left', fontweight='bold')
ax2.set_xlabel('Treatment')
ax2.set_ylabel('')

# Panel C: Correlation
ax3 = fig.add_subplot(gs[1, 0])
sns.scatterplot(
    data=correlation_df,
    x='gene1',
    y='gene2',
    hue='cell_type',
    alpha=0.6,
    ax=ax3
)
sns.regplot(
    data=correlation_df,
    x='gene1',
    y='gene2',
    scatter=False,
    color='black',
    ax=ax3
)
ax3.set_title('C. Gene Correlation', loc='left', fontweight='bold')
ax3.set_xlabel('Gene 1 Expression')
ax3.set_ylabel('Gene 2 Expression')

# Panel D: Heatmap
ax4 = fig.add_subplot(gs[1, 1:])
sns.heatmap(
    sample_matrix,
    cmap='RdBu_r',
    center=0,
    annot=True,
    fmt='.1f',
    cbar_kws={'label': 'Log2 Fold Change'},
    ax=ax4
)
ax4.set_title('D. Treatment Effects', loc='left', fontweight='bold')
ax4.set_xlabel('Sample')
ax4.set_ylabel('Gene')

# Clean up
sns.despine()
plt.savefig('figure.pdf', dpi=300, bbox_inches='tight')
plt.savefig('figure.png', dpi=300, bbox_inches='tight')
Raw lines 72 to 154
155### Box Plot with Significance Annotations
157```python
158import numpy as np
159from scipy import stats
160from statsmodels.stats.multitest import multipletests
162# Assumes independent experimental units and two prespecified contrasts.
163# Paired or clustered designs need a corresponding analysis.
164comparisons = [('Control', 'High'), ('Control', 'Low')]
165raw_p = []
166for left, right in comparisons:
167 a = df.loc[df['treatment'].eq(left), 'response'].dropna()
168 b = df.loc[df['treatment'].eq(right), 'response'].dropna()
169 if min(len(a), len(b)) < 2 or not np.isfinite(np.r_[a, b]).all():
170 raise ValueError('Need at least two finite independent measurements per group')
171 raw_p.append(stats.ttest_ind(a, b, equal_var=False).pvalue)
172if not np.isfinite(raw_p).all():
173 raise ValueError('A contrast was undefined; do not label it nonsignificant')
174adjusted_p = multipletests(raw_p, method='holm')[1]
176# Create plot
177fig, ax = plt.subplots(figsize=(8, 6))
178sns.boxplot(
179 data=df,
180 x='treatment',
181 y='response',
182 hue='treatment',
183 order=['Control', 'Low', 'Medium', 'High'],
184 palette='Set2',
185 legend=False,
186 ax=ax
189# Add individual points
190sns.stripplot(
191 data=df,
192 x='treatment',
193 y='response',
194 order=['Control', 'Low', 'Medium', 'High'],
195 color='black',
196 alpha=0.3,
197 size=3,
198 ax=ax
201# Add significance bars
202def add_significance_bar(ax, x1, x2, y, h, text):
203 ax.plot([x1, x1, x2, x2], [y, y+h, y+h, y], 'k-', lw=1.5)
204 ax.text((x1+x2)/2, y+h, text, ha='center', va='bottom')
206y_max = df['response'].max()
207add_significance_bar(ax, 0, 3, y_max + 1, 0.5, f'Holm p={adjusted_p[0]:.3g}')
208add_significance_bar(ax, 0, 1, y_max + 3, 0.5, f'Holm p={adjusted_p[1]:.3g}')
210ax.set_ylabel('Response (μM)')
211ax.set_xlabel('Treatment Condition')
212ax.set_title('Treatment Response Analysis')
213sns.despine()
214```

Box Plot with Significance Annotations

import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests

# Assumes independent experimental units and two prespecified contrasts.
# Paired or clustered designs need a corresponding analysis.
comparisons = [('Control', 'High'), ('Control', 'Low')]
raw_p = []
for left, right in comparisons:
    a = df.loc[df['treatment'].eq(left), 'response'].dropna()
    b = df.loc[df['treatment'].eq(right), 'response'].dropna()
    if min(len(a), len(b)) < 2 or not np.isfinite(np.r_[a, b]).all():
        raise ValueError('Need at least two finite independent measurements per group')
    raw_p.append(stats.ttest_ind(a, b, equal_var=False).pvalue)
if not np.isfinite(raw_p).all():
    raise ValueError('A contrast was undefined; do not label it nonsignificant')
adjusted_p = multipletests(raw_p, method='holm')[1]

# Create plot
fig, ax = plt.subplots(figsize=(8, 6))
sns.boxplot(
    data=df,
    x='treatment',
    y='response',
    hue='treatment',
    order=['Control', 'Low', 'Medium', 'High'],
    palette='Set2',
    legend=False,
    ax=ax
)

# Add individual points
sns.stripplot(
    data=df,
    x='treatment',
    y='response',
    order=['Control', 'Low', 'Medium', 'High'],
    color='black',
    alpha=0.3,
    size=3,
    ax=ax
)

# Add significance bars
def add_significance_bar(ax, x1, x2, y, h, text):
    ax.plot([x1, x1, x2, x2], [y, y+h, y+h, y], 'k-', lw=1.5)
    ax.text((x1+x2)/2, y+h, text, ha='center', va='bottom')

y_max = df['response'].max()
add_significance_bar(ax, 0, 3, y_max + 1, 0.5, f'Holm p={adjusted_p[0]:.3g}')
add_significance_bar(ax, 0, 1, y_max + 3, 0.5, f'Holm p={adjusted_p[1]:.3g}')

ax.set_ylabel('Response (μM)')
ax.set_xlabel('Treatment Condition')
ax.set_title('Treatment Response Analysis')
sns.despine()
Raw lines 155 to 215
216## Time Series Analysis
218These aggregate examples assume independent replicates at each timestamp. Inspect missingness and replicate counts before plotting. `lineplot` bridges missing rows; use the explicit segment recipe in [patterns_and_troubleshooting.md](patterns_and_troubleshooting.md) for gaps. Autocorrelated samples are not independent replicates.

Time Series Analysis

These aggregate examples assume independent replicates at each timestamp. Inspect missingness and replicate counts before plotting. lineplot bridges missing rows; use the explicit segment recipe in patterns_and_troubleshooting.md for gaps. Autocorrelated samples are not independent replicates.

Raw lines 216 to 219
220### Multiple Time Series with Confidence Bands
222```python
223# Plot with automatic aggregation
224fig, ax = plt.subplots(figsize=(10, 6))
225sns.lineplot(
226 data=timeseries_df,
227 x='timestamp',
228 y='value',
229 hue='sensor',
230 style='location',
231 markers=True,
232 dashes=False,
233 errorbar=('ci', 95),
234 ax=ax
237# Customize
238ax.set_xlabel('Date')
239ax.set_ylabel('Measurement (units)')
240ax.set_title('Sensor Measurements Over Time')
241ax.legend(title='Sensor & Location', bbox_to_anchor=(1.05, 1), loc='upper left')
243# Format x-axis for dates
244import matplotlib.dates as mdates
245ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
246ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
247plt.xticks(rotation=45, ha='right')
249plt.tight_layout()
250```

Multiple Time Series with Confidence Bands

# Plot with automatic aggregation
fig, ax = plt.subplots(figsize=(10, 6))
sns.lineplot(
    data=timeseries_df,
    x='timestamp',
    y='value',
    hue='sensor',
    style='location',
    markers=True,
    dashes=False,
    errorbar=('ci', 95),
    ax=ax
)

# Customize
ax.set_xlabel('Date')
ax.set_ylabel('Measurement (units)')
ax.set_title('Sensor Measurements Over Time')
ax.legend(title='Sensor & Location', bbox_to_anchor=(1.05, 1), loc='upper left')

# Format x-axis for dates
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
plt.xticks(rotation=45, ha='right')

plt.tight_layout()
Raw lines 220 to 251
252### Faceted Time Series
254```python
255# Create faceted time series
256g = sns.relplot(
257 data=long_timeseries,
258 x='date',
259 y='measurement',
260 hue='device',
261 col='location',
262 row='metric',
263 kind='line',
264 height=3,
265 aspect=2,
266 errorbar='sd',
267 facet_kws={'sharex': True, 'sharey': False}
270# Customize facet titles
271g.set_titles('{row_name} - {col_name}')
272g.set_axis_labels('Date', 'Value')
274# Rotate x-axis labels
275for ax in g.axes.flat:
276 ax.tick_params(axis='x', rotation=45)
278g.tight_layout()
279```

Faceted Time Series

# Create faceted time series
g = sns.relplot(
    data=long_timeseries,
    x='date',
    y='measurement',
    hue='device',
    col='location',
    row='metric',
    kind='line',
    height=3,
    aspect=2,
    errorbar='sd',
    facet_kws={'sharex': True, 'sharey': False}
)

# Customize facet titles
g.set_titles('{row_name} - {col_name}')
g.set_axis_labels('Date', 'Value')

# Rotate x-axis labels
for ax in g.axes.flat:
    ax.tick_params(axis='x', rotation=45)

g.tight_layout()
Raw lines 252 to 280
281## Categorical Comparisons

Categorical Comparisons

Raw lines 281 to 282
283### Nested Categorical Variables
285```python
286# Create figure
287fig, axes = plt.subplots(1, 2, figsize=(14, 6))
289# Left panel: Grouped bar plot
290sns.barplot(
291 data=df,
292 x='category',
293 y='value',
294 hue='subcategory',
295 errorbar=('ci', 95),
296 capsize=0.1,
297 ax=axes[0]
299axes[0].set_title('Mean Values with 95% CI')
300axes[0].set_ylabel('Value (units)')
301axes[0].legend(title='Subcategory')
303# Right panel: Strip + violin plot
304sns.violinplot(
305 data=df,
306 x='category',
307 y='value',
308 hue='subcategory',
309 inner=None,
310 alpha=0.3,
311 legend=False,
312 ax=axes[1]
314sns.stripplot(
315 data=df,
316 x='category',
317 y='value',
318 hue='subcategory',
319 dodge=True,
320 size=3,
321 alpha=0.6,
322 legend=False,
323 ax=axes[1]
325axes[1].set_title('Distribution of Individual Values')
326axes[1].set_ylabel('')
328plt.tight_layout()
329```

Nested Categorical Variables

# Create figure
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# Left panel: Grouped bar plot
sns.barplot(
    data=df,
    x='category',
    y='value',
    hue='subcategory',
    errorbar=('ci', 95),
    capsize=0.1,
    ax=axes[0]
)
axes[0].set_title('Mean Values with 95% CI')
axes[0].set_ylabel('Value (units)')
axes[0].legend(title='Subcategory')

# Right panel: Strip + violin plot
sns.violinplot(
    data=df,
    x='category',
    y='value',
    hue='subcategory',
    inner=None,
    alpha=0.3,
    legend=False,
    ax=axes[1]
)
sns.stripplot(
    data=df,
    x='category',
    y='value',
    hue='subcategory',
    dodge=True,
    size=3,
    alpha=0.6,
    legend=False,
    ax=axes[1]
)
axes[1].set_title('Distribution of Individual Values')
axes[1].set_ylabel('')

plt.tight_layout()
Raw lines 283 to 330
331### Point Plot for Trends
333```python
334# Show how values change across categories
335sns.pointplot(
336 data=df,
337 x='timepoint',
338 y='score',
339 hue='treatment',
340 markers=['o', 's', '^'],
341 linestyles=['-', '--', '-.'],
342 dodge=0.3,
343 capsize=0.1,
344 errorbar=('ci', 95)
347plt.xlabel('Timepoint')
348plt.ylabel('Performance Score')
349plt.title('Treatment Effects Over Time')
350plt.legend(title='Treatment', bbox_to_anchor=(1.05, 1), loc='upper left')
351sns.despine()
352plt.tight_layout()
353```

Point Plot for Trends

# Show how values change across categories
sns.pointplot(
    data=df,
    x='timepoint',
    y='score',
    hue='treatment',
    markers=['o', 's', '^'],
    linestyles=['-', '--', '-.'],
    dodge=0.3,
    capsize=0.1,
    errorbar=('ci', 95)
)

plt.xlabel('Timepoint')
plt.ylabel('Performance Score')
plt.title('Treatment Effects Over Time')
plt.legend(title='Treatment', bbox_to_anchor=(1.05, 1), loc='upper left')
sns.despine()
plt.tight_layout()
Raw lines 331 to 354
355## Regression and Relationships

Regression and Relationships

Raw lines 355 to 356
357### Linear Regression with Facets
359```python
360# Fit separate regressions for each category
361g = sns.lmplot(
362 data=df,
363 x='predictor',
364 y='response',
365 hue='treatment',
366 col='cell_line',
367 height=4,
368 aspect=1.2,
369 scatter_kws={'alpha': 0.5, 's': 50},
370 ci=95,
371 palette='Set2'
374g.set_axis_labels('Predictor Variable', 'Response Variable')
375g.set_titles('{col_name}')
376g.tight_layout()
377```

Linear Regression with Facets

# Fit separate regressions for each category
g = sns.lmplot(
    data=df,
    x='predictor',
    y='response',
    hue='treatment',
    col='cell_line',
    height=4,
    aspect=1.2,
    scatter_kws={'alpha': 0.5, 's': 50},
    ci=95,
    palette='Set2'
)

g.set_axis_labels('Predictor Variable', 'Response Variable')
g.set_titles('{col_name}')
g.tight_layout()
Raw lines 357 to 378
379### Polynomial Regression
381```python
382fig, axes = plt.subplots(1, 3, figsize=(15, 5))
384for idx, order in enumerate([1, 2, 3]):
385 sns.regplot(
386 data=df,
387 x='x',
388 y='y',
389 order=order,
390 scatter_kws={'alpha': 0.5},
391 line_kws={'color': 'red'},
392 ci=95,
393 ax=axes[idx]
394 )
395 axes[idx].set_title(f'Order {order} Polynomial Fit')
396 axes[idx].set_xlabel('X Variable')
397 axes[idx].set_ylabel('Y Variable')
399plt.tight_layout()
400```

Polynomial Regression

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

for idx, order in enumerate([1, 2, 3]):
    sns.regplot(
        data=df,
        x='x',
        y='y',
        order=order,
        scatter_kws={'alpha': 0.5},
        line_kws={'color': 'red'},
        ci=95,
        ax=axes[idx]
    )
    axes[idx].set_title(f'Order {order} Polynomial Fit')
    axes[idx].set_xlabel('X Variable')
    axes[idx].set_ylabel('Y Variable')

plt.tight_layout()
Raw lines 379 to 401
402### Residual Analysis
404```python
405fig, axes = plt.subplots(2, 2, figsize=(12, 10))
407# Use identical complete finite observations in all diagnostics
408df = df.loc[np.isfinite(df[['x', 'y']]).all(axis=1)].copy()
409# Main regression
410sns.regplot(data=df, x='x', y='y', ax=axes[0, 0])
411axes[0, 0].set_title('Regression Fit')
413# Residuals vs predictor
414sns.residplot(data=df, x='x', y='y', lowess=True,
415 scatter_kws={'alpha': 0.5},
416 line_kws={'color': 'red', 'lw': 2},
417 ax=axes[0, 1])
418axes[0, 1].set_title('Residuals vs Predictor')
419axes[0, 1].axhline(0, ls='--', color='gray')
421# Q-Q plot (using scipy)
422from scipy import stats as sp_stats
423residuals = df['y'] - np.poly1d(np.polyfit(df['x'], df['y'], 1))(df['x'])
424sp_stats.probplot(residuals, dist="norm", plot=axes[1, 0])
425axes[1, 0].set_title('Q-Q Plot')
427# Histogram of residuals
428sns.histplot(residuals, kde=True, ax=axes[1, 1])
429axes[1, 1].set_title('Residual Distribution')
430axes[1, 1].set_xlabel('Residuals')
432plt.tight_layout()
433```

Residual Analysis

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# Use identical complete finite observations in all diagnostics
df = df.loc[np.isfinite(df[['x', 'y']]).all(axis=1)].copy()
# Main regression
sns.regplot(data=df, x='x', y='y', ax=axes[0, 0])
axes[0, 0].set_title('Regression Fit')

# Residuals vs predictor
sns.residplot(data=df, x='x', y='y', lowess=True,
              scatter_kws={'alpha': 0.5},
              line_kws={'color': 'red', 'lw': 2},
              ax=axes[0, 1])
axes[0, 1].set_title('Residuals vs Predictor')
axes[0, 1].axhline(0, ls='--', color='gray')

# Q-Q plot (using scipy)
from scipy import stats as sp_stats
residuals = df['y'] - np.poly1d(np.polyfit(df['x'], df['y'], 1))(df['x'])
sp_stats.probplot(residuals, dist="norm", plot=axes[1, 0])
axes[1, 0].set_title('Q-Q Plot')

# Histogram of residuals
sns.histplot(residuals, kde=True, ax=axes[1, 1])
axes[1, 1].set_title('Residual Distribution')
axes[1, 1].set_xlabel('Residuals')

plt.tight_layout()
Raw lines 402 to 434
435## Bivariate and Joint Distributions

Bivariate and Joint Distributions

Raw lines 435 to 436
437### Joint Plot with Multiple Representations
439```python
440# With hue, scatter jointplot uses KDE marginals (not histogram kwargs).
441g = sns.jointplot(
442 data=df,
443 x='var1',
444 y='var2',
445 hue='category',
446 kind='scatter',
447 height=8,
448 ratio=4,
449 space=0.1,
450 joint_kws={'alpha': 0.5, 's': 50},
451 marginal_kws={'fill': True, 'common_norm': False}
454# Add reference lines
455g.ax_joint.axline((0, 0), slope=1, color='r', ls='--', alpha=0.5, label='y=x')
456g.ax_joint.legend()
458g.set_axis_labels('Variable 1', 'Variable 2', fontsize=12)
459```

Joint Plot with Multiple Representations

# With hue, scatter jointplot uses KDE marginals (not histogram kwargs).
g = sns.jointplot(
    data=df,
    x='var1',
    y='var2',
    hue='category',
    kind='scatter',
    height=8,
    ratio=4,
    space=0.1,
    joint_kws={'alpha': 0.5, 's': 50},
    marginal_kws={'fill': True, 'common_norm': False}
)

# Add reference lines
g.ax_joint.axline((0, 0), slope=1, color='r', ls='--', alpha=0.5, label='y=x')
g.ax_joint.legend()

g.set_axis_labels('Variable 1', 'Variable 2', fontsize=12)
Raw lines 437 to 460
461### KDE Contour Plot
463```python
464fig, ax = plt.subplots(figsize=(8, 8))
466# Bivariate KDE with filled contours
467sns.kdeplot(
468 data=df,
469 x='x',
470 y='y',
471 fill=True,
472 levels=10,
473 cmap='viridis',
474 thresh=0.05,
475 ax=ax
478# Overlay scatter
479sns.scatterplot(
480 data=df,
481 x='x',
482 y='y',
483 color='white',
484 edgecolor='black',
485 s=50,
486 alpha=0.6,
487 ax=ax
490ax.set_xlabel('X Variable')
491ax.set_ylabel('Y Variable')
492ax.set_title('Bivariate Distribution')
493```

KDE Contour Plot

fig, ax = plt.subplots(figsize=(8, 8))

# Bivariate KDE with filled contours
sns.kdeplot(
    data=df,
    x='x',
    y='y',
    fill=True,
    levels=10,
    cmap='viridis',
    thresh=0.05,
    ax=ax
)

# Overlay scatter
sns.scatterplot(
    data=df,
    x='x',
    y='y',
    color='white',
    edgecolor='black',
    s=50,
    alpha=0.6,
    ax=ax
)

ax.set_xlabel('X Variable')
ax.set_ylabel('Y Variable')
ax.set_title('Bivariate Distribution')
Raw lines 461 to 494
495### Hexbin with Marginals
497```python
498# For large datasets
499g = sns.jointplot(
500 data=large_df,
501 x='x',
502 y='y',
503 kind='hex',
504 height=8,
505 ratio=5,
506 space=0.1,
507 joint_kws={'gridsize': 30, 'cmap': 'viridis'},
508 marginal_kws={'bins': 50, 'color': 'skyblue'}
511g.set_axis_labels('X Variable', 'Y Variable')
512```

Hexbin with Marginals

# For large datasets
g = sns.jointplot(
    data=large_df,
    x='x',
    y='y',
    kind='hex',
    height=8,
    ratio=5,
    space=0.1,
    joint_kws={'gridsize': 30, 'cmap': 'viridis'},
    marginal_kws={'bins': 50, 'color': 'skyblue'}
)

g.set_axis_labels('X Variable', 'Y Variable')
Raw lines 495 to 513
514## Matrix and Heatmap Visualizations

Matrix and Heatmap Visualizations

Raw lines 514 to 515
516### Hierarchical Clustering Heatmap
518```python
519# Prepare data (samples x features)
520data_matrix = df.set_index('sample_id')[feature_columns]
522# Create color annotations
523row_colors = df.set_index('sample_id')['condition'].map({
524 'control': '#1f77b4',
525 'treatment': '#ff7f0e'
526})
528col_colors = pd.Series(['#2ca02c' if 'gene' in col else '#d62728'
529 for col in data_matrix.columns], index=data_matrix.columns)
531# Scaling rows compares each sample's across-feature profile. Use z_score=1
532# only when standardizing each feature across samples matches the question.
533if not data_matrix.index.is_unique or not data_matrix.columns.is_unique:
534 raise ValueError('Sample and feature labels must be unique')
535if not np.isfinite(data_matrix.to_numpy()).all() or (data_matrix.std(axis=1) == 0).any():
536 raise ValueError('Clustering needs finite, nonconstant rows for z_score=0')
537if row_colors.isna().any() or col_colors.isna().any():
538 raise ValueError('Missing annotation color')
540# Plot
541g = sns.clustermap(
542 data_matrix,
543 method='ward',
544 metric='euclidean',
545 z_score=0, # Normalize rows
546 cmap='RdBu_r',
547 center=0,
548 row_colors=row_colors,
549 col_colors=col_colors,
550 figsize=(12, 10),
551 dendrogram_ratio=(0.1, 0.1),
552 cbar_pos=(0.02, 0.8, 0.03, 0.15),
553 linewidths=0.5
556g.ax_heatmap.set_xlabel('Features')
557g.ax_heatmap.set_ylabel('Samples')
558g.savefig('clustermap.png', dpi=300, bbox_inches='tight')
559```

Hierarchical Clustering Heatmap

# Prepare data (samples x features)
data_matrix = df.set_index('sample_id')[feature_columns]

# Create color annotations
row_colors = df.set_index('sample_id')['condition'].map({
    'control': '#1f77b4',
    'treatment': '#ff7f0e'
})

col_colors = pd.Series(['#2ca02c' if 'gene' in col else '#d62728'
                        for col in data_matrix.columns], index=data_matrix.columns)

# Scaling rows compares each sample's across-feature profile. Use z_score=1
# only when standardizing each feature across samples matches the question.
if not data_matrix.index.is_unique or not data_matrix.columns.is_unique:
    raise ValueError('Sample and feature labels must be unique')
if not np.isfinite(data_matrix.to_numpy()).all() or (data_matrix.std(axis=1) == 0).any():
    raise ValueError('Clustering needs finite, nonconstant rows for z_score=0')
if row_colors.isna().any() or col_colors.isna().any():
    raise ValueError('Missing annotation color')

# Plot
g = sns.clustermap(
    data_matrix,
    method='ward',
    metric='euclidean',
    z_score=0,  # Normalize rows
    cmap='RdBu_r',
    center=0,
    row_colors=row_colors,
    col_colors=col_colors,
    figsize=(12, 10),
    dendrogram_ratio=(0.1, 0.1),
    cbar_pos=(0.02, 0.8, 0.03, 0.15),
    linewidths=0.5
)

g.ax_heatmap.set_xlabel('Features')
g.ax_heatmap.set_ylabel('Samples')
g.savefig('clustermap.png', dpi=300, bbox_inches='tight')
Raw lines 516 to 560
561### Annotated Heatmap with Custom Colorbar
563```python
564# Pivot data for heatmap
565pivot_data = df.pivot(index='row_var', columns='col_var', values='value')
567# Create heatmap
568fig, ax = plt.subplots(figsize=(10, 8))
569sns.heatmap(
570 pivot_data,
571 annot=True,
572 fmt='.1f',
573 cmap='RdYlGn',
574 center=pivot_data.mean().mean(),
575 vmin=pivot_data.min().min(),
576 vmax=pivot_data.max().max(),
577 linewidths=0.5,
578 linecolor='gray',
579 cbar_kws={
580 'label': 'Value (units)',
581 'orientation': 'vertical',
582 'shrink': 0.8,
583 'aspect': 20
584 },
585 ax=ax
588ax.set_title('Variable Relationships', fontsize=14, pad=20)
589ax.set_xlabel('Column Variable', fontsize=12)
590ax.set_ylabel('Row Variable', fontsize=12)
592plt.xticks(rotation=45, ha='right')
593plt.yticks(rotation=0)
594plt.tight_layout()
595```

Annotated Heatmap with Custom Colorbar

# Pivot data for heatmap
pivot_data = df.pivot(index='row_var', columns='col_var', values='value')

# Create heatmap
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
    pivot_data,
    annot=True,
    fmt='.1f',
    cmap='RdYlGn',
    center=pivot_data.mean().mean(),
    vmin=pivot_data.min().min(),
    vmax=pivot_data.max().max(),
    linewidths=0.5,
    linecolor='gray',
    cbar_kws={
        'label': 'Value (units)',
        'orientation': 'vertical',
        'shrink': 0.8,
        'aspect': 20
    },
    ax=ax
)

ax.set_title('Variable Relationships', fontsize=14, pad=20)
ax.set_xlabel('Column Variable', fontsize=12)
ax.set_ylabel('Row Variable', fontsize=12)

plt.xticks(rotation=45, ha='right')
plt.yticks(rotation=0)
plt.tight_layout()
Raw lines 561 to 596
597## Statistical Comparisons

Statistical Comparisons

Raw lines 597 to 598
599### Before/After Comparison
601Retain complete matched subjects for a paired-effect analysis. The red timepoint intervals below describe marginal means, not uncertainty in the change. Analyze and plot `after - before` per subject when the treatment effect is the estimand.
603```python
604# This wide input has one row per subject.
605if not df['subject'].is_unique:
606 raise ValueError('Expected one before/after pair per subject')
607# Select the paired columns so unrelated columns cannot collide with value_name.
608df_paired = df[['subject', 'before', 'after']].melt(
609 id_vars='subject',
610 value_vars=['before', 'after'],
611 var_name='timepoint',
612 value_name='measurement'
615fig, axes = plt.subplots(1, 2, figsize=(12, 5))
617# Left: Individual trajectories
618for subject in df_paired['subject'].unique():
619 subject_data = df_paired[df_paired['subject'] == subject]
620 axes[0].plot(subject_data['timepoint'], subject_data['measurement'],
621 'o-', alpha=0.3, color='gray')
623sns.pointplot(
624 data=df_paired,
625 x='timepoint',
626 y='measurement',
627 color='red',
628 markers='D',
629 markersize=8,
630 errorbar=('ci', 95),
631 capsize=0.2,
632 ax=axes[0]
634axes[0].set_title('Individual Changes')
635axes[0].set_ylabel('Measurement')
637# Right: Distribution comparison
638sns.violinplot(
639 data=df_paired,
640 x='timepoint',
641 y='measurement',
642 inner='box',
643 ax=axes[1]
645sns.swarmplot(
646 data=df_paired,
647 x='timepoint',
648 y='measurement',
649 color='black',
650 alpha=0.5,
651 size=3,
652 ax=axes[1]
654axes[1].set_title('Distribution Comparison')
655axes[1].set_ylabel('')
657plt.tight_layout()
658```

Before/After Comparison

Retain complete matched subjects for a paired-effect analysis. The red timepoint intervals below describe marginal means, not uncertainty in the change. Analyze and plot after - before per subject when the treatment effect is the estimand.

# This wide input has one row per subject.
if not df['subject'].is_unique:
    raise ValueError('Expected one before/after pair per subject')
# Select the paired columns so unrelated columns cannot collide with value_name.
df_paired = df[['subject', 'before', 'after']].melt(
    id_vars='subject',
    value_vars=['before', 'after'],
    var_name='timepoint',
    value_name='measurement'
)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Left: Individual trajectories
for subject in df_paired['subject'].unique():
    subject_data = df_paired[df_paired['subject'] == subject]
    axes[0].plot(subject_data['timepoint'], subject_data['measurement'],
                 'o-', alpha=0.3, color='gray')

sns.pointplot(
    data=df_paired,
    x='timepoint',
    y='measurement',
    color='red',
    markers='D',
    markersize=8,
    errorbar=('ci', 95),
    capsize=0.2,
    ax=axes[0]
)
axes[0].set_title('Individual Changes')
axes[0].set_ylabel('Measurement')

# Right: Distribution comparison
sns.violinplot(
    data=df_paired,
    x='timepoint',
    y='measurement',
    inner='box',
    ax=axes[1]
)
sns.swarmplot(
    data=df_paired,
    x='timepoint',
    y='measurement',
    color='black',
    alpha=0.5,
    size=3,
    ax=axes[1]
)
axes[1].set_title('Distribution Comparison')
axes[1].set_ylabel('')

plt.tight_layout()
Raw lines 599 to 659
660### Dose-Response Curve
662Use numeric concentration coordinates. An ordinal strip/point axis makes unequally
663spaced doses look equally spaced and cannot support a numeric fitted curve.
664This illustrative fit assumes a monotone increasing four-parameter response and
665independent observations. A fit or small residual is not evidence of identifiable
666EC50: inspect uncertainty, residuals, plate effects, and coverage of both plateaus.
667Zero-dose controls need separate display/model handling, not a logarithm.
669```python
670from scipy.optimize import curve_fit
671from scipy.special import expit
673fit_data = dose_df[['dose', 'response']].astype(float).copy()
674if not np.isfinite(fit_data.to_numpy()).all() or (fit_data['dose'] <= 0).any():
675 raise ValueError('This log-dose recipe requires finite responses and positive doses')
676if fit_data['dose'].nunique() < 5:
677 raise ValueError('Use at least five distinct positive doses for this demonstration')
679def sigmoid(x, bottom, amplitude, ec50, hill):
680 return bottom + amplitude * expit(hill * (np.log(x) - np.log(ec50)))
682x = fit_data['dose'].to_numpy()
683y = fit_data['response'].to_numpy()
684span = np.ptp(y)
685if span == 0:
686 raise ValueError('Constant response cannot identify a dose-response curve')
687params, covariance = curve_fit(
688 sigmoid, x, y, p0=[y.min(), span, np.median(x), 1.],
689 bounds=([-np.inf, 0., np.finfo(float).tiny, .01],
690 [np.inf, np.inf, np.inf, 10.]), maxfev=10000,
692x_smooth = np.geomspace(x.min(), x.max(), 200)
693fig, ax = plt.subplots(figsize=(8, 6))
694sns.scatterplot(data=fit_data, x='dose', y='response', color='gray', alpha=.4, ax=ax)
695sns.lineplot(data=fit_data, x='dose', y='response', errorbar=('ci', 95),
696 seed=7, marker='o', color='blue', ax=ax, label='Mean and 95% CI')
697ax.plot(x_smooth, sigmoid(x_smooth, *params), color='red', label='Illustrative fit')
698ax.set(xscale='log', xlabel='Dose (concentration units)', ylabel='Response')
699ax.legend()
700sns.despine(ax=ax)
701```

Dose-Response Curve

Use numeric concentration coordinates. An ordinal strip/point axis makes unequally spaced doses look equally spaced and cannot support a numeric fitted curve. This illustrative fit assumes a monotone increasing four-parameter response and independent observations. A fit or small residual is not evidence of identifiable EC50: inspect uncertainty, residuals, plate effects, and coverage of both plateaus. Zero-dose controls need separate display/model handling, not a logarithm.

from scipy.optimize import curve_fit
from scipy.special import expit

fit_data = dose_df[['dose', 'response']].astype(float).copy()
if not np.isfinite(fit_data.to_numpy()).all() or (fit_data['dose'] <= 0).any():
    raise ValueError('This log-dose recipe requires finite responses and positive doses')
if fit_data['dose'].nunique() < 5:
    raise ValueError('Use at least five distinct positive doses for this demonstration')

def sigmoid(x, bottom, amplitude, ec50, hill):
    return bottom + amplitude * expit(hill * (np.log(x) - np.log(ec50)))

x = fit_data['dose'].to_numpy()
y = fit_data['response'].to_numpy()
span = np.ptp(y)
if span == 0:
    raise ValueError('Constant response cannot identify a dose-response curve')
params, covariance = curve_fit(
    sigmoid, x, y, p0=[y.min(), span, np.median(x), 1.],
    bounds=([-np.inf, 0., np.finfo(float).tiny, .01],
            [np.inf, np.inf, np.inf, 10.]), maxfev=10000,
)
x_smooth = np.geomspace(x.min(), x.max(), 200)
fig, ax = plt.subplots(figsize=(8, 6))
sns.scatterplot(data=fit_data, x='dose', y='response', color='gray', alpha=.4, ax=ax)
sns.lineplot(data=fit_data, x='dose', y='response', errorbar=('ci', 95),
             seed=7, marker='o', color='blue', ax=ax, label='Mean and 95% CI')
ax.plot(x_smooth, sigmoid(x_smooth, *params), color='red', label='Illustrative fit')
ax.set(xscale='log', xlabel='Dose (concentration units)', ylabel='Response')
ax.legend()
sns.despine(ax=ax)
Raw lines 660 to 702
703## Custom Styling

Custom Styling

Raw lines 703 to 704
705### Custom Color Palette from Hex Codes
707```python
708# Define custom palette
709custom_palette = ['#E64B35', '#4DBBD5', '#00A087', '#3C5488', '#F39B7F']
710sns.set_palette(custom_palette)
712# Or use for specific plot
713sns.scatterplot(
714 data=df,
715 x='x',
716 y='y',
717 hue='category',
718 palette=custom_palette
720```

Custom Color Palette from Hex Codes

# Define custom palette
custom_palette = ['#E64B35', '#4DBBD5', '#00A087', '#3C5488', '#F39B7F']
sns.set_palette(custom_palette)

# Or use for specific plot
sns.scatterplot(
    data=df,
    x='x',
    y='y',
    hue='category',
    palette=custom_palette
)
Raw lines 705 to 721
722### Publication-Ready Theme
724```python
725# Set comprehensive theme
726sns.set_theme(
727 context='paper',
728 style='ticks',
729 palette='colorblind',
730 font='Arial',
731 font_scale=1.1,
732 rc={
733 'figure.dpi': 300,
734 'savefig.dpi': 300,
735 'savefig.format': 'pdf',
736 'axes.linewidth': 1.0,
737 'axes.labelweight': 'bold',
738 'xtick.major.width': 1.0,
739 'ytick.major.width': 1.0,
740 'xtick.direction': 'out',
741 'ytick.direction': 'out',
742 'legend.frameon': False,
743 'pdf.fonttype': 42, # True Type fonts for PDFs
744 }
746```

Publication-Ready Theme

# Set comprehensive theme
sns.set_theme(
    context='paper',
    style='ticks',
    palette='colorblind',
    font='Arial',
    font_scale=1.1,
    rc={
        'figure.dpi': 300,
        'savefig.dpi': 300,
        'savefig.format': 'pdf',
        'axes.linewidth': 1.0,
        'axes.labelweight': 'bold',
        'xtick.major.width': 1.0,
        'ytick.major.width': 1.0,
        'xtick.direction': 'out',
        'ytick.direction': 'out',
        'legend.frameon': False,
        'pdf.fonttype': 42,  # True Type fonts for PDFs
    }
)
Raw lines 722 to 747
748### Diverging Colormap Centered on Zero
750```python
751# For data with meaningful zero point (e.g., log fold change)
752from matplotlib.colors import TwoSlopeNorm
754# Find data range
755vmin, vmax = pivot_data.min().min(), pivot_data.max().max()
756if not np.isfinite([vmin, vmax]).all() or not vmin < 0 < vmax:
757 raise ValueError('TwoSlopeNorm with zero center needs finite values on both sides')
758vcenter = 0
760# Create norm
761norm = TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax)
763# Plot
764sns.heatmap(
765 pivot_data,
766 cmap='RdBu_r',
767 norm=norm,
768 annot=True,
769 fmt='.2f'
771```

Diverging Colormap Centered on Zero

# For data with meaningful zero point (e.g., log fold change)
from matplotlib.colors import TwoSlopeNorm

# Find data range
vmin, vmax = pivot_data.min().min(), pivot_data.max().max()
if not np.isfinite([vmin, vmax]).all() or not vmin < 0 < vmax:
    raise ValueError('TwoSlopeNorm with zero center needs finite values on both sides')
vcenter = 0

# Create norm
norm = TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax)

# Plot
sns.heatmap(
    pivot_data,
    cmap='RdBu_r',
    norm=norm,
    annot=True,
    fmt='.2f'
)
Raw lines 748 to 772
773## Large Datasets

Large Datasets

Raw lines 773 to 774
775### Downsampling Strategy
777```python
778# Equal allocation emphasizes rare groups and changes their apparent prevalence.
779# Use full data for estimates/densities; label this as a display-only sample.
780def smart_sample(df, target_size=10000, category_col=None, seed=7):
781 if not isinstance(target_size, int) or target_size < 1:
782 raise ValueError('target_size must be a positive integer')
783 if len(df) <= target_size:
784 return df.copy()
785 if category_col is None:
786 return df.sample(n=target_size, random_state=seed)
787 groups = list(df.groupby(category_col, observed=True, dropna=False, sort=False))
788 if target_size < len(groups):
789 raise ValueError('Budget must allow at least one row per observed group')
790 quota = target_size // len(groups)
791 rng = np.random.default_rng(seed)
792 return pd.concat([
793 group.sample(n=min(len(group), quota), random_state=rng)
794 for _, group in groups
795 ]) # At most target_size; unused quota is deliberately not redistributed.
797# Use sampled data for visualization
798df_sampled = smart_sample(large_df, target_size=5000, category_col='category')
800sns.scatterplot(data=df_sampled, x='x', y='y', hue='category', alpha=0.5)
801```

Downsampling Strategy

# Equal allocation emphasizes rare groups and changes their apparent prevalence.
# Use full data for estimates/densities; label this as a display-only sample.
def smart_sample(df, target_size=10000, category_col=None, seed=7):
    if not isinstance(target_size, int) or target_size < 1:
        raise ValueError('target_size must be a positive integer')
    if len(df) <= target_size:
        return df.copy()
    if category_col is None:
        return df.sample(n=target_size, random_state=seed)
    groups = list(df.groupby(category_col, observed=True, dropna=False, sort=False))
    if target_size < len(groups):
        raise ValueError('Budget must allow at least one row per observed group')
    quota = target_size // len(groups)
    rng = np.random.default_rng(seed)
    return pd.concat([
        group.sample(n=min(len(group), quota), random_state=rng)
        for _, group in groups
    ])  # At most target_size; unused quota is deliberately not redistributed.

# Use sampled data for visualization
df_sampled = smart_sample(large_df, target_size=5000, category_col='category')

sns.scatterplot(data=df_sampled, x='x', y='y', hue='category', alpha=0.5)
Raw lines 775 to 802
803### Hexbin for Dense Scatter Plots
805```python
806# For millions of points
807fig, axes = plt.subplots(1, 2, figsize=(14, 6))
809# Regular scatter (slow)
810axes[0].scatter(df['x'], df['y'], alpha=0.1, s=1)
811axes[0].set_title('Scatter (all points)')
813# Hexbin (fast)
814hb = axes[1].hexbin(df['x'], df['y'], gridsize=50, cmap='viridis', mincnt=1)
815axes[1].set_title('Hexbin Aggregation')
816plt.colorbar(hb, ax=axes[1], label='Count')
818plt.tight_layout()
819```

Hexbin for Dense Scatter Plots

# For millions of points
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# Regular scatter (slow)
axes[0].scatter(df['x'], df['y'], alpha=0.1, s=1)
axes[0].set_title('Scatter (all points)')

# Hexbin (fast)
hb = axes[1].hexbin(df['x'], df['y'], gridsize=50, cmap='viridis', mincnt=1)
axes[1].set_title('Hexbin Aggregation')
plt.colorbar(hb, ax=axes[1], label='Count')

plt.tight_layout()
Raw lines 803 to 820
821## Interactive Elements for Notebooks
823These examples require an ipywidgets-enabled notebook frontend; they are illustrative and were not browser-tested. An empty filter should display a message rather than attempting a categorical plot.

Interactive Elements for Notebooks

These examples require an ipywidgets-enabled notebook frontend; they are illustrative and were not browser-tested. An empty filter should display a message rather than attempting a categorical plot.

Raw lines 821 to 824
825### Adjustable Parameters
827```python
828from ipywidgets import interact, FloatSlider
830@interact(bandwidth=FloatSlider(min=0.1, max=3.0, step=0.1, value=1.0))
831def plot_kde(bandwidth):
832 plt.figure(figsize=(10, 6))
833 sns.kdeplot(data=df, x='value', hue='category',
834 bw_adjust=bandwidth, fill=True)
835 plt.title(f'KDE with bandwidth adjustment = {bandwidth}')
836 plt.show()
837```

Adjustable Parameters

from ipywidgets import interact, FloatSlider

@interact(bandwidth=FloatSlider(min=0.1, max=3.0, step=0.1, value=1.0))
def plot_kde(bandwidth):
    plt.figure(figsize=(10, 6))
    sns.kdeplot(data=df, x='value', hue='category',
                bw_adjust=bandwidth, fill=True)
    plt.title(f'KDE with bandwidth adjustment = {bandwidth}')
    plt.show()
Raw lines 825 to 838
839### Dynamic Filtering
841```python
842from ipywidgets import interact, SelectMultiple
844categories = df['category'].dropna().unique().tolist()
845if not categories:
846 raise ValueError('No nonmissing categories available')
848@interact(selected=SelectMultiple(options=categories, value=[categories[0]]))
849def filtered_plot(selected):
850 filtered_df = df[df['category'].isin(selected)]
851 if filtered_df.empty:
852 print('Select at least one category with observations.')
853 return
855 fig, ax = plt.subplots(figsize=(10, 6))
856 sns.violinplot(data=filtered_df, x='category', y='value', ax=ax)
857 ax.set_title(f'Showing {len(selected)} categories')
858 plt.show()
859```
862Sources: [Seaborn regression](https://seaborn.pydata.org/generated/seaborn.regplot.html),
863[jointplot](https://seaborn.pydata.org/generated/seaborn.jointplot.html),
864[clustermap](https://seaborn.pydata.org/generated/seaborn.clustermap.html),
865[SciPy curve_fit](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html),
866[pandas groupby](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html).

Dynamic Filtering

from ipywidgets import interact, SelectMultiple

categories = df['category'].dropna().unique().tolist()
if not categories:
    raise ValueError('No nonmissing categories available')

@interact(selected=SelectMultiple(options=categories, value=[categories[0]]))
def filtered_plot(selected):
    filtered_df = df[df['category'].isin(selected)]
    if filtered_df.empty:
        print('Select at least one category with observations.')
        return

    fig, ax = plt.subplots(figsize=(10, 6))
    sns.violinplot(data=filtered_df, x='category', y='value', ax=ax)
    ax.set_title(f'Showing {len(selected)} categories')
    plt.show()

Sources: Seaborn regression, jointplot, clustermap, SciPy curve_fit, pandas groupby.

Raw lines 839 to 866