Initial commit
This commit is contained in:
355
skills/matplotlib/SKILL.md
Normal file
355
skills/matplotlib/SKILL.md
Normal file
@@ -0,0 +1,355 @@
|
||||
---
|
||||
name: matplotlib
|
||||
description: "Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures."
|
||||
---
|
||||
|
||||
# Matplotlib
|
||||
|
||||
## Overview
|
||||
|
||||
Matplotlib is Python's foundational visualization library for creating static, animated, and interactive plots. This skill provides guidance on using matplotlib effectively, covering both the pyplot interface (MATLAB-style) and the object-oriented API (Figure/Axes), along with best practices for creating publication-quality visualizations.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
This skill should be used when:
|
||||
- Creating any type of plot or chart (line, scatter, bar, histogram, heatmap, contour, etc.)
|
||||
- Generating scientific or statistical visualizations
|
||||
- Customizing plot appearance (colors, styles, labels, legends)
|
||||
- Creating multi-panel figures with subplots
|
||||
- Exporting visualizations to various formats (PNG, PDF, SVG, etc.)
|
||||
- Building interactive plots or animations
|
||||
- Working with 3D visualizations
|
||||
- Integrating plots into Jupyter notebooks or GUI applications
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### The Matplotlib Hierarchy
|
||||
|
||||
Matplotlib uses a hierarchical structure of objects:
|
||||
|
||||
1. **Figure** - The top-level container for all plot elements
|
||||
2. **Axes** - The actual plotting area where data is displayed (one Figure can contain multiple Axes)
|
||||
3. **Artist** - Everything visible on the figure (lines, text, ticks, etc.)
|
||||
4. **Axis** - The number line objects (x-axis, y-axis) that handle ticks and labels
|
||||
|
||||
### Two Interfaces
|
||||
|
||||
**1. pyplot Interface (Implicit, MATLAB-style)**
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.plot([1, 2, 3, 4])
|
||||
plt.ylabel('some numbers')
|
||||
plt.show()
|
||||
```
|
||||
- Convenient for quick, simple plots
|
||||
- Maintains state automatically
|
||||
- Good for interactive work and simple scripts
|
||||
|
||||
**2. Object-Oriented Interface (Explicit)**
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([1, 2, 3, 4])
|
||||
ax.set_ylabel('some numbers')
|
||||
plt.show()
|
||||
```
|
||||
- **Recommended for most use cases**
|
||||
- More explicit control over figure and axes
|
||||
- Better for complex figures with multiple subplots
|
||||
- Easier to maintain and debug
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### 1. Basic Plot Creation
|
||||
|
||||
**Single plot workflow:**
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
# Create figure and axes (OO interface - RECOMMENDED)
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
|
||||
# Generate and plot data
|
||||
x = np.linspace(0, 2*np.pi, 100)
|
||||
ax.plot(x, np.sin(x), label='sin(x)')
|
||||
ax.plot(x, np.cos(x), label='cos(x)')
|
||||
|
||||
# Customize
|
||||
ax.set_xlabel('x')
|
||||
ax.set_ylabel('y')
|
||||
ax.set_title('Trigonometric Functions')
|
||||
ax.legend()
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
# Save and/or display
|
||||
plt.savefig('plot.png', dpi=300, bbox_inches='tight')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
### 2. Multiple Subplots
|
||||
|
||||
**Creating subplot layouts:**
|
||||
```python
|
||||
# Method 1: Regular grid
|
||||
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
|
||||
axes[0, 0].plot(x, y1)
|
||||
axes[0, 1].scatter(x, y2)
|
||||
axes[1, 0].bar(categories, values)
|
||||
axes[1, 1].hist(data, bins=30)
|
||||
|
||||
# Method 2: Mosaic layout (more flexible)
|
||||
fig, axes = plt.subplot_mosaic([['left', 'right_top'],
|
||||
['left', 'right_bottom']],
|
||||
figsize=(10, 8))
|
||||
axes['left'].plot(x, y)
|
||||
axes['right_top'].scatter(x, y)
|
||||
axes['right_bottom'].hist(data)
|
||||
|
||||
# Method 3: GridSpec (maximum control)
|
||||
from matplotlib.gridspec import GridSpec
|
||||
fig = plt.figure(figsize=(12, 8))
|
||||
gs = GridSpec(3, 3, figure=fig)
|
||||
ax1 = fig.add_subplot(gs[0, :]) # Top row, all columns
|
||||
ax2 = fig.add_subplot(gs[1:, 0]) # Bottom two rows, first column
|
||||
ax3 = fig.add_subplot(gs[1:, 1:]) # Bottom two rows, last two columns
|
||||
```
|
||||
|
||||
### 3. Plot Types and Use Cases
|
||||
|
||||
**Line plots** - Time series, continuous data, trends
|
||||
```python
|
||||
ax.plot(x, y, linewidth=2, linestyle='--', marker='o', color='blue')
|
||||
```
|
||||
|
||||
**Scatter plots** - Relationships between variables, correlations
|
||||
```python
|
||||
ax.scatter(x, y, s=sizes, c=colors, alpha=0.6, cmap='viridis')
|
||||
```
|
||||
|
||||
**Bar charts** - Categorical comparisons
|
||||
```python
|
||||
ax.bar(categories, values, color='steelblue', edgecolor='black')
|
||||
# For horizontal bars:
|
||||
ax.barh(categories, values)
|
||||
```
|
||||
|
||||
**Histograms** - Distributions
|
||||
```python
|
||||
ax.hist(data, bins=30, edgecolor='black', alpha=0.7)
|
||||
```
|
||||
|
||||
**Heatmaps** - Matrix data, correlations
|
||||
```python
|
||||
im = ax.imshow(matrix, cmap='coolwarm', aspect='auto')
|
||||
plt.colorbar(im, ax=ax)
|
||||
```
|
||||
|
||||
**Contour plots** - 3D data on 2D plane
|
||||
```python
|
||||
contour = ax.contour(X, Y, Z, levels=10)
|
||||
ax.clabel(contour, inline=True, fontsize=8)
|
||||
```
|
||||
|
||||
**Box plots** - Statistical distributions
|
||||
```python
|
||||
ax.boxplot([data1, data2, data3], labels=['A', 'B', 'C'])
|
||||
```
|
||||
|
||||
**Violin plots** - Distribution densities
|
||||
```python
|
||||
ax.violinplot([data1, data2, data3], positions=[1, 2, 3])
|
||||
```
|
||||
|
||||
For comprehensive plot type examples and variations, refer to `references/plot_types.md`.
|
||||
|
||||
### 4. Styling and Customization
|
||||
|
||||
**Color specification methods:**
|
||||
- Named colors: `'red'`, `'blue'`, `'steelblue'`
|
||||
- Hex codes: `'#FF5733'`
|
||||
- RGB tuples: `(0.1, 0.2, 0.3)`
|
||||
- Colormaps: `cmap='viridis'`, `cmap='plasma'`, `cmap='coolwarm'`
|
||||
|
||||
**Using style sheets:**
|
||||
```python
|
||||
plt.style.use('seaborn-v0_8-darkgrid') # Apply predefined style
|
||||
# Available styles: 'ggplot', 'bmh', 'fivethirtyeight', etc.
|
||||
print(plt.style.available) # List all available styles
|
||||
```
|
||||
|
||||
**Customizing with rcParams:**
|
||||
```python
|
||||
plt.rcParams['font.size'] = 12
|
||||
plt.rcParams['axes.labelsize'] = 14
|
||||
plt.rcParams['axes.titlesize'] = 16
|
||||
plt.rcParams['xtick.labelsize'] = 10
|
||||
plt.rcParams['ytick.labelsize'] = 10
|
||||
plt.rcParams['legend.fontsize'] = 12
|
||||
plt.rcParams['figure.titlesize'] = 18
|
||||
```
|
||||
|
||||
**Text and annotations:**
|
||||
```python
|
||||
ax.text(x, y, 'annotation', fontsize=12, ha='center')
|
||||
ax.annotate('important point', xy=(x, y), xytext=(x+1, y+1),
|
||||
arrowprops=dict(arrowstyle='->', color='red'))
|
||||
```
|
||||
|
||||
For detailed styling options and colormap guidelines, see `references/styling_guide.md`.
|
||||
|
||||
### 5. Saving Figures
|
||||
|
||||
**Export to various formats:**
|
||||
```python
|
||||
# High-resolution PNG for presentations/papers
|
||||
plt.savefig('figure.png', dpi=300, bbox_inches='tight', facecolor='white')
|
||||
|
||||
# Vector format for publications (scalable)
|
||||
plt.savefig('figure.pdf', bbox_inches='tight')
|
||||
plt.savefig('figure.svg', bbox_inches='tight')
|
||||
|
||||
# Transparent background
|
||||
plt.savefig('figure.png', dpi=300, bbox_inches='tight', transparent=True)
|
||||
```
|
||||
|
||||
**Important parameters:**
|
||||
- `dpi`: Resolution (300 for publications, 150 for web, 72 for screen)
|
||||
- `bbox_inches='tight'`: Removes excess whitespace
|
||||
- `facecolor='white'`: Ensures white background (useful for transparent themes)
|
||||
- `transparent=True`: Transparent background
|
||||
|
||||
### 6. Working with 3D Plots
|
||||
|
||||
```python
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
|
||||
# Surface plot
|
||||
ax.plot_surface(X, Y, Z, cmap='viridis')
|
||||
|
||||
# 3D scatter
|
||||
ax.scatter(x, y, z, c=colors, marker='o')
|
||||
|
||||
# 3D line plot
|
||||
ax.plot(x, y, z, linewidth=2)
|
||||
|
||||
# Labels
|
||||
ax.set_xlabel('X Label')
|
||||
ax.set_ylabel('Y Label')
|
||||
ax.set_zlabel('Z Label')
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Interface Selection
|
||||
- **Use the object-oriented interface** (fig, ax = plt.subplots()) for production code
|
||||
- Reserve pyplot interface for quick interactive exploration only
|
||||
- Always create figures explicitly rather than relying on implicit state
|
||||
|
||||
### 2. Figure Size and DPI
|
||||
- Set figsize at creation: `fig, ax = plt.subplots(figsize=(10, 6))`
|
||||
- Use appropriate DPI for output medium:
|
||||
- Screen/notebook: 72-100 dpi
|
||||
- Web: 150 dpi
|
||||
- Print/publications: 300 dpi
|
||||
|
||||
### 3. Layout Management
|
||||
- Use `constrained_layout=True` or `tight_layout()` to prevent overlapping elements
|
||||
- `fig, ax = plt.subplots(constrained_layout=True)` is recommended for automatic spacing
|
||||
|
||||
### 4. Colormap Selection
|
||||
- **Sequential** (viridis, plasma, inferno): Ordered data with consistent progression
|
||||
- **Diverging** (coolwarm, RdBu): Data with meaningful center point (e.g., zero)
|
||||
- **Qualitative** (tab10, Set3): Categorical/nominal data
|
||||
- Avoid rainbow colormaps (jet) - they are not perceptually uniform
|
||||
|
||||
### 5. Accessibility
|
||||
- Use colorblind-friendly colormaps (viridis, cividis)
|
||||
- Add patterns/hatching for bar charts in addition to colors
|
||||
- Ensure sufficient contrast between elements
|
||||
- Include descriptive labels and legends
|
||||
|
||||
### 6. Performance
|
||||
- For large datasets, use `rasterized=True` in plot calls to reduce file size
|
||||
- Use appropriate data reduction before plotting (e.g., downsample dense time series)
|
||||
- For animations, use blitting for better performance
|
||||
|
||||
### 7. Code Organization
|
||||
```python
|
||||
# Good practice: Clear structure
|
||||
def create_analysis_plot(data, title):
|
||||
"""Create standardized analysis plot."""
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
# Plot data
|
||||
ax.plot(data['x'], data['y'], linewidth=2)
|
||||
|
||||
# Customize
|
||||
ax.set_xlabel('X Axis Label', fontsize=12)
|
||||
ax.set_ylabel('Y Axis Label', fontsize=12)
|
||||
ax.set_title(title, fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3)
|
||||
|
||||
return fig, ax
|
||||
|
||||
# Use the function
|
||||
fig, ax = create_analysis_plot(my_data, 'My Analysis')
|
||||
plt.savefig('analysis.png', dpi=300, bbox_inches='tight')
|
||||
```
|
||||
|
||||
## Quick Reference Scripts
|
||||
|
||||
This skill includes helper scripts in the `scripts/` directory:
|
||||
|
||||
### `plot_template.py`
|
||||
Template script demonstrating various plot types with best practices. Use this as a starting point for creating new visualizations.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python scripts/plot_template.py
|
||||
```
|
||||
|
||||
### `style_configurator.py`
|
||||
Interactive utility to configure matplotlib style preferences and generate custom style sheets.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python scripts/style_configurator.py
|
||||
```
|
||||
|
||||
## Detailed References
|
||||
|
||||
For comprehensive information, consult the reference documents:
|
||||
|
||||
- **`references/plot_types.md`** - Complete catalog of plot types with code examples and use cases
|
||||
- **`references/styling_guide.md`** - Detailed styling options, colormaps, and customization
|
||||
- **`references/api_reference.md`** - Core classes and methods reference
|
||||
- **`references/common_issues.md`** - Troubleshooting guide for common problems
|
||||
|
||||
## Integration with Other Tools
|
||||
|
||||
Matplotlib integrates well with:
|
||||
- **NumPy/Pandas** - Direct plotting from arrays and DataFrames
|
||||
- **Seaborn** - High-level statistical visualizations built on matplotlib
|
||||
- **Jupyter** - Interactive plotting with `%matplotlib inline` or `%matplotlib widget`
|
||||
- **GUI frameworks** - Embedding in Tkinter, Qt, wxPython applications
|
||||
|
||||
## Common Gotchas
|
||||
|
||||
1. **Overlapping elements**: Use `constrained_layout=True` or `tight_layout()`
|
||||
2. **State confusion**: Use OO interface to avoid pyplot state machine issues
|
||||
3. **Memory issues with many figures**: Close figures explicitly with `plt.close(fig)`
|
||||
4. **Font warnings**: Install fonts or suppress warnings with `plt.rcParams['font.sans-serif']`
|
||||
5. **DPI confusion**: Remember that figsize is in inches, not pixels: `pixels = dpi * inches`
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- Official documentation: https://matplotlib.org/
|
||||
- Gallery: https://matplotlib.org/stable/gallery/index.html
|
||||
- Cheatsheets: https://matplotlib.org/cheatsheets/
|
||||
- Tutorials: https://matplotlib.org/stable/tutorials/index.html
|
||||
412
skills/matplotlib/references/api_reference.md
Normal file
412
skills/matplotlib/references/api_reference.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# Matplotlib API Reference
|
||||
|
||||
This document provides a quick reference for the most commonly used matplotlib classes and methods.
|
||||
|
||||
## Core Classes
|
||||
|
||||
### Figure
|
||||
|
||||
The top-level container for all plot elements.
|
||||
|
||||
**Creation:**
|
||||
```python
|
||||
fig = plt.figure(figsize=(10, 6), dpi=100, facecolor='white')
|
||||
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(10, 6))
|
||||
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
|
||||
```
|
||||
|
||||
**Key Methods:**
|
||||
- `fig.add_subplot(nrows, ncols, index)` - Add a subplot
|
||||
- `fig.add_axes([left, bottom, width, height])` - Add axes at specific position
|
||||
- `fig.savefig(filename, dpi=300, bbox_inches='tight')` - Save figure
|
||||
- `fig.tight_layout()` - Adjust spacing to prevent overlaps
|
||||
- `fig.suptitle(title)` - Set figure title
|
||||
- `fig.legend()` - Create figure-level legend
|
||||
- `fig.colorbar(mappable)` - Add colorbar to figure
|
||||
- `plt.close(fig)` - Close figure to free memory
|
||||
|
||||
**Key Attributes:**
|
||||
- `fig.axes` - List of all axes in the figure
|
||||
- `fig.dpi` - Resolution in dots per inch
|
||||
- `fig.figsize` - Figure dimensions in inches (width, height)
|
||||
|
||||
### Axes
|
||||
|
||||
The actual plotting area where data is visualized.
|
||||
|
||||
**Creation:**
|
||||
```python
|
||||
fig, ax = plt.subplots() # Single axes
|
||||
ax = fig.add_subplot(111) # Alternative method
|
||||
```
|
||||
|
||||
**Plotting Methods:**
|
||||
|
||||
**Line plots:**
|
||||
- `ax.plot(x, y, **kwargs)` - Line plot
|
||||
- `ax.step(x, y, where='pre'/'mid'/'post')` - Step plot
|
||||
- `ax.errorbar(x, y, yerr, xerr)` - Error bars
|
||||
|
||||
**Scatter plots:**
|
||||
- `ax.scatter(x, y, s=size, c=color, marker='o', alpha=0.5)` - Scatter plot
|
||||
|
||||
**Bar charts:**
|
||||
- `ax.bar(x, height, width=0.8, align='center')` - Vertical bar chart
|
||||
- `ax.barh(y, width)` - Horizontal bar chart
|
||||
|
||||
**Statistical plots:**
|
||||
- `ax.hist(data, bins=10, density=False)` - Histogram
|
||||
- `ax.boxplot(data, labels=None)` - Box plot
|
||||
- `ax.violinplot(data)` - Violin plot
|
||||
|
||||
**2D plots:**
|
||||
- `ax.imshow(array, cmap='viridis', aspect='auto')` - Display image/matrix
|
||||
- `ax.contour(X, Y, Z, levels=10)` - Contour lines
|
||||
- `ax.contourf(X, Y, Z, levels=10)` - Filled contours
|
||||
- `ax.pcolormesh(X, Y, Z)` - Pseudocolor plot
|
||||
|
||||
**Filling:**
|
||||
- `ax.fill_between(x, y1, y2, alpha=0.3)` - Fill between curves
|
||||
- `ax.fill_betweenx(y, x1, x2)` - Fill between vertical curves
|
||||
|
||||
**Text and annotations:**
|
||||
- `ax.text(x, y, text, fontsize=12)` - Add text
|
||||
- `ax.annotate(text, xy=(x, y), xytext=(x2, y2), arrowprops={})` - Annotate with arrow
|
||||
|
||||
**Customization Methods:**
|
||||
|
||||
**Labels and titles:**
|
||||
- `ax.set_xlabel(label, fontsize=12)` - Set x-axis label
|
||||
- `ax.set_ylabel(label, fontsize=12)` - Set y-axis label
|
||||
- `ax.set_title(title, fontsize=14)` - Set axes title
|
||||
|
||||
**Limits and scales:**
|
||||
- `ax.set_xlim(left, right)` - Set x-axis limits
|
||||
- `ax.set_ylim(bottom, top)` - Set y-axis limits
|
||||
- `ax.set_xscale('linear'/'log'/'symlog')` - Set x-axis scale
|
||||
- `ax.set_yscale('linear'/'log'/'symlog')` - Set y-axis scale
|
||||
|
||||
**Ticks:**
|
||||
- `ax.set_xticks(positions)` - Set x-tick positions
|
||||
- `ax.set_xticklabels(labels)` - Set x-tick labels
|
||||
- `ax.tick_params(axis='both', labelsize=10)` - Customize tick appearance
|
||||
|
||||
**Grid and spines:**
|
||||
- `ax.grid(True, alpha=0.3, linestyle='--')` - Add grid
|
||||
- `ax.spines['top'].set_visible(False)` - Hide top spine
|
||||
- `ax.spines['right'].set_visible(False)` - Hide right spine
|
||||
|
||||
**Legend:**
|
||||
- `ax.legend(loc='best', fontsize=10, frameon=True)` - Add legend
|
||||
- `ax.legend(handles, labels)` - Custom legend
|
||||
|
||||
**Aspect and layout:**
|
||||
- `ax.set_aspect('equal'/'auto'/ratio)` - Set aspect ratio
|
||||
- `ax.invert_xaxis()` - Invert x-axis
|
||||
- `ax.invert_yaxis()` - Invert y-axis
|
||||
|
||||
### pyplot Module
|
||||
|
||||
High-level interface for quick plotting.
|
||||
|
||||
**Figure creation:**
|
||||
- `plt.figure()` - Create new figure
|
||||
- `plt.subplots()` - Create figure and axes
|
||||
- `plt.subplot()` - Add subplot to current figure
|
||||
|
||||
**Plotting (uses current axes):**
|
||||
- `plt.plot()` - Line plot
|
||||
- `plt.scatter()` - Scatter plot
|
||||
- `plt.bar()` - Bar chart
|
||||
- `plt.hist()` - Histogram
|
||||
- (All axes methods available)
|
||||
|
||||
**Display and save:**
|
||||
- `plt.show()` - Display figure
|
||||
- `plt.savefig()` - Save figure
|
||||
- `plt.close()` - Close figure
|
||||
|
||||
**Style:**
|
||||
- `plt.style.use(style_name)` - Apply style sheet
|
||||
- `plt.style.available` - List available styles
|
||||
|
||||
**State management:**
|
||||
- `plt.gca()` - Get current axes
|
||||
- `plt.gcf()` - Get current figure
|
||||
- `plt.sca(ax)` - Set current axes
|
||||
- `plt.clf()` - Clear current figure
|
||||
- `plt.cla()` - Clear current axes
|
||||
|
||||
## Line and Marker Styles
|
||||
|
||||
### Line Styles
|
||||
- `'-'` or `'solid'` - Solid line
|
||||
- `'--'` or `'dashed'` - Dashed line
|
||||
- `'-.'` or `'dashdot'` - Dash-dot line
|
||||
- `':'` or `'dotted'` - Dotted line
|
||||
- `''` or `' '` or `'None'` - No line
|
||||
|
||||
### Marker Styles
|
||||
- `'.'` - Point marker
|
||||
- `'o'` - Circle marker
|
||||
- `'v'`, `'^'`, `'<'`, `'>'` - Triangle markers
|
||||
- `'s'` - Square marker
|
||||
- `'p'` - Pentagon marker
|
||||
- `'*'` - Star marker
|
||||
- `'h'`, `'H'` - Hexagon markers
|
||||
- `'+'` - Plus marker
|
||||
- `'x'` - X marker
|
||||
- `'D'`, `'d'` - Diamond markers
|
||||
|
||||
### Color Specifications
|
||||
|
||||
**Single character shortcuts:**
|
||||
- `'b'` - Blue
|
||||
- `'g'` - Green
|
||||
- `'r'` - Red
|
||||
- `'c'` - Cyan
|
||||
- `'m'` - Magenta
|
||||
- `'y'` - Yellow
|
||||
- `'k'` - Black
|
||||
- `'w'` - White
|
||||
|
||||
**Named colors:**
|
||||
- `'steelblue'`, `'coral'`, `'teal'`, etc.
|
||||
- See full list: https://matplotlib.org/stable/gallery/color/named_colors.html
|
||||
|
||||
**Other formats:**
|
||||
- Hex: `'#FF5733'`
|
||||
- RGB tuple: `(0.1, 0.2, 0.3)`
|
||||
- RGBA tuple: `(0.1, 0.2, 0.3, 0.5)`
|
||||
|
||||
## Common Parameters
|
||||
|
||||
### Plot Function Parameters
|
||||
|
||||
```python
|
||||
ax.plot(x, y,
|
||||
color='blue', # Line color
|
||||
linewidth=2, # Line width
|
||||
linestyle='--', # Line style
|
||||
marker='o', # Marker style
|
||||
markersize=8, # Marker size
|
||||
markerfacecolor='red', # Marker fill color
|
||||
markeredgecolor='black',# Marker edge color
|
||||
markeredgewidth=1, # Marker edge width
|
||||
alpha=0.7, # Transparency (0-1)
|
||||
label='data', # Legend label
|
||||
zorder=2, # Drawing order
|
||||
rasterized=True # Rasterize for smaller file size
|
||||
)
|
||||
```
|
||||
|
||||
### Scatter Function Parameters
|
||||
|
||||
```python
|
||||
ax.scatter(x, y,
|
||||
s=50, # Size (scalar or array)
|
||||
c='blue', # Color (scalar, array, or sequence)
|
||||
marker='o', # Marker style
|
||||
cmap='viridis', # Colormap (if c is numeric)
|
||||
alpha=0.5, # Transparency
|
||||
edgecolors='black', # Edge color
|
||||
linewidths=1, # Edge width
|
||||
vmin=0, vmax=1, # Color scale limits
|
||||
label='data' # Legend label
|
||||
)
|
||||
```
|
||||
|
||||
### Text Parameters
|
||||
|
||||
```python
|
||||
ax.text(x, y, text,
|
||||
fontsize=12, # Font size
|
||||
fontweight='normal', # 'normal', 'bold', 'heavy', 'light'
|
||||
fontstyle='normal', # 'normal', 'italic', 'oblique'
|
||||
fontfamily='sans-serif',# Font family
|
||||
color='black', # Text color
|
||||
alpha=1.0, # Transparency
|
||||
ha='center', # Horizontal alignment: 'left', 'center', 'right'
|
||||
va='center', # Vertical alignment: 'top', 'center', 'bottom', 'baseline'
|
||||
rotation=0, # Rotation angle in degrees
|
||||
bbox=dict( # Background box
|
||||
facecolor='white',
|
||||
edgecolor='black',
|
||||
boxstyle='round'
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## rcParams Configuration
|
||||
|
||||
Common rcParams settings for global customization:
|
||||
|
||||
```python
|
||||
# Font settings
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica']
|
||||
plt.rcParams['font.size'] = 12
|
||||
|
||||
# Figure settings
|
||||
plt.rcParams['figure.figsize'] = (10, 6)
|
||||
plt.rcParams['figure.dpi'] = 100
|
||||
plt.rcParams['figure.facecolor'] = 'white'
|
||||
plt.rcParams['savefig.dpi'] = 300
|
||||
plt.rcParams['savefig.bbox'] = 'tight'
|
||||
|
||||
# Axes settings
|
||||
plt.rcParams['axes.labelsize'] = 14
|
||||
plt.rcParams['axes.titlesize'] = 16
|
||||
plt.rcParams['axes.grid'] = True
|
||||
plt.rcParams['axes.grid.alpha'] = 0.3
|
||||
|
||||
# Line settings
|
||||
plt.rcParams['lines.linewidth'] = 2
|
||||
plt.rcParams['lines.markersize'] = 8
|
||||
|
||||
# Tick settings
|
||||
plt.rcParams['xtick.labelsize'] = 10
|
||||
plt.rcParams['ytick.labelsize'] = 10
|
||||
plt.rcParams['xtick.direction'] = 'in' # 'in', 'out', 'inout'
|
||||
plt.rcParams['ytick.direction'] = 'in'
|
||||
|
||||
# Legend settings
|
||||
plt.rcParams['legend.fontsize'] = 12
|
||||
plt.rcParams['legend.frameon'] = True
|
||||
plt.rcParams['legend.framealpha'] = 0.8
|
||||
|
||||
# Grid settings
|
||||
plt.rcParams['grid.alpha'] = 0.3
|
||||
plt.rcParams['grid.linestyle'] = '--'
|
||||
```
|
||||
|
||||
## GridSpec for Complex Layouts
|
||||
|
||||
```python
|
||||
from matplotlib.gridspec import GridSpec
|
||||
|
||||
fig = plt.figure(figsize=(12, 8))
|
||||
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)
|
||||
|
||||
# Span multiple cells
|
||||
ax1 = fig.add_subplot(gs[0, :]) # Top row, all columns
|
||||
ax2 = fig.add_subplot(gs[1:, 0]) # Bottom two rows, first column
|
||||
ax3 = fig.add_subplot(gs[1, 1:]) # Middle row, last two columns
|
||||
ax4 = fig.add_subplot(gs[2, 1]) # Bottom row, middle column
|
||||
ax5 = fig.add_subplot(gs[2, 2]) # Bottom row, right column
|
||||
```
|
||||
|
||||
## 3D Plotting
|
||||
|
||||
```python
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
|
||||
# Plot types
|
||||
ax.plot(x, y, z) # 3D line
|
||||
ax.scatter(x, y, z) # 3D scatter
|
||||
ax.plot_surface(X, Y, Z) # 3D surface
|
||||
ax.plot_wireframe(X, Y, Z) # 3D wireframe
|
||||
ax.contour(X, Y, Z) # 3D contour
|
||||
ax.bar3d(x, y, z, dx, dy, dz) # 3D bar
|
||||
|
||||
# Customization
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
ax.view_init(elev=30, azim=45) # Set viewing angle
|
||||
```
|
||||
|
||||
## Animation
|
||||
|
||||
```python
|
||||
from matplotlib.animation import FuncAnimation
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
line, = ax.plot([], [])
|
||||
|
||||
def init():
|
||||
ax.set_xlim(0, 2*np.pi)
|
||||
ax.set_ylim(-1, 1)
|
||||
return line,
|
||||
|
||||
def update(frame):
|
||||
x = np.linspace(0, 2*np.pi, 100)
|
||||
y = np.sin(x + frame/10)
|
||||
line.set_data(x, y)
|
||||
return line,
|
||||
|
||||
anim = FuncAnimation(fig, update, init_func=init,
|
||||
frames=100, interval=50, blit=True)
|
||||
|
||||
# Save animation
|
||||
anim.save('animation.gif', writer='pillow', fps=20)
|
||||
anim.save('animation.mp4', writer='ffmpeg', fps=20)
|
||||
```
|
||||
|
||||
## Image Operations
|
||||
|
||||
```python
|
||||
# Read and display image
|
||||
img = plt.imread('image.png')
|
||||
ax.imshow(img)
|
||||
|
||||
# Display matrix as image
|
||||
ax.imshow(matrix, cmap='viridis', aspect='auto',
|
||||
interpolation='nearest', origin='lower')
|
||||
|
||||
# Colorbar
|
||||
cbar = plt.colorbar(im, ax=ax)
|
||||
cbar.set_label('Values')
|
||||
|
||||
# Image extent (set coordinates)
|
||||
ax.imshow(img, extent=[x_min, x_max, y_min, y_max])
|
||||
```
|
||||
|
||||
## Event Handling
|
||||
|
||||
```python
|
||||
# Mouse click event
|
||||
def on_click(event):
|
||||
if event.inaxes:
|
||||
print(f'Clicked at x={event.xdata:.2f}, y={event.ydata:.2f}')
|
||||
|
||||
fig.canvas.mpl_connect('button_press_event', on_click)
|
||||
|
||||
# Key press event
|
||||
def on_key(event):
|
||||
print(f'Key pressed: {event.key}')
|
||||
|
||||
fig.canvas.mpl_connect('key_press_event', on_key)
|
||||
```
|
||||
|
||||
## Useful Utilities
|
||||
|
||||
```python
|
||||
# Get current axis limits
|
||||
xlims = ax.get_xlim()
|
||||
ylims = ax.get_ylim()
|
||||
|
||||
# Set equal aspect ratio
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
|
||||
# Share axes between subplots
|
||||
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
|
||||
|
||||
# Twin axes (two y-axes)
|
||||
ax2 = ax1.twinx()
|
||||
|
||||
# Remove tick labels
|
||||
ax.set_xticklabels([])
|
||||
ax.set_yticklabels([])
|
||||
|
||||
# Scientific notation
|
||||
ax.ticklabel_format(style='scientific', axis='y', scilimits=(0,0))
|
||||
|
||||
# Date formatting
|
||||
import matplotlib.dates as mdates
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
|
||||
ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
|
||||
```
|
||||
563
skills/matplotlib/references/common_issues.md
Normal file
563
skills/matplotlib/references/common_issues.md
Normal file
@@ -0,0 +1,563 @@
|
||||
# Matplotlib Common Issues and Solutions
|
||||
|
||||
Troubleshooting guide for frequently encountered matplotlib problems.
|
||||
|
||||
## Display and Backend Issues
|
||||
|
||||
### Issue: Plots Not Showing
|
||||
|
||||
**Problem:** `plt.show()` doesn't display anything
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# 1. Check if backend is properly set (for interactive use)
|
||||
import matplotlib
|
||||
print(matplotlib.get_backend())
|
||||
|
||||
# 2. Try different backends
|
||||
matplotlib.use('TkAgg') # or 'Qt5Agg', 'MacOSX'
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 3. In Jupyter notebooks, use magic command
|
||||
%matplotlib inline # Static images
|
||||
# or
|
||||
%matplotlib widget # Interactive plots
|
||||
|
||||
# 4. Ensure plt.show() is called
|
||||
plt.plot([1, 2, 3])
|
||||
plt.show()
|
||||
```
|
||||
|
||||
### Issue: "RuntimeError: main thread is not in main loop"
|
||||
|
||||
**Problem:** Interactive mode issues with threading
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Switch to non-interactive backend
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Or turn off interactive mode
|
||||
plt.ioff()
|
||||
```
|
||||
|
||||
### Issue: Figures Not Updating Interactively
|
||||
|
||||
**Problem:** Changes not reflected in interactive windows
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Enable interactive mode
|
||||
plt.ion()
|
||||
|
||||
# Draw after each change
|
||||
plt.plot(x, y)
|
||||
plt.draw()
|
||||
plt.pause(0.001) # Brief pause to update display
|
||||
```
|
||||
|
||||
## Layout and Spacing Issues
|
||||
|
||||
### Issue: Overlapping Labels and Titles
|
||||
|
||||
**Problem:** Labels, titles, or tick labels overlap or get cut off
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Constrained layout (RECOMMENDED)
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
|
||||
# Solution 2: Tight layout
|
||||
fig, ax = plt.subplots()
|
||||
plt.tight_layout()
|
||||
|
||||
# Solution 3: Adjust margins manually
|
||||
plt.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15)
|
||||
|
||||
# Solution 4: Save with bbox_inches='tight'
|
||||
plt.savefig('figure.png', bbox_inches='tight')
|
||||
|
||||
# Solution 5: Rotate long tick labels
|
||||
ax.set_xticklabels(labels, rotation=45, ha='right')
|
||||
```
|
||||
|
||||
### Issue: Colorbar Affects Subplot Size
|
||||
|
||||
**Problem:** Adding colorbar shrinks the plot
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Solution 1: Use constrained layout
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
im = ax.imshow(data)
|
||||
plt.colorbar(im, ax=ax)
|
||||
|
||||
# Solution 2: Manually specify colorbar dimensions
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="5%", pad=0.05)
|
||||
plt.colorbar(im, cax=cax)
|
||||
|
||||
# Solution 3: For multiple subplots, share colorbar
|
||||
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
|
||||
for ax in axes:
|
||||
im = ax.imshow(data)
|
||||
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.95)
|
||||
```
|
||||
|
||||
### Issue: Subplots Too Close Together
|
||||
|
||||
**Problem:** Multiple subplots overlapping
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Solution 1: Use constrained_layout
|
||||
fig, axes = plt.subplots(2, 2, constrained_layout=True)
|
||||
|
||||
# Solution 2: Adjust spacing with subplots_adjust
|
||||
fig, axes = plt.subplots(2, 2)
|
||||
plt.subplots_adjust(hspace=0.4, wspace=0.4)
|
||||
|
||||
# Solution 3: Specify spacing in tight_layout
|
||||
plt.tight_layout(h_pad=2.0, w_pad=2.0)
|
||||
```
|
||||
|
||||
## Memory and Performance Issues
|
||||
|
||||
### Issue: Memory Leak with Multiple Figures
|
||||
|
||||
**Problem:** Memory usage grows when creating many figures
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Close figures explicitly
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
plt.savefig('plot.png')
|
||||
plt.close(fig) # or plt.close('all')
|
||||
|
||||
# Clear current figure without closing
|
||||
plt.clf()
|
||||
|
||||
# Clear current axes
|
||||
plt.cla()
|
||||
```
|
||||
|
||||
### Issue: Large File Sizes
|
||||
|
||||
**Problem:** Saved figures are too large
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Reduce DPI
|
||||
plt.savefig('figure.png', dpi=150) # Instead of 300
|
||||
|
||||
# Solution 2: Use rasterization for complex plots
|
||||
ax.plot(x, y, rasterized=True)
|
||||
|
||||
# Solution 3: Use vector format for simple plots
|
||||
plt.savefig('figure.pdf') # or .svg
|
||||
|
||||
# Solution 4: Compress PNG
|
||||
plt.savefig('figure.png', dpi=300, optimize=True)
|
||||
```
|
||||
|
||||
### Issue: Slow Plotting with Large Datasets
|
||||
|
||||
**Problem:** Plotting takes too long with many points
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Downsample data
|
||||
from scipy.signal import decimate
|
||||
y_downsampled = decimate(y, 10) # Keep every 10th point
|
||||
|
||||
# Solution 2: Use rasterization
|
||||
ax.plot(x, y, rasterized=True)
|
||||
|
||||
# Solution 3: Use line simplification
|
||||
ax.plot(x, y)
|
||||
for line in ax.get_lines():
|
||||
line.set_rasterized(True)
|
||||
|
||||
# Solution 4: For scatter plots, consider hexbin or 2d histogram
|
||||
ax.hexbin(x, y, gridsize=50, cmap='viridis')
|
||||
```
|
||||
|
||||
## Font and Text Issues
|
||||
|
||||
### Issue: Font Warnings
|
||||
|
||||
**Problem:** "findfont: Font family [...] not found"
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Use available fonts
|
||||
from matplotlib.font_manager import findfont, FontProperties
|
||||
print(findfont(FontProperties(family='sans-serif')))
|
||||
|
||||
# Solution 2: Rebuild font cache
|
||||
import matplotlib.font_manager
|
||||
matplotlib.font_manager._rebuild()
|
||||
|
||||
# Solution 3: Suppress warnings
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", category=UserWarning)
|
||||
|
||||
# Solution 4: Specify fallback fonts
|
||||
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'sans-serif']
|
||||
```
|
||||
|
||||
### Issue: LaTeX Rendering Errors
|
||||
|
||||
**Problem:** Math text not rendering correctly
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Use raw strings with r prefix
|
||||
ax.set_xlabel(r'$\alpha$') # Not '\alpha'
|
||||
|
||||
# Solution 2: Escape backslashes in regular strings
|
||||
ax.set_xlabel('$\\alpha$')
|
||||
|
||||
# Solution 3: Disable LaTeX if not installed
|
||||
plt.rcParams['text.usetex'] = False
|
||||
|
||||
# Solution 4: Use mathtext instead of full LaTeX
|
||||
# Mathtext is always available, no LaTeX installation needed
|
||||
ax.text(x, y, r'$\int_0^\infty e^{-x} dx$')
|
||||
```
|
||||
|
||||
### Issue: Text Cut Off or Outside Figure
|
||||
|
||||
**Problem:** Labels or annotations appear outside figure bounds
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Use bbox_inches='tight'
|
||||
plt.savefig('figure.png', bbox_inches='tight')
|
||||
|
||||
# Solution 2: Adjust figure bounds
|
||||
plt.subplots_adjust(left=0.15, right=0.85, top=0.85, bottom=0.15)
|
||||
|
||||
# Solution 3: Clip text to axes
|
||||
ax.text(x, y, 'text', clip_on=True)
|
||||
|
||||
# Solution 4: Use constrained_layout
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
```
|
||||
|
||||
## Color and Colormap Issues
|
||||
|
||||
### Issue: Colorbar Not Matching Plot
|
||||
|
||||
**Problem:** Colorbar shows different range than data
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Explicitly set vmin and vmax
|
||||
im = ax.imshow(data, vmin=0, vmax=1, cmap='viridis')
|
||||
plt.colorbar(im, ax=ax)
|
||||
|
||||
# Or use the same norm for multiple plots
|
||||
import matplotlib.colors as mcolors
|
||||
norm = mcolors.Normalize(vmin=data.min(), vmax=data.max())
|
||||
im1 = ax1.imshow(data1, norm=norm, cmap='viridis')
|
||||
im2 = ax2.imshow(data2, norm=norm, cmap='viridis')
|
||||
```
|
||||
|
||||
### Issue: Colors Look Wrong
|
||||
|
||||
**Problem:** Unexpected colors in plots
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Check color specification format
|
||||
ax.plot(x, y, color='blue') # Correct
|
||||
ax.plot(x, y, color=(0, 0, 1)) # Correct RGB
|
||||
ax.plot(x, y, color='#0000FF') # Correct hex
|
||||
|
||||
# Solution 2: Verify colormap exists
|
||||
print(plt.colormaps()) # List available colormaps
|
||||
|
||||
# Solution 3: For scatter plots, ensure c shape matches
|
||||
ax.scatter(x, y, c=colors) # colors should have same length as x, y
|
||||
|
||||
# Solution 4: Check if alpha is set correctly
|
||||
ax.plot(x, y, alpha=1.0) # 0=transparent, 1=opaque
|
||||
```
|
||||
|
||||
### Issue: Reversed Colormap
|
||||
|
||||
**Problem:** Colormap direction is backwards
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Add _r suffix to reverse any colormap
|
||||
ax.imshow(data, cmap='viridis_r')
|
||||
```
|
||||
|
||||
## Axis and Scale Issues
|
||||
|
||||
### Issue: Axis Limits Not Working
|
||||
|
||||
**Problem:** `set_xlim` or `set_ylim` not taking effect
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Set after plotting
|
||||
ax.plot(x, y)
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(-1, 1)
|
||||
|
||||
# Solution 2: Disable autoscaling
|
||||
ax.autoscale(False)
|
||||
ax.set_xlim(0, 10)
|
||||
|
||||
# Solution 3: Use axis method
|
||||
ax.axis([xmin, xmax, ymin, ymax])
|
||||
```
|
||||
|
||||
### Issue: Log Scale with Zero or Negative Values
|
||||
|
||||
**Problem:** ValueError when using log scale with data ≤ 0
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Filter out non-positive values
|
||||
mask = (data > 0)
|
||||
ax.plot(x[mask], data[mask])
|
||||
ax.set_yscale('log')
|
||||
|
||||
# Solution 2: Use symlog for data with positive and negative values
|
||||
ax.set_yscale('symlog')
|
||||
|
||||
# Solution 3: Add small offset
|
||||
ax.plot(x, data + 1e-10)
|
||||
ax.set_yscale('log')
|
||||
```
|
||||
|
||||
### Issue: Dates Not Displaying Correctly
|
||||
|
||||
**Problem:** Date axis shows numbers instead of dates
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
import matplotlib.dates as mdates
|
||||
import pandas as pd
|
||||
|
||||
# Convert to datetime if needed
|
||||
dates = pd.to_datetime(date_strings)
|
||||
|
||||
ax.plot(dates, values)
|
||||
|
||||
# Format date axis
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
|
||||
ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
|
||||
plt.xticks(rotation=45)
|
||||
```
|
||||
|
||||
## Legend Issues
|
||||
|
||||
### Issue: Legend Covers Data
|
||||
|
||||
**Problem:** Legend obscures important parts of plot
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Use 'best' location
|
||||
ax.legend(loc='best')
|
||||
|
||||
# Solution 2: Place outside plot area
|
||||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
|
||||
|
||||
# Solution 3: Make legend semi-transparent
|
||||
ax.legend(framealpha=0.7)
|
||||
|
||||
# Solution 4: Put legend below plot
|
||||
ax.legend(bbox_to_anchor=(0.5, -0.15), loc='upper center', ncol=3)
|
||||
```
|
||||
|
||||
### Issue: Too Many Items in Legend
|
||||
|
||||
**Problem:** Legend is cluttered with many entries
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Only label selected items
|
||||
for i, (x, y) in enumerate(data):
|
||||
label = f'Data {i}' if i % 5 == 0 else None
|
||||
ax.plot(x, y, label=label)
|
||||
|
||||
# Solution 2: Use multiple columns
|
||||
ax.legend(ncol=3)
|
||||
|
||||
# Solution 3: Create custom legend with fewer entries
|
||||
from matplotlib.lines import Line2D
|
||||
custom_lines = [Line2D([0], [0], color='r'),
|
||||
Line2D([0], [0], color='b')]
|
||||
ax.legend(custom_lines, ['Category A', 'Category B'])
|
||||
|
||||
# Solution 4: Use separate legend figure
|
||||
fig_leg = plt.figure(figsize=(3, 2))
|
||||
ax_leg = fig_leg.add_subplot(111)
|
||||
ax_leg.legend(*ax.get_legend_handles_labels(), loc='center')
|
||||
ax_leg.axis('off')
|
||||
```
|
||||
|
||||
## 3D Plot Issues
|
||||
|
||||
### Issue: 3D Plots Look Flat
|
||||
|
||||
**Problem:** Difficult to perceive depth in 3D plots
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Adjust viewing angle
|
||||
ax.view_init(elev=30, azim=45)
|
||||
|
||||
# Solution 2: Add gridlines
|
||||
ax.grid(True)
|
||||
|
||||
# Solution 3: Use color for depth
|
||||
scatter = ax.scatter(x, y, z, c=z, cmap='viridis')
|
||||
|
||||
# Solution 4: Rotate interactively (if using interactive backend)
|
||||
# User can click and drag to rotate
|
||||
```
|
||||
|
||||
### Issue: 3D Axis Labels Cut Off
|
||||
|
||||
**Problem:** 3D axis labels appear outside figure
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.plot_surface(X, Y, Z)
|
||||
|
||||
# Add padding
|
||||
fig.tight_layout(pad=3.0)
|
||||
|
||||
# Or save with tight bounding box
|
||||
plt.savefig('3d_plot.png', bbox_inches='tight', pad_inches=0.5)
|
||||
```
|
||||
|
||||
## Image and Colorbar Issues
|
||||
|
||||
### Issue: Images Appear Flipped
|
||||
|
||||
**Problem:** Image orientation is wrong
|
||||
|
||||
**Solution:**
|
||||
```python
|
||||
# Set origin parameter
|
||||
ax.imshow(img, origin='lower') # or 'upper' (default)
|
||||
|
||||
# Or flip array
|
||||
ax.imshow(np.flipud(img))
|
||||
```
|
||||
|
||||
### Issue: Images Look Pixelated
|
||||
|
||||
**Problem:** Image appears blocky when zoomed
|
||||
|
||||
**Solutions:**
|
||||
```python
|
||||
# Solution 1: Use interpolation
|
||||
ax.imshow(img, interpolation='bilinear')
|
||||
# Options: 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', etc.
|
||||
|
||||
# Solution 2: Increase DPI when saving
|
||||
plt.savefig('figure.png', dpi=300)
|
||||
|
||||
# Solution 3: Use vector format if appropriate
|
||||
plt.savefig('figure.pdf')
|
||||
```
|
||||
|
||||
## Common Errors and Fixes
|
||||
|
||||
### "TypeError: 'AxesSubplot' object is not subscriptable"
|
||||
|
||||
**Problem:** Trying to index single axes
|
||||
```python
|
||||
# Wrong
|
||||
fig, ax = plt.subplots()
|
||||
ax[0].plot(x, y) # Error!
|
||||
|
||||
# Correct
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
```
|
||||
|
||||
### "ValueError: x and y must have same first dimension"
|
||||
|
||||
**Problem:** Data arrays have mismatched lengths
|
||||
```python
|
||||
# Check shapes
|
||||
print(f"x shape: {x.shape}, y shape: {y.shape}")
|
||||
|
||||
# Ensure they match
|
||||
assert len(x) == len(y), "x and y must have same length"
|
||||
```
|
||||
|
||||
### "AttributeError: 'numpy.ndarray' object has no attribute 'plot'"
|
||||
|
||||
**Problem:** Calling plot on array instead of axes
|
||||
```python
|
||||
# Wrong
|
||||
data.plot(x, y)
|
||||
|
||||
# Correct
|
||||
ax.plot(x, y)
|
||||
# or for pandas
|
||||
data.plot(ax=ax)
|
||||
```
|
||||
|
||||
## Best Practices to Avoid Issues
|
||||
|
||||
1. **Always use the OO interface** - Avoid pyplot state machine
|
||||
```python
|
||||
fig, ax = plt.subplots() # Good
|
||||
ax.plot(x, y)
|
||||
```
|
||||
|
||||
2. **Use constrained_layout** - Prevents overlap issues
|
||||
```python
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
```
|
||||
|
||||
3. **Close figures explicitly** - Prevents memory leaks
|
||||
```python
|
||||
plt.close(fig)
|
||||
```
|
||||
|
||||
4. **Set figure size at creation** - Better than resizing later
|
||||
```python
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
```
|
||||
|
||||
5. **Use raw strings for math text** - Avoids escape issues
|
||||
```python
|
||||
ax.set_xlabel(r'$\alpha$')
|
||||
```
|
||||
|
||||
6. **Check data shapes before plotting** - Catch size mismatches early
|
||||
```python
|
||||
assert len(x) == len(y)
|
||||
```
|
||||
|
||||
7. **Use appropriate DPI** - 300 for print, 150 for web
|
||||
```python
|
||||
plt.savefig('figure.png', dpi=300)
|
||||
```
|
||||
|
||||
8. **Test with different backends** - If display issues occur
|
||||
```python
|
||||
import matplotlib
|
||||
matplotlib.use('TkAgg')
|
||||
```
|
||||
476
skills/matplotlib/references/plot_types.md
Normal file
476
skills/matplotlib/references/plot_types.md
Normal file
@@ -0,0 +1,476 @@
|
||||
# Matplotlib Plot Types Guide
|
||||
|
||||
Comprehensive guide to different plot types in matplotlib with examples and use cases.
|
||||
|
||||
## 1. Line Plots
|
||||
|
||||
**Use cases:** Time series, continuous data, trends, function visualization
|
||||
|
||||
### Basic Line Plot
|
||||
```python
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
ax.plot(x, y, linewidth=2, label='Data')
|
||||
ax.set_xlabel('X axis')
|
||||
ax.set_ylabel('Y axis')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Multiple Lines
|
||||
```python
|
||||
ax.plot(x, y1, label='Dataset 1', linewidth=2)
|
||||
ax.plot(x, y2, label='Dataset 2', linewidth=2, linestyle='--')
|
||||
ax.plot(x, y3, label='Dataset 3', linewidth=2, linestyle=':')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Line with Markers
|
||||
```python
|
||||
ax.plot(x, y, marker='o', markersize=8, linestyle='-',
|
||||
linewidth=2, markerfacecolor='red', markeredgecolor='black')
|
||||
```
|
||||
|
||||
### Step Plot
|
||||
```python
|
||||
ax.step(x, y, where='mid', linewidth=2, label='Step function')
|
||||
# where options: 'pre', 'post', 'mid'
|
||||
```
|
||||
|
||||
### Error Bars
|
||||
```python
|
||||
ax.errorbar(x, y, yerr=error, fmt='o-', linewidth=2,
|
||||
capsize=5, capthick=2, label='With uncertainty')
|
||||
```
|
||||
|
||||
## 2. Scatter Plots
|
||||
|
||||
**Use cases:** Correlations, relationships between variables, clusters, outliers
|
||||
|
||||
### Basic Scatter
|
||||
```python
|
||||
ax.scatter(x, y, s=50, alpha=0.6)
|
||||
```
|
||||
|
||||
### Sized and Colored Scatter
|
||||
```python
|
||||
scatter = ax.scatter(x, y, s=sizes*100, c=colors,
|
||||
cmap='viridis', alpha=0.6, edgecolors='black')
|
||||
plt.colorbar(scatter, ax=ax, label='Color variable')
|
||||
```
|
||||
|
||||
### Categorical Scatter
|
||||
```python
|
||||
for category in categories:
|
||||
mask = data['category'] == category
|
||||
ax.scatter(data[mask]['x'], data[mask]['y'],
|
||||
label=category, s=50, alpha=0.7)
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
## 3. Bar Charts
|
||||
|
||||
**Use cases:** Categorical comparisons, discrete data, counts
|
||||
|
||||
### Vertical Bar Chart
|
||||
```python
|
||||
ax.bar(categories, values, color='steelblue',
|
||||
edgecolor='black', linewidth=1.5)
|
||||
ax.set_ylabel('Values')
|
||||
```
|
||||
|
||||
### Horizontal Bar Chart
|
||||
```python
|
||||
ax.barh(categories, values, color='coral',
|
||||
edgecolor='black', linewidth=1.5)
|
||||
ax.set_xlabel('Values')
|
||||
```
|
||||
|
||||
### Grouped Bar Chart
|
||||
```python
|
||||
x = np.arange(len(categories))
|
||||
width = 0.35
|
||||
|
||||
ax.bar(x - width/2, values1, width, label='Group 1')
|
||||
ax.bar(x + width/2, values2, width, label='Group 2')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(categories)
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Stacked Bar Chart
|
||||
```python
|
||||
ax.bar(categories, values1, label='Part 1')
|
||||
ax.bar(categories, values2, bottom=values1, label='Part 2')
|
||||
ax.bar(categories, values3, bottom=values1+values2, label='Part 3')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Bar Chart with Error Bars
|
||||
```python
|
||||
ax.bar(categories, values, yerr=errors, capsize=5,
|
||||
color='steelblue', edgecolor='black')
|
||||
```
|
||||
|
||||
### Bar Chart with Patterns
|
||||
```python
|
||||
bars1 = ax.bar(x - width/2, values1, width, label='Group 1',
|
||||
color='white', edgecolor='black', hatch='//')
|
||||
bars2 = ax.bar(x + width/2, values2, width, label='Group 2',
|
||||
color='white', edgecolor='black', hatch='\\\\')
|
||||
```
|
||||
|
||||
## 4. Histograms
|
||||
|
||||
**Use cases:** Distributions, frequency analysis
|
||||
|
||||
### Basic Histogram
|
||||
```python
|
||||
ax.hist(data, bins=30, edgecolor='black', alpha=0.7)
|
||||
ax.set_xlabel('Value')
|
||||
ax.set_ylabel('Frequency')
|
||||
```
|
||||
|
||||
### Multiple Overlapping Histograms
|
||||
```python
|
||||
ax.hist(data1, bins=30, alpha=0.5, label='Dataset 1')
|
||||
ax.hist(data2, bins=30, alpha=0.5, label='Dataset 2')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Normalized Histogram (Density)
|
||||
```python
|
||||
ax.hist(data, bins=30, density=True, alpha=0.7,
|
||||
edgecolor='black', label='Empirical')
|
||||
|
||||
# Overlay theoretical distribution
|
||||
from scipy.stats import norm
|
||||
x = np.linspace(data.min(), data.max(), 100)
|
||||
ax.plot(x, norm.pdf(x, data.mean(), data.std()),
|
||||
'r-', linewidth=2, label='Normal fit')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### 2D Histogram (Hexbin)
|
||||
```python
|
||||
hexbin = ax.hexbin(x, y, gridsize=30, cmap='Blues')
|
||||
plt.colorbar(hexbin, ax=ax, label='Counts')
|
||||
```
|
||||
|
||||
### 2D Histogram (hist2d)
|
||||
```python
|
||||
h = ax.hist2d(x, y, bins=30, cmap='Blues')
|
||||
plt.colorbar(h[3], ax=ax, label='Counts')
|
||||
```
|
||||
|
||||
## 5. Box and Violin Plots
|
||||
|
||||
**Use cases:** Statistical distributions, outlier detection, comparing distributions
|
||||
|
||||
### Box Plot
|
||||
```python
|
||||
ax.boxplot([data1, data2, data3],
|
||||
labels=['Group A', 'Group B', 'Group C'],
|
||||
showmeans=True, meanline=True)
|
||||
ax.set_ylabel('Values')
|
||||
```
|
||||
|
||||
### Horizontal Box Plot
|
||||
```python
|
||||
ax.boxplot([data1, data2, data3], vert=False,
|
||||
labels=['Group A', 'Group B', 'Group C'])
|
||||
ax.set_xlabel('Values')
|
||||
```
|
||||
|
||||
### Violin Plot
|
||||
```python
|
||||
parts = ax.violinplot([data1, data2, data3],
|
||||
positions=[1, 2, 3],
|
||||
showmeans=True, showmedians=True)
|
||||
ax.set_xticks([1, 2, 3])
|
||||
ax.set_xticklabels(['Group A', 'Group B', 'Group C'])
|
||||
```
|
||||
|
||||
## 6. Heatmaps
|
||||
|
||||
**Use cases:** Matrix data, correlations, intensity maps
|
||||
|
||||
### Basic Heatmap
|
||||
```python
|
||||
im = ax.imshow(matrix, cmap='coolwarm', aspect='auto')
|
||||
plt.colorbar(im, ax=ax, label='Values')
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
```
|
||||
|
||||
### Heatmap with Annotations
|
||||
```python
|
||||
im = ax.imshow(matrix, cmap='coolwarm')
|
||||
plt.colorbar(im, ax=ax)
|
||||
|
||||
# Add text annotations
|
||||
for i in range(matrix.shape[0]):
|
||||
for j in range(matrix.shape[1]):
|
||||
text = ax.text(j, i, f'{matrix[i, j]:.2f}',
|
||||
ha='center', va='center', color='black')
|
||||
```
|
||||
|
||||
### Correlation Matrix
|
||||
```python
|
||||
corr = data.corr()
|
||||
im = ax.imshow(corr, cmap='RdBu_r', vmin=-1, vmax=1)
|
||||
plt.colorbar(im, ax=ax, label='Correlation')
|
||||
|
||||
# Set tick labels
|
||||
ax.set_xticks(range(len(corr)))
|
||||
ax.set_yticks(range(len(corr)))
|
||||
ax.set_xticklabels(corr.columns, rotation=45, ha='right')
|
||||
ax.set_yticklabels(corr.columns)
|
||||
```
|
||||
|
||||
## 7. Contour Plots
|
||||
|
||||
**Use cases:** 3D data on 2D plane, topography, function visualization
|
||||
|
||||
### Contour Lines
|
||||
```python
|
||||
contour = ax.contour(X, Y, Z, levels=10, cmap='viridis')
|
||||
ax.clabel(contour, inline=True, fontsize=8)
|
||||
plt.colorbar(contour, ax=ax)
|
||||
```
|
||||
|
||||
### Filled Contours
|
||||
```python
|
||||
contourf = ax.contourf(X, Y, Z, levels=20, cmap='viridis')
|
||||
plt.colorbar(contourf, ax=ax)
|
||||
```
|
||||
|
||||
### Combined Contours
|
||||
```python
|
||||
contourf = ax.contourf(X, Y, Z, levels=20, cmap='viridis', alpha=0.8)
|
||||
contour = ax.contour(X, Y, Z, levels=10, colors='black',
|
||||
linewidths=0.5, alpha=0.4)
|
||||
ax.clabel(contour, inline=True, fontsize=8)
|
||||
plt.colorbar(contourf, ax=ax)
|
||||
```
|
||||
|
||||
## 8. Pie Charts
|
||||
|
||||
**Use cases:** Proportions, percentages (use sparingly)
|
||||
|
||||
### Basic Pie Chart
|
||||
```python
|
||||
ax.pie(sizes, labels=labels, autopct='%1.1f%%',
|
||||
startangle=90, colors=colors)
|
||||
ax.axis('equal') # Equal aspect ratio ensures circular pie
|
||||
```
|
||||
|
||||
### Exploded Pie Chart
|
||||
```python
|
||||
explode = (0.1, 0, 0, 0) # Explode first slice
|
||||
ax.pie(sizes, explode=explode, labels=labels,
|
||||
autopct='%1.1f%%', shadow=True, startangle=90)
|
||||
ax.axis('equal')
|
||||
```
|
||||
|
||||
### Donut Chart
|
||||
```python
|
||||
ax.pie(sizes, labels=labels, autopct='%1.1f%%',
|
||||
wedgeprops=dict(width=0.5), startangle=90)
|
||||
ax.axis('equal')
|
||||
```
|
||||
|
||||
## 9. Polar Plots
|
||||
|
||||
**Use cases:** Cyclic data, directional data, radar charts
|
||||
|
||||
### Basic Polar Plot
|
||||
```python
|
||||
theta = np.linspace(0, 2*np.pi, 100)
|
||||
r = np.abs(np.sin(2*theta))
|
||||
|
||||
ax = plt.subplot(111, projection='polar')
|
||||
ax.plot(theta, r, linewidth=2)
|
||||
```
|
||||
|
||||
### Radar Chart
|
||||
```python
|
||||
categories = ['A', 'B', 'C', 'D', 'E']
|
||||
values = [4, 3, 5, 2, 4]
|
||||
|
||||
# Add first value to the end to close the polygon
|
||||
angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False)
|
||||
values_closed = np.concatenate((values, [values[0]]))
|
||||
angles_closed = np.concatenate((angles, [angles[0]]))
|
||||
|
||||
ax = plt.subplot(111, projection='polar')
|
||||
ax.plot(angles_closed, values_closed, 'o-', linewidth=2)
|
||||
ax.fill(angles_closed, values_closed, alpha=0.25)
|
||||
ax.set_xticks(angles)
|
||||
ax.set_xticklabels(categories)
|
||||
```
|
||||
|
||||
## 10. Stream and Quiver Plots
|
||||
|
||||
**Use cases:** Vector fields, flow visualization
|
||||
|
||||
### Quiver Plot (Vector Field)
|
||||
```python
|
||||
ax.quiver(X, Y, U, V, alpha=0.8)
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_aspect('equal')
|
||||
```
|
||||
|
||||
### Stream Plot
|
||||
```python
|
||||
ax.streamplot(X, Y, U, V, density=1.5, color='k', linewidth=1)
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_aspect('equal')
|
||||
```
|
||||
|
||||
## 11. Fill Between
|
||||
|
||||
**Use cases:** Uncertainty bounds, confidence intervals, areas under curves
|
||||
|
||||
### Fill Between Two Curves
|
||||
```python
|
||||
ax.plot(x, y, 'k-', linewidth=2, label='Mean')
|
||||
ax.fill_between(x, y - std, y + std, alpha=0.3,
|
||||
label='±1 std dev')
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
### Fill Between with Condition
|
||||
```python
|
||||
ax.plot(x, y1, label='Line 1')
|
||||
ax.plot(x, y2, label='Line 2')
|
||||
ax.fill_between(x, y1, y2, where=(y2 >= y1),
|
||||
alpha=0.3, label='y2 > y1', interpolate=True)
|
||||
ax.legend()
|
||||
```
|
||||
|
||||
## 12. 3D Plots
|
||||
|
||||
**Use cases:** Three-dimensional data visualization
|
||||
|
||||
### 3D Scatter
|
||||
```python
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
scatter = ax.scatter(x, y, z, c=colors, cmap='viridis',
|
||||
marker='o', s=50)
|
||||
plt.colorbar(scatter, ax=ax)
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
```
|
||||
|
||||
### 3D Surface Plot
|
||||
```python
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
surf = ax.plot_surface(X, Y, Z, cmap='viridis',
|
||||
edgecolor='none', alpha=0.9)
|
||||
plt.colorbar(surf, ax=ax)
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
```
|
||||
|
||||
### 3D Wireframe
|
||||
```python
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.plot_wireframe(X, Y, Z, color='black', linewidth=0.5)
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
```
|
||||
|
||||
### 3D Contour
|
||||
```python
|
||||
fig = plt.figure(figsize=(10, 8))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.contour(X, Y, Z, levels=15, cmap='viridis')
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
```
|
||||
|
||||
## 13. Specialized Plots
|
||||
|
||||
### Stem Plot
|
||||
```python
|
||||
ax.stem(x, y, linefmt='C0-', markerfmt='C0o', basefmt='k-')
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
```
|
||||
|
||||
### Filled Polygon
|
||||
```python
|
||||
vertices = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
||||
from matplotlib.patches import Polygon
|
||||
polygon = Polygon(vertices, closed=True, edgecolor='black',
|
||||
facecolor='lightblue', alpha=0.5)
|
||||
ax.add_patch(polygon)
|
||||
ax.set_xlim(-0.5, 1.5)
|
||||
ax.set_ylim(-0.5, 1.5)
|
||||
```
|
||||
|
||||
### Staircase Plot
|
||||
```python
|
||||
ax.stairs(values, edges, fill=True, alpha=0.5)
|
||||
```
|
||||
|
||||
### Broken Barh (Gantt-style)
|
||||
```python
|
||||
ax.broken_barh([(10, 50), (100, 20), (130, 10)], (10, 9),
|
||||
facecolors='tab:blue')
|
||||
ax.broken_barh([(10, 20), (50, 50), (120, 30)], (20, 9),
|
||||
facecolors='tab:orange')
|
||||
ax.set_ylim(5, 35)
|
||||
ax.set_xlim(0, 200)
|
||||
ax.set_xlabel('Time')
|
||||
ax.set_yticks([15, 25])
|
||||
ax.set_yticklabels(['Task 1', 'Task 2'])
|
||||
```
|
||||
|
||||
## 14. Time Series Plots
|
||||
|
||||
### Basic Time Series
|
||||
```python
|
||||
import pandas as pd
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
ax.plot(dates, values, linewidth=2)
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
|
||||
ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
|
||||
plt.xticks(rotation=45)
|
||||
ax.set_xlabel('Date')
|
||||
ax.set_ylabel('Value')
|
||||
```
|
||||
|
||||
### Time Series with Shaded Regions
|
||||
```python
|
||||
ax.plot(dates, values, linewidth=2)
|
||||
# Shade weekends or specific periods
|
||||
ax.axvspan(start_date, end_date, alpha=0.2, color='gray')
|
||||
```
|
||||
|
||||
## Plot Selection Guide
|
||||
|
||||
| Data Type | Recommended Plot | Alternative Options |
|
||||
|-----------|-----------------|---------------------|
|
||||
| Single continuous variable | Histogram, KDE | Box plot, Violin plot |
|
||||
| Two continuous variables | Scatter plot | Hexbin, 2D histogram |
|
||||
| Time series | Line plot | Area plot, Step plot |
|
||||
| Categorical vs continuous | Bar chart, Box plot | Violin plot, Strip plot |
|
||||
| Two categorical variables | Heatmap | Grouped bar chart |
|
||||
| Three continuous variables | 3D scatter, Contour | Color-coded scatter |
|
||||
| Proportions | Bar chart | Pie chart (use sparingly) |
|
||||
| Distributions comparison | Box plot, Violin plot | Overlaid histograms |
|
||||
| Correlation matrix | Heatmap | Clustered heatmap |
|
||||
| Vector field | Quiver plot, Stream plot | - |
|
||||
| Function visualization | Line plot, Contour | 3D surface |
|
||||
589
skills/matplotlib/references/styling_guide.md
Normal file
589
skills/matplotlib/references/styling_guide.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# Matplotlib Styling Guide
|
||||
|
||||
Comprehensive guide for styling and customizing matplotlib visualizations.
|
||||
|
||||
## Colormaps
|
||||
|
||||
### Colormap Categories
|
||||
|
||||
**1. Perceptually Uniform Sequential**
|
||||
Best for ordered data that progresses from low to high values.
|
||||
- `viridis` (default, colorblind-friendly)
|
||||
- `plasma`
|
||||
- `inferno`
|
||||
- `magma`
|
||||
- `cividis` (optimized for colorblind viewers)
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
im = ax.imshow(data, cmap='viridis')
|
||||
scatter = ax.scatter(x, y, c=values, cmap='plasma')
|
||||
```
|
||||
|
||||
**2. Sequential**
|
||||
Traditional colormaps for ordered data.
|
||||
- `Blues`, `Greens`, `Reds`, `Oranges`, `Purples`
|
||||
- `YlOrBr`, `YlOrRd`, `OrRd`, `PuRd`
|
||||
- `BuPu`, `GnBu`, `PuBu`, `YlGnBu`
|
||||
|
||||
**3. Diverging**
|
||||
Best for data with a meaningful center point (e.g., zero, mean).
|
||||
- `coolwarm` (blue to red)
|
||||
- `RdBu` (red-blue)
|
||||
- `RdYlBu` (red-yellow-blue)
|
||||
- `RdYlGn` (red-yellow-green)
|
||||
- `PiYG`, `PRGn`, `BrBG`, `PuOr`, `RdGy`
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
# Center colormap at zero
|
||||
im = ax.imshow(data, cmap='coolwarm', vmin=-1, vmax=1)
|
||||
```
|
||||
|
||||
**4. Qualitative**
|
||||
Best for categorical/nominal data without inherent ordering.
|
||||
- `tab10` (10 distinct colors)
|
||||
- `tab20` (20 distinct colors)
|
||||
- `Set1`, `Set2`, `Set3`
|
||||
- `Pastel1`, `Pastel2`
|
||||
- `Dark2`, `Accent`, `Paired`
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
colors = plt.cm.tab10(np.linspace(0, 1, n_categories))
|
||||
for i, category in enumerate(categories):
|
||||
ax.plot(x, y[i], color=colors[i], label=category)
|
||||
```
|
||||
|
||||
**5. Cyclic**
|
||||
Best for cyclic data (e.g., phase, angle).
|
||||
- `twilight`
|
||||
- `twilight_shifted`
|
||||
- `hsv`
|
||||
|
||||
### Colormap Best Practices
|
||||
|
||||
1. **Avoid `jet` colormap** - Not perceptually uniform, misleading
|
||||
2. **Use perceptually uniform colormaps** - `viridis`, `plasma`, `cividis`
|
||||
3. **Consider colorblind users** - Use `viridis`, `cividis`, or test with colorblind simulators
|
||||
4. **Match colormap to data type**:
|
||||
- Sequential: increasing/decreasing data
|
||||
- Diverging: data with meaningful center
|
||||
- Qualitative: categories
|
||||
5. **Reverse colormaps** - Add `_r` suffix: `viridis_r`, `coolwarm_r`
|
||||
|
||||
### Creating Custom Colormaps
|
||||
|
||||
```python
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
|
||||
# From color list
|
||||
colors = ['blue', 'white', 'red']
|
||||
n_bins = 100
|
||||
cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins)
|
||||
|
||||
# From RGB values
|
||||
colors = [(0, 0, 1), (1, 1, 1), (1, 0, 0)] # RGB tuples
|
||||
cmap = LinearSegmentedColormap.from_list('custom', colors)
|
||||
|
||||
# Use the custom colormap
|
||||
ax.imshow(data, cmap=cmap)
|
||||
```
|
||||
|
||||
### Discrete Colormaps
|
||||
|
||||
```python
|
||||
import matplotlib.colors as mcolors
|
||||
|
||||
# Create discrete colormap from continuous
|
||||
cmap = plt.cm.viridis
|
||||
bounds = np.linspace(0, 10, 11)
|
||||
norm = mcolors.BoundaryNorm(bounds, cmap.N)
|
||||
im = ax.imshow(data, cmap=cmap, norm=norm)
|
||||
```
|
||||
|
||||
## Style Sheets
|
||||
|
||||
### Using Built-in Styles
|
||||
|
||||
```python
|
||||
# List available styles
|
||||
print(plt.style.available)
|
||||
|
||||
# Apply a style
|
||||
plt.style.use('seaborn-v0_8-darkgrid')
|
||||
|
||||
# Apply multiple styles (later styles override earlier ones)
|
||||
plt.style.use(['seaborn-v0_8-whitegrid', 'seaborn-v0_8-poster'])
|
||||
|
||||
# Temporarily use a style
|
||||
with plt.style.context('ggplot'):
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
```
|
||||
|
||||
### Popular Built-in Styles
|
||||
|
||||
- `default` - Matplotlib's default style
|
||||
- `classic` - Classic matplotlib look (pre-2.0)
|
||||
- `seaborn-v0_8-*` - Seaborn-inspired styles
|
||||
- `seaborn-v0_8-darkgrid`, `seaborn-v0_8-whitegrid`
|
||||
- `seaborn-v0_8-dark`, `seaborn-v0_8-white`
|
||||
- `seaborn-v0_8-ticks`, `seaborn-v0_8-poster`, `seaborn-v0_8-talk`
|
||||
- `ggplot` - ggplot2-inspired style
|
||||
- `bmh` - Bayesian Methods for Hackers style
|
||||
- `fivethirtyeight` - FiveThirtyEight style
|
||||
- `grayscale` - Grayscale style
|
||||
|
||||
### Creating Custom Style Sheets
|
||||
|
||||
Create a file named `custom_style.mplstyle`:
|
||||
|
||||
```
|
||||
# custom_style.mplstyle
|
||||
|
||||
# Figure
|
||||
figure.figsize: 10, 6
|
||||
figure.dpi: 100
|
||||
figure.facecolor: white
|
||||
|
||||
# Font
|
||||
font.family: sans-serif
|
||||
font.sans-serif: Arial, Helvetica
|
||||
font.size: 12
|
||||
|
||||
# Axes
|
||||
axes.labelsize: 14
|
||||
axes.titlesize: 16
|
||||
axes.facecolor: white
|
||||
axes.edgecolor: black
|
||||
axes.linewidth: 1.5
|
||||
axes.grid: True
|
||||
axes.axisbelow: True
|
||||
|
||||
# Grid
|
||||
grid.color: gray
|
||||
grid.linestyle: --
|
||||
grid.linewidth: 0.5
|
||||
grid.alpha: 0.3
|
||||
|
||||
# Lines
|
||||
lines.linewidth: 2
|
||||
lines.markersize: 8
|
||||
|
||||
# Ticks
|
||||
xtick.labelsize: 10
|
||||
ytick.labelsize: 10
|
||||
xtick.direction: in
|
||||
ytick.direction: in
|
||||
xtick.major.size: 6
|
||||
ytick.major.size: 6
|
||||
xtick.minor.size: 3
|
||||
ytick.minor.size: 3
|
||||
|
||||
# Legend
|
||||
legend.fontsize: 12
|
||||
legend.frameon: True
|
||||
legend.framealpha: 0.8
|
||||
legend.fancybox: True
|
||||
|
||||
# Savefig
|
||||
savefig.dpi: 300
|
||||
savefig.bbox: tight
|
||||
savefig.facecolor: white
|
||||
```
|
||||
|
||||
Load and use:
|
||||
```python
|
||||
plt.style.use('path/to/custom_style.mplstyle')
|
||||
```
|
||||
|
||||
## rcParams Configuration
|
||||
|
||||
### Global Configuration
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Configure globally
|
||||
plt.rcParams['figure.figsize'] = (10, 6)
|
||||
plt.rcParams['font.size'] = 12
|
||||
plt.rcParams['axes.labelsize'] = 14
|
||||
|
||||
# Or update multiple at once
|
||||
plt.rcParams.update({
|
||||
'figure.figsize': (10, 6),
|
||||
'font.size': 12,
|
||||
'axes.labelsize': 14,
|
||||
'axes.titlesize': 16,
|
||||
'lines.linewidth': 2
|
||||
})
|
||||
```
|
||||
|
||||
### Temporary Configuration
|
||||
|
||||
```python
|
||||
# Context manager for temporary changes
|
||||
with plt.rc_context({'font.size': 14, 'lines.linewidth': 2.5}):
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
```
|
||||
|
||||
### Common rcParams
|
||||
|
||||
**Figure settings:**
|
||||
```python
|
||||
plt.rcParams['figure.figsize'] = (10, 6)
|
||||
plt.rcParams['figure.dpi'] = 100
|
||||
plt.rcParams['figure.facecolor'] = 'white'
|
||||
plt.rcParams['figure.edgecolor'] = 'white'
|
||||
plt.rcParams['figure.autolayout'] = False
|
||||
plt.rcParams['figure.constrained_layout.use'] = True
|
||||
```
|
||||
|
||||
**Font settings:**
|
||||
```python
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica', 'DejaVu Sans']
|
||||
plt.rcParams['font.size'] = 12
|
||||
plt.rcParams['font.weight'] = 'normal'
|
||||
```
|
||||
|
||||
**Axes settings:**
|
||||
```python
|
||||
plt.rcParams['axes.facecolor'] = 'white'
|
||||
plt.rcParams['axes.edgecolor'] = 'black'
|
||||
plt.rcParams['axes.linewidth'] = 1.5
|
||||
plt.rcParams['axes.grid'] = True
|
||||
plt.rcParams['axes.labelsize'] = 14
|
||||
plt.rcParams['axes.titlesize'] = 16
|
||||
plt.rcParams['axes.labelweight'] = 'normal'
|
||||
plt.rcParams['axes.spines.top'] = True
|
||||
plt.rcParams['axes.spines.right'] = True
|
||||
```
|
||||
|
||||
**Line settings:**
|
||||
```python
|
||||
plt.rcParams['lines.linewidth'] = 2
|
||||
plt.rcParams['lines.linestyle'] = '-'
|
||||
plt.rcParams['lines.marker'] = 'None'
|
||||
plt.rcParams['lines.markersize'] = 6
|
||||
```
|
||||
|
||||
**Save settings:**
|
||||
```python
|
||||
plt.rcParams['savefig.dpi'] = 300
|
||||
plt.rcParams['savefig.format'] = 'png'
|
||||
plt.rcParams['savefig.bbox'] = 'tight'
|
||||
plt.rcParams['savefig.pad_inches'] = 0.1
|
||||
plt.rcParams['savefig.transparent'] = False
|
||||
```
|
||||
|
||||
## Color Palettes
|
||||
|
||||
### Named Color Sets
|
||||
|
||||
```python
|
||||
# Tableau colors
|
||||
tableau_colors = plt.cm.tab10.colors
|
||||
|
||||
# CSS4 colors (subset)
|
||||
css_colors = ['steelblue', 'coral', 'teal', 'goldenrod', 'crimson']
|
||||
|
||||
# Manual definition
|
||||
custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
|
||||
```
|
||||
|
||||
### Color Cycles
|
||||
|
||||
```python
|
||||
# Set default color cycle
|
||||
from cycler import cycler
|
||||
colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
|
||||
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)
|
||||
|
||||
# Or combine color and line style
|
||||
plt.rcParams['axes.prop_cycle'] = cycler(color=colors) + cycler(linestyle=['-', '--', ':', '-.'])
|
||||
```
|
||||
|
||||
### Palette Generation
|
||||
|
||||
```python
|
||||
# Evenly spaced colors from colormap
|
||||
n_colors = 5
|
||||
colors = plt.cm.viridis(np.linspace(0, 1, n_colors))
|
||||
|
||||
# Use in plot
|
||||
for i, (x, y) in enumerate(data):
|
||||
ax.plot(x, y, color=colors[i])
|
||||
```
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Configuration
|
||||
|
||||
```python
|
||||
# Set font family
|
||||
plt.rcParams['font.family'] = 'serif'
|
||||
plt.rcParams['font.serif'] = ['Times New Roman', 'DejaVu Serif']
|
||||
|
||||
# Or sans-serif
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica']
|
||||
|
||||
# Or monospace
|
||||
plt.rcParams['font.family'] = 'monospace'
|
||||
plt.rcParams['font.monospace'] = ['Courier New', 'DejaVu Sans Mono']
|
||||
```
|
||||
|
||||
### Font Properties in Text
|
||||
|
||||
```python
|
||||
from matplotlib import font_manager
|
||||
|
||||
# Specify font properties
|
||||
ax.text(x, y, 'Text',
|
||||
fontsize=14,
|
||||
fontweight='bold', # 'normal', 'bold', 'heavy', 'light'
|
||||
fontstyle='italic', # 'normal', 'italic', 'oblique'
|
||||
fontfamily='serif')
|
||||
|
||||
# Use specific font file
|
||||
prop = font_manager.FontProperties(fname='path/to/font.ttf')
|
||||
ax.text(x, y, 'Text', fontproperties=prop)
|
||||
```
|
||||
|
||||
### Mathematical Text
|
||||
|
||||
```python
|
||||
# LaTeX-style math
|
||||
ax.set_title(r'$\alpha > \beta$')
|
||||
ax.set_xlabel(r'$\mu \pm \sigma$')
|
||||
ax.text(x, y, r'$\int_0^\infty e^{-x} dx = 1$')
|
||||
|
||||
# Subscripts and superscripts
|
||||
ax.set_ylabel(r'$y = x^2 + 2x + 1$')
|
||||
ax.text(x, y, r'$x_1, x_2, \ldots, x_n$')
|
||||
|
||||
# Greek letters
|
||||
ax.text(x, y, r'$\alpha, \beta, \gamma, \delta, \epsilon$')
|
||||
```
|
||||
|
||||
### Using Full LaTeX
|
||||
|
||||
```python
|
||||
# Enable full LaTeX rendering (requires LaTeX installation)
|
||||
plt.rcParams['text.usetex'] = True
|
||||
plt.rcParams['text.latex.preamble'] = r'\usepackage{amsmath}'
|
||||
|
||||
ax.set_title(r'\textbf{Bold Title}')
|
||||
ax.set_xlabel(r'Time $t$ (s)')
|
||||
```
|
||||
|
||||
## Spines and Grids
|
||||
|
||||
### Spine Customization
|
||||
|
||||
```python
|
||||
# Hide specific spines
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
# Move spine position
|
||||
ax.spines['left'].set_position(('outward', 10))
|
||||
ax.spines['bottom'].set_position(('data', 0))
|
||||
|
||||
# Change spine color and width
|
||||
ax.spines['left'].set_color('red')
|
||||
ax.spines['bottom'].set_linewidth(2)
|
||||
```
|
||||
|
||||
### Grid Customization
|
||||
|
||||
```python
|
||||
# Basic grid
|
||||
ax.grid(True)
|
||||
|
||||
# Customized grid
|
||||
ax.grid(True, which='major', linestyle='--', linewidth=0.8, alpha=0.3)
|
||||
ax.grid(True, which='minor', linestyle=':', linewidth=0.5, alpha=0.2)
|
||||
|
||||
# Grid for specific axis
|
||||
ax.grid(True, axis='x') # Only vertical lines
|
||||
ax.grid(True, axis='y') # Only horizontal lines
|
||||
|
||||
# Grid behind or in front of data
|
||||
ax.set_axisbelow(True) # Grid behind data
|
||||
```
|
||||
|
||||
## Legend Customization
|
||||
|
||||
### Legend Positioning
|
||||
|
||||
```python
|
||||
# Location strings
|
||||
ax.legend(loc='best') # Automatic best position
|
||||
ax.legend(loc='upper right')
|
||||
ax.legend(loc='upper left')
|
||||
ax.legend(loc='lower right')
|
||||
ax.legend(loc='lower left')
|
||||
ax.legend(loc='center')
|
||||
ax.legend(loc='upper center')
|
||||
ax.legend(loc='lower center')
|
||||
ax.legend(loc='center left')
|
||||
ax.legend(loc='center right')
|
||||
|
||||
# Precise positioning (bbox_to_anchor)
|
||||
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left') # Outside plot area
|
||||
ax.legend(bbox_to_anchor=(0.5, -0.15), loc='upper center', ncol=3) # Below plot
|
||||
```
|
||||
|
||||
### Legend Styling
|
||||
|
||||
```python
|
||||
ax.legend(
|
||||
fontsize=12,
|
||||
frameon=True, # Show frame
|
||||
framealpha=0.9, # Frame transparency
|
||||
fancybox=True, # Rounded corners
|
||||
shadow=True, # Shadow effect
|
||||
ncol=2, # Number of columns
|
||||
title='Legend Title', # Legend title
|
||||
title_fontsize=14, # Title font size
|
||||
edgecolor='black', # Frame edge color
|
||||
facecolor='white' # Frame background color
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Legend Entries
|
||||
|
||||
```python
|
||||
from matplotlib.lines import Line2D
|
||||
|
||||
# Create custom legend handles
|
||||
custom_lines = [Line2D([0], [0], color='red', lw=2),
|
||||
Line2D([0], [0], color='blue', lw=2, linestyle='--'),
|
||||
Line2D([0], [0], marker='o', color='w', markerfacecolor='green', markersize=10)]
|
||||
|
||||
ax.legend(custom_lines, ['Label 1', 'Label 2', 'Label 3'])
|
||||
```
|
||||
|
||||
## Layout and Spacing
|
||||
|
||||
### Constrained Layout
|
||||
|
||||
```python
|
||||
# Preferred method (automatic adjustment)
|
||||
fig, axes = plt.subplots(2, 2, constrained_layout=True)
|
||||
```
|
||||
|
||||
### Tight Layout
|
||||
|
||||
```python
|
||||
# Alternative method
|
||||
fig, axes = plt.subplots(2, 2)
|
||||
plt.tight_layout(pad=1.5, h_pad=2.0, w_pad=2.0)
|
||||
```
|
||||
|
||||
### Manual Adjustment
|
||||
|
||||
```python
|
||||
# Fine-grained control
|
||||
plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1,
|
||||
hspace=0.3, wspace=0.4)
|
||||
```
|
||||
|
||||
## Professional Publication Style
|
||||
|
||||
Example configuration for publication-quality figures:
|
||||
|
||||
```python
|
||||
# Publication style configuration
|
||||
plt.rcParams.update({
|
||||
# Figure
|
||||
'figure.figsize': (8, 6),
|
||||
'figure.dpi': 100,
|
||||
'savefig.dpi': 300,
|
||||
'savefig.bbox': 'tight',
|
||||
'savefig.pad_inches': 0.1,
|
||||
|
||||
# Font
|
||||
'font.family': 'sans-serif',
|
||||
'font.sans-serif': ['Arial', 'Helvetica'],
|
||||
'font.size': 11,
|
||||
|
||||
# Axes
|
||||
'axes.labelsize': 12,
|
||||
'axes.titlesize': 14,
|
||||
'axes.linewidth': 1.5,
|
||||
'axes.grid': False,
|
||||
'axes.spines.top': False,
|
||||
'axes.spines.right': False,
|
||||
|
||||
# Lines
|
||||
'lines.linewidth': 2,
|
||||
'lines.markersize': 8,
|
||||
|
||||
# Ticks
|
||||
'xtick.labelsize': 10,
|
||||
'ytick.labelsize': 10,
|
||||
'xtick.major.size': 6,
|
||||
'ytick.major.size': 6,
|
||||
'xtick.major.width': 1.5,
|
||||
'ytick.major.width': 1.5,
|
||||
'xtick.direction': 'in',
|
||||
'ytick.direction': 'in',
|
||||
|
||||
# Legend
|
||||
'legend.fontsize': 10,
|
||||
'legend.frameon': True,
|
||||
'legend.framealpha': 1.0,
|
||||
'legend.edgecolor': 'black'
|
||||
})
|
||||
```
|
||||
|
||||
## Dark Theme
|
||||
|
||||
```python
|
||||
# Dark background style
|
||||
plt.style.use('dark_background')
|
||||
|
||||
# Or manual configuration
|
||||
plt.rcParams.update({
|
||||
'figure.facecolor': '#1e1e1e',
|
||||
'axes.facecolor': '#1e1e1e',
|
||||
'axes.edgecolor': 'white',
|
||||
'axes.labelcolor': 'white',
|
||||
'text.color': 'white',
|
||||
'xtick.color': 'white',
|
||||
'ytick.color': 'white',
|
||||
'grid.color': 'gray',
|
||||
'legend.facecolor': '#1e1e1e',
|
||||
'legend.edgecolor': 'white'
|
||||
})
|
||||
```
|
||||
|
||||
## Color Accessibility
|
||||
|
||||
### Colorblind-Friendly Palettes
|
||||
|
||||
```python
|
||||
# Use colorblind-friendly colormaps
|
||||
colorblind_friendly = ['viridis', 'plasma', 'cividis']
|
||||
|
||||
# Colorblind-friendly discrete colors
|
||||
cb_colors = ['#0173B2', '#DE8F05', '#029E73', '#CC78BC',
|
||||
'#CA9161', '#949494', '#ECE133', '#56B4E9']
|
||||
|
||||
# Test with simulation tools or use these validated palettes
|
||||
```
|
||||
|
||||
### High Contrast
|
||||
|
||||
```python
|
||||
# Ensure sufficient contrast
|
||||
plt.rcParams['axes.edgecolor'] = 'black'
|
||||
plt.rcParams['axes.linewidth'] = 2
|
||||
plt.rcParams['xtick.major.width'] = 2
|
||||
plt.rcParams['ytick.major.width'] = 2
|
||||
```
|
||||
401
skills/matplotlib/scripts/plot_template.py
Normal file
401
skills/matplotlib/scripts/plot_template.py
Normal file
@@ -0,0 +1,401 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Matplotlib Plot Template
|
||||
|
||||
Comprehensive template demonstrating various plot types and best practices.
|
||||
Use this as a starting point for creating publication-quality visualizations.
|
||||
|
||||
Usage:
|
||||
python plot_template.py [--plot-type TYPE] [--style STYLE] [--output FILE]
|
||||
|
||||
Plot types:
|
||||
line, scatter, bar, histogram, heatmap, contour, box, violin, 3d, all
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.gridspec import GridSpec
|
||||
import argparse
|
||||
|
||||
|
||||
def set_publication_style():
|
||||
"""Configure matplotlib for publication-quality figures."""
|
||||
plt.rcParams.update({
|
||||
'figure.figsize': (10, 6),
|
||||
'figure.dpi': 100,
|
||||
'savefig.dpi': 300,
|
||||
'savefig.bbox': 'tight',
|
||||
'font.size': 11,
|
||||
'axes.labelsize': 12,
|
||||
'axes.titlesize': 14,
|
||||
'xtick.labelsize': 10,
|
||||
'ytick.labelsize': 10,
|
||||
'legend.fontsize': 10,
|
||||
'lines.linewidth': 2,
|
||||
'axes.linewidth': 1.5,
|
||||
})
|
||||
|
||||
|
||||
def generate_sample_data():
|
||||
"""Generate sample data for demonstrations."""
|
||||
np.random.seed(42)
|
||||
x = np.linspace(0, 10, 100)
|
||||
y1 = np.sin(x)
|
||||
y2 = np.cos(x)
|
||||
scatter_x = np.random.randn(200)
|
||||
scatter_y = np.random.randn(200)
|
||||
categories = ['A', 'B', 'C', 'D', 'E']
|
||||
bar_values = np.random.randint(10, 100, len(categories))
|
||||
hist_data = np.random.normal(0, 1, 1000)
|
||||
matrix = np.random.rand(10, 10)
|
||||
|
||||
X, Y = np.meshgrid(np.linspace(-3, 3, 100), np.linspace(-3, 3, 100))
|
||||
Z = np.sin(np.sqrt(X**2 + Y**2))
|
||||
|
||||
return {
|
||||
'x': x, 'y1': y1, 'y2': y2,
|
||||
'scatter_x': scatter_x, 'scatter_y': scatter_y,
|
||||
'categories': categories, 'bar_values': bar_values,
|
||||
'hist_data': hist_data, 'matrix': matrix,
|
||||
'X': X, 'Y': Y, 'Z': Z
|
||||
}
|
||||
|
||||
|
||||
def create_line_plot(data, ax=None):
|
||||
"""Create line plot with best practices."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
ax.plot(data['x'], data['y1'], label='sin(x)', linewidth=2, marker='o',
|
||||
markevery=10, markersize=6)
|
||||
ax.plot(data['x'], data['y2'], label='cos(x)', linewidth=2, linestyle='--')
|
||||
|
||||
ax.set_xlabel('x')
|
||||
ax.set_ylabel('y')
|
||||
ax.set_title('Line Plot Example')
|
||||
ax.legend(loc='best', framealpha=0.9)
|
||||
ax.grid(True, alpha=0.3, linestyle='--')
|
||||
|
||||
# Remove top and right spines for cleaner look
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_scatter_plot(data, ax=None):
|
||||
"""Create scatter plot with color and size variations."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
# Color based on distance from origin
|
||||
colors = np.sqrt(data['scatter_x']**2 + data['scatter_y']**2)
|
||||
sizes = 50 * (1 + np.abs(data['scatter_x']))
|
||||
|
||||
scatter = ax.scatter(data['scatter_x'], data['scatter_y'],
|
||||
c=colors, s=sizes, alpha=0.6,
|
||||
cmap='viridis', edgecolors='black', linewidth=0.5)
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_title('Scatter Plot Example')
|
||||
ax.grid(True, alpha=0.3, linestyle='--')
|
||||
|
||||
# Add colorbar
|
||||
cbar = plt.colorbar(scatter, ax=ax)
|
||||
cbar.set_label('Distance from origin')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_bar_chart(data, ax=None):
|
||||
"""Create bar chart with error bars and styling."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
x_pos = np.arange(len(data['categories']))
|
||||
errors = np.random.randint(5, 15, len(data['categories']))
|
||||
|
||||
bars = ax.bar(x_pos, data['bar_values'], yerr=errors,
|
||||
color='steelblue', edgecolor='black', linewidth=1.5,
|
||||
capsize=5, alpha=0.8)
|
||||
|
||||
# Color bars by value
|
||||
colors = plt.cm.viridis(data['bar_values'] / data['bar_values'].max())
|
||||
for bar, color in zip(bars, colors):
|
||||
bar.set_facecolor(color)
|
||||
|
||||
ax.set_xlabel('Category')
|
||||
ax.set_ylabel('Values')
|
||||
ax.set_title('Bar Chart Example')
|
||||
ax.set_xticks(x_pos)
|
||||
ax.set_xticklabels(data['categories'])
|
||||
ax.grid(True, axis='y', alpha=0.3, linestyle='--')
|
||||
|
||||
# Remove top and right spines
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_histogram(data, ax=None):
|
||||
"""Create histogram with density overlay."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
n, bins, patches = ax.hist(data['hist_data'], bins=30, density=True,
|
||||
alpha=0.7, edgecolor='black', color='steelblue')
|
||||
|
||||
# Overlay theoretical normal distribution
|
||||
from scipy.stats import norm
|
||||
mu, std = norm.fit(data['hist_data'])
|
||||
x_theory = np.linspace(data['hist_data'].min(), data['hist_data'].max(), 100)
|
||||
ax.plot(x_theory, norm.pdf(x_theory, mu, std), 'r-', linewidth=2,
|
||||
label=f'Normal fit (μ={mu:.2f}, σ={std:.2f})')
|
||||
|
||||
ax.set_xlabel('Value')
|
||||
ax.set_ylabel('Density')
|
||||
ax.set_title('Histogram with Normal Fit')
|
||||
ax.legend()
|
||||
ax.grid(True, axis='y', alpha=0.3, linestyle='--')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_heatmap(data, ax=None):
|
||||
"""Create heatmap with colorbar and annotations."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 8), constrained_layout=True)
|
||||
|
||||
im = ax.imshow(data['matrix'], cmap='coolwarm', aspect='auto',
|
||||
vmin=0, vmax=1)
|
||||
|
||||
# Add colorbar
|
||||
cbar = plt.colorbar(im, ax=ax)
|
||||
cbar.set_label('Value')
|
||||
|
||||
# Optional: Add text annotations
|
||||
# for i in range(data['matrix'].shape[0]):
|
||||
# for j in range(data['matrix'].shape[1]):
|
||||
# text = ax.text(j, i, f'{data["matrix"][i, j]:.2f}',
|
||||
# ha='center', va='center', color='black', fontsize=8)
|
||||
|
||||
ax.set_xlabel('X Index')
|
||||
ax.set_ylabel('Y Index')
|
||||
ax.set_title('Heatmap Example')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_contour_plot(data, ax=None):
|
||||
"""Create contour plot with filled contours and labels."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 8), constrained_layout=True)
|
||||
|
||||
# Filled contours
|
||||
contourf = ax.contourf(data['X'], data['Y'], data['Z'],
|
||||
levels=20, cmap='viridis', alpha=0.8)
|
||||
|
||||
# Contour lines
|
||||
contour = ax.contour(data['X'], data['Y'], data['Z'],
|
||||
levels=10, colors='black', linewidths=0.5, alpha=0.4)
|
||||
|
||||
# Add labels to contour lines
|
||||
ax.clabel(contour, inline=True, fontsize=8)
|
||||
|
||||
# Add colorbar
|
||||
cbar = plt.colorbar(contourf, ax=ax)
|
||||
cbar.set_label('Z value')
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_title('Contour Plot Example')
|
||||
ax.set_aspect('equal')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_box_plot(data, ax=None):
|
||||
"""Create box plot comparing distributions."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
# Generate multiple distributions
|
||||
box_data = [np.random.normal(0, std, 100) for std in range(1, 5)]
|
||||
|
||||
bp = ax.boxplot(box_data, labels=['Group 1', 'Group 2', 'Group 3', 'Group 4'],
|
||||
patch_artist=True, showmeans=True,
|
||||
boxprops=dict(facecolor='lightblue', edgecolor='black'),
|
||||
medianprops=dict(color='red', linewidth=2),
|
||||
meanprops=dict(marker='D', markerfacecolor='green', markersize=8))
|
||||
|
||||
ax.set_xlabel('Groups')
|
||||
ax.set_ylabel('Values')
|
||||
ax.set_title('Box Plot Example')
|
||||
ax.grid(True, axis='y', alpha=0.3, linestyle='--')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_violin_plot(data, ax=None):
|
||||
"""Create violin plot showing distribution shapes."""
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
|
||||
# Generate multiple distributions
|
||||
violin_data = [np.random.normal(0, std, 100) for std in range(1, 5)]
|
||||
|
||||
parts = ax.violinplot(violin_data, positions=range(1, 5),
|
||||
showmeans=True, showmedians=True)
|
||||
|
||||
# Customize colors
|
||||
for pc in parts['bodies']:
|
||||
pc.set_facecolor('lightblue')
|
||||
pc.set_alpha(0.7)
|
||||
pc.set_edgecolor('black')
|
||||
|
||||
ax.set_xlabel('Groups')
|
||||
ax.set_ylabel('Values')
|
||||
ax.set_title('Violin Plot Example')
|
||||
ax.set_xticks(range(1, 5))
|
||||
ax.set_xticklabels(['Group 1', 'Group 2', 'Group 3', 'Group 4'])
|
||||
ax.grid(True, axis='y', alpha=0.3, linestyle='--')
|
||||
|
||||
if ax is None:
|
||||
return fig
|
||||
return ax
|
||||
|
||||
|
||||
def create_3d_plot():
|
||||
"""Create 3D surface plot."""
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure(figsize=(12, 9))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
|
||||
# Generate data
|
||||
X = np.linspace(-5, 5, 50)
|
||||
Y = np.linspace(-5, 5, 50)
|
||||
X, Y = np.meshgrid(X, Y)
|
||||
Z = np.sin(np.sqrt(X**2 + Y**2))
|
||||
|
||||
# Create surface plot
|
||||
surf = ax.plot_surface(X, Y, Z, cmap='viridis',
|
||||
edgecolor='none', alpha=0.9)
|
||||
|
||||
# Add colorbar
|
||||
fig.colorbar(surf, ax=ax, shrink=0.5)
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.set_zlabel('Z')
|
||||
ax.set_title('3D Surface Plot Example')
|
||||
|
||||
# Set viewing angle
|
||||
ax.view_init(elev=30, azim=45)
|
||||
|
||||
plt.tight_layout()
|
||||
return fig
|
||||
|
||||
|
||||
def create_comprehensive_figure():
|
||||
"""Create a comprehensive figure with multiple subplots."""
|
||||
data = generate_sample_data()
|
||||
|
||||
fig = plt.figure(figsize=(16, 12), constrained_layout=True)
|
||||
gs = GridSpec(3, 3, figure=fig)
|
||||
|
||||
# Create subplots
|
||||
ax1 = fig.add_subplot(gs[0, :2]) # Line plot - top left, spans 2 columns
|
||||
create_line_plot(data, ax1)
|
||||
|
||||
ax2 = fig.add_subplot(gs[0, 2]) # Bar chart - top right
|
||||
create_bar_chart(data, ax2)
|
||||
|
||||
ax3 = fig.add_subplot(gs[1, 0]) # Scatter plot - middle left
|
||||
create_scatter_plot(data, ax3)
|
||||
|
||||
ax4 = fig.add_subplot(gs[1, 1]) # Histogram - middle center
|
||||
create_histogram(data, ax4)
|
||||
|
||||
ax5 = fig.add_subplot(gs[1, 2]) # Box plot - middle right
|
||||
create_box_plot(data, ax5)
|
||||
|
||||
ax6 = fig.add_subplot(gs[2, :2]) # Contour plot - bottom left, spans 2 columns
|
||||
create_contour_plot(data, ax6)
|
||||
|
||||
ax7 = fig.add_subplot(gs[2, 2]) # Heatmap - bottom right
|
||||
create_heatmap(data, ax7)
|
||||
|
||||
fig.suptitle('Comprehensive Matplotlib Template', fontsize=18, fontweight='bold')
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the template."""
|
||||
parser = argparse.ArgumentParser(description='Matplotlib plot template')
|
||||
parser.add_argument('--plot-type', type=str, default='all',
|
||||
choices=['line', 'scatter', 'bar', 'histogram', 'heatmap',
|
||||
'contour', 'box', 'violin', '3d', 'all'],
|
||||
help='Type of plot to create')
|
||||
parser.add_argument('--style', type=str, default='default',
|
||||
help='Matplotlib style to use')
|
||||
parser.add_argument('--output', type=str, default='plot.png',
|
||||
help='Output filename')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Set style
|
||||
if args.style != 'default':
|
||||
plt.style.use(args.style)
|
||||
else:
|
||||
set_publication_style()
|
||||
|
||||
# Generate data
|
||||
data = generate_sample_data()
|
||||
|
||||
# Create plot based on type
|
||||
plot_functions = {
|
||||
'line': create_line_plot,
|
||||
'scatter': create_scatter_plot,
|
||||
'bar': create_bar_chart,
|
||||
'histogram': create_histogram,
|
||||
'heatmap': create_heatmap,
|
||||
'contour': create_contour_plot,
|
||||
'box': create_box_plot,
|
||||
'violin': create_violin_plot,
|
||||
}
|
||||
|
||||
if args.plot_type == '3d':
|
||||
fig = create_3d_plot()
|
||||
elif args.plot_type == 'all':
|
||||
fig = create_comprehensive_figure()
|
||||
else:
|
||||
fig = plot_functions[args.plot_type](data)
|
||||
|
||||
# Save figure
|
||||
plt.savefig(args.output, dpi=300, bbox_inches='tight')
|
||||
print(f"Plot saved to {args.output}")
|
||||
|
||||
# Display
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
409
skills/matplotlib/scripts/style_configurator.py
Normal file
409
skills/matplotlib/scripts/style_configurator.py
Normal file
@@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Matplotlib Style Configurator
|
||||
|
||||
Interactive utility to configure matplotlib style preferences and generate
|
||||
custom style sheets. Creates a preview of the style and optionally saves
|
||||
it as a .mplstyle file.
|
||||
|
||||
Usage:
|
||||
python style_configurator.py [--preset PRESET] [--output FILE] [--preview]
|
||||
|
||||
Presets:
|
||||
publication, presentation, web, dark, minimal
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.gridspec import GridSpec
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
# Predefined style presets
|
||||
STYLE_PRESETS = {
|
||||
'publication': {
|
||||
'figure.figsize': (8, 6),
|
||||
'figure.dpi': 100,
|
||||
'savefig.dpi': 300,
|
||||
'savefig.bbox': 'tight',
|
||||
'font.family': 'sans-serif',
|
||||
'font.sans-serif': ['Arial', 'Helvetica'],
|
||||
'font.size': 11,
|
||||
'axes.labelsize': 12,
|
||||
'axes.titlesize': 14,
|
||||
'axes.linewidth': 1.5,
|
||||
'axes.grid': False,
|
||||
'axes.spines.top': False,
|
||||
'axes.spines.right': False,
|
||||
'lines.linewidth': 2,
|
||||
'lines.markersize': 8,
|
||||
'xtick.labelsize': 10,
|
||||
'ytick.labelsize': 10,
|
||||
'xtick.direction': 'in',
|
||||
'ytick.direction': 'in',
|
||||
'xtick.major.size': 6,
|
||||
'ytick.major.size': 6,
|
||||
'xtick.major.width': 1.5,
|
||||
'ytick.major.width': 1.5,
|
||||
'legend.fontsize': 10,
|
||||
'legend.frameon': True,
|
||||
'legend.framealpha': 1.0,
|
||||
'legend.edgecolor': 'black',
|
||||
},
|
||||
'presentation': {
|
||||
'figure.figsize': (12, 8),
|
||||
'figure.dpi': 100,
|
||||
'savefig.dpi': 150,
|
||||
'font.size': 16,
|
||||
'axes.labelsize': 20,
|
||||
'axes.titlesize': 24,
|
||||
'axes.linewidth': 2,
|
||||
'lines.linewidth': 3,
|
||||
'lines.markersize': 12,
|
||||
'xtick.labelsize': 16,
|
||||
'ytick.labelsize': 16,
|
||||
'legend.fontsize': 16,
|
||||
'axes.grid': True,
|
||||
'grid.alpha': 0.3,
|
||||
},
|
||||
'web': {
|
||||
'figure.figsize': (10, 6),
|
||||
'figure.dpi': 96,
|
||||
'savefig.dpi': 150,
|
||||
'font.size': 11,
|
||||
'axes.labelsize': 12,
|
||||
'axes.titlesize': 14,
|
||||
'lines.linewidth': 2,
|
||||
'axes.grid': True,
|
||||
'grid.alpha': 0.2,
|
||||
'grid.linestyle': '--',
|
||||
},
|
||||
'dark': {
|
||||
'figure.facecolor': '#1e1e1e',
|
||||
'figure.edgecolor': '#1e1e1e',
|
||||
'axes.facecolor': '#1e1e1e',
|
||||
'axes.edgecolor': 'white',
|
||||
'axes.labelcolor': 'white',
|
||||
'text.color': 'white',
|
||||
'xtick.color': 'white',
|
||||
'ytick.color': 'white',
|
||||
'grid.color': 'gray',
|
||||
'grid.alpha': 0.3,
|
||||
'axes.grid': True,
|
||||
'legend.facecolor': '#1e1e1e',
|
||||
'legend.edgecolor': 'white',
|
||||
'savefig.facecolor': '#1e1e1e',
|
||||
},
|
||||
'minimal': {
|
||||
'figure.figsize': (10, 6),
|
||||
'axes.spines.top': False,
|
||||
'axes.spines.right': False,
|
||||
'axes.spines.left': False,
|
||||
'axes.spines.bottom': False,
|
||||
'axes.grid': False,
|
||||
'xtick.bottom': True,
|
||||
'ytick.left': True,
|
||||
'axes.axisbelow': True,
|
||||
'lines.linewidth': 2.5,
|
||||
'font.size': 12,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def generate_preview_data():
|
||||
"""Generate sample data for style preview."""
|
||||
np.random.seed(42)
|
||||
x = np.linspace(0, 10, 100)
|
||||
y1 = np.sin(x) + 0.1 * np.random.randn(100)
|
||||
y2 = np.cos(x) + 0.1 * np.random.randn(100)
|
||||
scatter_x = np.random.randn(100)
|
||||
scatter_y = 2 * scatter_x + np.random.randn(100)
|
||||
categories = ['A', 'B', 'C', 'D', 'E']
|
||||
bar_values = [25, 40, 30, 55, 45]
|
||||
|
||||
return {
|
||||
'x': x, 'y1': y1, 'y2': y2,
|
||||
'scatter_x': scatter_x, 'scatter_y': scatter_y,
|
||||
'categories': categories, 'bar_values': bar_values
|
||||
}
|
||||
|
||||
|
||||
def create_style_preview(style_dict=None):
|
||||
"""Create a preview figure demonstrating the style."""
|
||||
if style_dict:
|
||||
plt.rcParams.update(style_dict)
|
||||
|
||||
data = generate_preview_data()
|
||||
|
||||
fig = plt.figure(figsize=(14, 10))
|
||||
gs = GridSpec(2, 2, figure=fig, hspace=0.3, wspace=0.3)
|
||||
|
||||
# Line plot
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax1.plot(data['x'], data['y1'], label='sin(x)', marker='o', markevery=10)
|
||||
ax1.plot(data['x'], data['y2'], label='cos(x)', linestyle='--')
|
||||
ax1.set_xlabel('X axis')
|
||||
ax1.set_ylabel('Y axis')
|
||||
ax1.set_title('Line Plot')
|
||||
ax1.legend()
|
||||
ax1.grid(True, alpha=0.3)
|
||||
|
||||
# Scatter plot
|
||||
ax2 = fig.add_subplot(gs[0, 1])
|
||||
colors = np.sqrt(data['scatter_x']**2 + data['scatter_y']**2)
|
||||
scatter = ax2.scatter(data['scatter_x'], data['scatter_y'],
|
||||
c=colors, cmap='viridis', alpha=0.6, s=50)
|
||||
ax2.set_xlabel('X axis')
|
||||
ax2.set_ylabel('Y axis')
|
||||
ax2.set_title('Scatter Plot')
|
||||
cbar = plt.colorbar(scatter, ax=ax2)
|
||||
cbar.set_label('Distance')
|
||||
ax2.grid(True, alpha=0.3)
|
||||
|
||||
# Bar chart
|
||||
ax3 = fig.add_subplot(gs[1, 0])
|
||||
bars = ax3.bar(data['categories'], data['bar_values'],
|
||||
edgecolor='black', linewidth=1)
|
||||
# Color bars with gradient
|
||||
colors = plt.cm.viridis(np.linspace(0.2, 0.8, len(bars)))
|
||||
for bar, color in zip(bars, colors):
|
||||
bar.set_facecolor(color)
|
||||
ax3.set_xlabel('Categories')
|
||||
ax3.set_ylabel('Values')
|
||||
ax3.set_title('Bar Chart')
|
||||
ax3.grid(True, axis='y', alpha=0.3)
|
||||
|
||||
# Multiple line plot with fills
|
||||
ax4 = fig.add_subplot(gs[1, 1])
|
||||
ax4.plot(data['x'], data['y1'], label='Signal 1', linewidth=2)
|
||||
ax4.fill_between(data['x'], data['y1'] - 0.2, data['y1'] + 0.2,
|
||||
alpha=0.3, label='±1 std')
|
||||
ax4.plot(data['x'], data['y2'], label='Signal 2', linewidth=2)
|
||||
ax4.fill_between(data['x'], data['y2'] - 0.2, data['y2'] + 0.2,
|
||||
alpha=0.3)
|
||||
ax4.set_xlabel('X axis')
|
||||
ax4.set_ylabel('Y axis')
|
||||
ax4.set_title('Time Series with Uncertainty')
|
||||
ax4.legend()
|
||||
ax4.grid(True, alpha=0.3)
|
||||
|
||||
fig.suptitle('Style Preview', fontsize=16, fontweight='bold')
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def save_style_file(style_dict, filename):
|
||||
"""Save style dictionary as .mplstyle file."""
|
||||
with open(filename, 'w') as f:
|
||||
f.write("# Custom matplotlib style\n")
|
||||
f.write("# Generated by style_configurator.py\n\n")
|
||||
|
||||
# Group settings by category
|
||||
categories = {
|
||||
'Figure': ['figure.'],
|
||||
'Font': ['font.'],
|
||||
'Axes': ['axes.'],
|
||||
'Lines': ['lines.'],
|
||||
'Markers': ['markers.'],
|
||||
'Ticks': ['tick.', 'xtick.', 'ytick.'],
|
||||
'Grid': ['grid.'],
|
||||
'Legend': ['legend.'],
|
||||
'Savefig': ['savefig.'],
|
||||
'Text': ['text.'],
|
||||
}
|
||||
|
||||
for category, prefixes in categories.items():
|
||||
category_items = {k: v for k, v in style_dict.items()
|
||||
if any(k.startswith(p) for p in prefixes)}
|
||||
if category_items:
|
||||
f.write(f"# {category}\n")
|
||||
for key, value in sorted(category_items.items()):
|
||||
# Format value appropriately
|
||||
if isinstance(value, (list, tuple)):
|
||||
value_str = ', '.join(str(v) for v in value)
|
||||
elif isinstance(value, bool):
|
||||
value_str = str(value)
|
||||
else:
|
||||
value_str = str(value)
|
||||
f.write(f"{key}: {value_str}\n")
|
||||
f.write("\n")
|
||||
|
||||
print(f"Style saved to {filename}")
|
||||
|
||||
|
||||
def print_style_info(style_dict):
|
||||
"""Print information about the style."""
|
||||
print("\n" + "="*60)
|
||||
print("STYLE CONFIGURATION")
|
||||
print("="*60)
|
||||
|
||||
categories = {
|
||||
'Figure Settings': ['figure.'],
|
||||
'Font Settings': ['font.'],
|
||||
'Axes Settings': ['axes.'],
|
||||
'Line Settings': ['lines.'],
|
||||
'Grid Settings': ['grid.'],
|
||||
'Legend Settings': ['legend.'],
|
||||
}
|
||||
|
||||
for category, prefixes in categories.items():
|
||||
category_items = {k: v for k, v in style_dict.items()
|
||||
if any(k.startswith(p) for p in prefixes)}
|
||||
if category_items:
|
||||
print(f"\n{category}:")
|
||||
for key, value in sorted(category_items.items()):
|
||||
print(f" {key}: {value}")
|
||||
|
||||
print("\n" + "="*60 + "\n")
|
||||
|
||||
|
||||
def list_available_presets():
|
||||
"""Print available style presets."""
|
||||
print("\nAvailable style presets:")
|
||||
print("-" * 40)
|
||||
descriptions = {
|
||||
'publication': 'Optimized for academic publications',
|
||||
'presentation': 'Large fonts for presentations',
|
||||
'web': 'Optimized for web display',
|
||||
'dark': 'Dark background theme',
|
||||
'minimal': 'Minimal, clean style',
|
||||
}
|
||||
for preset, desc in descriptions.items():
|
||||
print(f" {preset:15s} - {desc}")
|
||||
print("-" * 40 + "\n")
|
||||
|
||||
|
||||
def interactive_mode():
|
||||
"""Run interactive mode to customize style settings."""
|
||||
print("\n" + "="*60)
|
||||
print("MATPLOTLIB STYLE CONFIGURATOR - Interactive Mode")
|
||||
print("="*60)
|
||||
|
||||
list_available_presets()
|
||||
|
||||
preset = input("Choose a preset to start from (or 'custom' for default): ").strip().lower()
|
||||
|
||||
if preset in STYLE_PRESETS:
|
||||
style_dict = STYLE_PRESETS[preset].copy()
|
||||
print(f"\nStarting from '{preset}' preset")
|
||||
else:
|
||||
style_dict = {}
|
||||
print("\nStarting from default matplotlib style")
|
||||
|
||||
print("\nCommon settings you might want to customize:")
|
||||
print(" 1. Figure size")
|
||||
print(" 2. Font sizes")
|
||||
print(" 3. Line widths")
|
||||
print(" 4. Grid settings")
|
||||
print(" 5. Color scheme")
|
||||
print(" 6. Done, show preview")
|
||||
|
||||
while True:
|
||||
choice = input("\nSelect option (1-6): ").strip()
|
||||
|
||||
if choice == '1':
|
||||
width = input(" Figure width (inches, default 10): ").strip() or '10'
|
||||
height = input(" Figure height (inches, default 6): ").strip() or '6'
|
||||
style_dict['figure.figsize'] = (float(width), float(height))
|
||||
|
||||
elif choice == '2':
|
||||
base = input(" Base font size (default 12): ").strip() or '12'
|
||||
style_dict['font.size'] = float(base)
|
||||
style_dict['axes.labelsize'] = float(base) + 2
|
||||
style_dict['axes.titlesize'] = float(base) + 4
|
||||
|
||||
elif choice == '3':
|
||||
lw = input(" Line width (default 2): ").strip() or '2'
|
||||
style_dict['lines.linewidth'] = float(lw)
|
||||
|
||||
elif choice == '4':
|
||||
grid = input(" Enable grid? (y/n): ").strip().lower()
|
||||
style_dict['axes.grid'] = grid == 'y'
|
||||
if style_dict['axes.grid']:
|
||||
alpha = input(" Grid transparency (0-1, default 0.3): ").strip() or '0.3'
|
||||
style_dict['grid.alpha'] = float(alpha)
|
||||
|
||||
elif choice == '5':
|
||||
print(" Theme options: 1=Light, 2=Dark")
|
||||
theme = input(" Select theme (1-2): ").strip()
|
||||
if theme == '2':
|
||||
style_dict.update(STYLE_PRESETS['dark'])
|
||||
|
||||
elif choice == '6':
|
||||
break
|
||||
|
||||
return style_dict
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Matplotlib style configurator',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Show available presets
|
||||
python style_configurator.py --list
|
||||
|
||||
# Preview a preset
|
||||
python style_configurator.py --preset publication --preview
|
||||
|
||||
# Save a preset as .mplstyle file
|
||||
python style_configurator.py --preset publication --output my_style.mplstyle
|
||||
|
||||
# Interactive mode
|
||||
python style_configurator.py --interactive
|
||||
"""
|
||||
)
|
||||
parser.add_argument('--preset', type=str, choices=list(STYLE_PRESETS.keys()),
|
||||
help='Use a predefined style preset')
|
||||
parser.add_argument('--output', type=str,
|
||||
help='Save style to .mplstyle file')
|
||||
parser.add_argument('--preview', action='store_true',
|
||||
help='Show style preview')
|
||||
parser.add_argument('--list', action='store_true',
|
||||
help='List available presets')
|
||||
parser.add_argument('--interactive', action='store_true',
|
||||
help='Run in interactive mode')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list:
|
||||
list_available_presets()
|
||||
# Also show currently available matplotlib styles
|
||||
print("\nBuilt-in matplotlib styles:")
|
||||
print("-" * 40)
|
||||
for style in sorted(plt.style.available):
|
||||
print(f" {style}")
|
||||
return
|
||||
|
||||
if args.interactive:
|
||||
style_dict = interactive_mode()
|
||||
elif args.preset:
|
||||
style_dict = STYLE_PRESETS[args.preset].copy()
|
||||
print(f"Using '{args.preset}' preset")
|
||||
else:
|
||||
print("No preset or interactive mode specified. Showing default preview.")
|
||||
style_dict = {}
|
||||
|
||||
if style_dict:
|
||||
print_style_info(style_dict)
|
||||
|
||||
if args.output:
|
||||
save_style_file(style_dict, args.output)
|
||||
|
||||
if args.preview or args.interactive:
|
||||
print("Creating style preview...")
|
||||
fig = create_style_preview(style_dict if style_dict else None)
|
||||
|
||||
if args.output:
|
||||
preview_filename = args.output.replace('.mplstyle', '_preview.png')
|
||||
plt.savefig(preview_filename, dpi=150, bbox_inches='tight')
|
||||
print(f"Preview saved to {preview_filename}")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user