init commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
from plt_util import plt_config, plt_export
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.patches import FancyArrowPatch
|
||||
import matplotlib.ticker as ticker
|
||||
import numpy as np
|
||||
|
||||
plt_config(plt)
|
||||
|
||||
## Initiate test plot data
|
||||
x = np.linspace(0, 10, 14)
|
||||
|
||||
# -- bounds
|
||||
upper_bound = np.ones_like(x) * 4.5
|
||||
lower_bound = upper_bound * 0.4
|
||||
|
||||
# -- lines
|
||||
method_1_1 = upper_bound - (upper_bound - lower_bound) * np.exp(-0.7 * x)
|
||||
method_1_2 = upper_bound - (upper_bound - lower_bound) * np.exp(-0.5 * x)
|
||||
method_1_3 = upper_bound - (upper_bound - lower_bound) * np.exp(-0.3 * x)
|
||||
method_2 = upper_bound - (upper_bound - lower_bound) * np.exp(-0.5 * x) * np.cos(x)
|
||||
|
||||
|
||||
## Plot
|
||||
x_min, x_max = 0, 10
|
||||
y_min, y_max = 2, 6
|
||||
plt.plot(x, upper_bound, 'k-', label='Optimal')
|
||||
plt.plot(x, method_1_1, '^b-', label='Method 1')
|
||||
plt.plot(x, method_1_2, '^b--')
|
||||
plt.plot(x, method_1_3, '^b-.')
|
||||
plt.plot(x, method_2, '*r-', label='Method 2')
|
||||
plt.xlim(x_min, x_max)
|
||||
plt.ylim(y_min, y_max)
|
||||
plt.grid(True, linestyle='--', color='gray', linewidth=0.5)
|
||||
|
||||
# -- Add arrow
|
||||
arrow = FancyArrowPatch((2.5, 4.2), # start point
|
||||
(3.8, 3), # end point
|
||||
arrowstyle='-|>', # arrow style
|
||||
mutation_scale=10, # arrow head size
|
||||
color='black', # arrow color
|
||||
linewidth=0.5) # line width
|
||||
plt.gca().add_patch(arrow)
|
||||
plt.text(4, 3, '$\gamma = 1, 2, 3$')
|
||||
|
||||
# -- Add legends and labels
|
||||
plt.legend()
|
||||
plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(1))
|
||||
plt.gca().yaxis.set_major_locator(ticker.MultipleLocator(1))
|
||||
plt.ylabel(r"$\mathbb{E}\left[S\right]$")
|
||||
plt.xlabel(r'Rounds of Iteration')
|
||||
|
||||
# -- export and show
|
||||
plt_export(filename='example_python.pdf',
|
||||
folder='../../images',
|
||||
format='pdf')
|
||||
plt.show()
|
||||
@@ -0,0 +1,249 @@
|
||||
import os, re, platform
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
|
||||
def plt_config(plt, custom_rcparams=None):
|
||||
"""
|
||||
Configure matplotlib runtime parameters for consistent styling in IEEETran-like figures.
|
||||
|
||||
Parameters:
|
||||
- plt: The matplotlib module to configure.
|
||||
- custom_rcparams: (dict, optional) Custom runtime parameters to override or extend the defaults.
|
||||
Default is None.
|
||||
"""
|
||||
# In IEEETran, \textwidth is 7.1413 inches
|
||||
# - Use `\printinunitsof{in}\prntlen{\textwidth}`
|
||||
# - To output the \textwidth value in the generated document,
|
||||
# you need to use the `printlen` and `layouts` packages.
|
||||
textwidth_inches = 7.1413
|
||||
|
||||
# Configure figure dimensions: width as 0.5 * \textwidth and aspect ratio (16:9 or 4:3)
|
||||
aspect = 16 / 9 # Aspect ratio (can also use 4/3)
|
||||
axes_left, axes_right, axes_bottom, axes_top = 0.15, 0.9, 0.15, 0.9
|
||||
axes_width = axes_right - axes_left
|
||||
axes_height = axes_top - axes_bottom
|
||||
fig_width = 0.5 * textwidth_inches
|
||||
fig_height = (fig_width * axes_width / aspect) / axes_height
|
||||
|
||||
# Default runtime parameters
|
||||
# -- call plt.rcParams.keys() to list all available rcParameter
|
||||
default_rcparams = {
|
||||
"text.usetex": True, # Use LaTeX for rendering text
|
||||
"text.latex.preamble": r'\usepackage{amsmath,amsfonts}', # Load LaTeX packages
|
||||
"font.family": 'Times New Roman', # Set font family
|
||||
"font.weight": 'normal', # Set font weight
|
||||
"font.size": 10, # Set base font size
|
||||
"figure.figsize": (fig_width, fig_height), # Set figure size
|
||||
"lines.linewidth": 0.5, # Set line width
|
||||
"lines.markeredgewidth": 0.5, # Set marker edge width
|
||||
"lines.markersize": 2, # Set marker size
|
||||
"figure.subplot.bottom": axes_bottom, # Subplot bottom margin
|
||||
"figure.subplot.left": axes_left, # Subplot left margin
|
||||
"figure.subplot.right": axes_right, # Subplot right margin
|
||||
"figure.subplot.top": axes_top, # Subplot top margin
|
||||
"legend.markerscale": 1.0, # Scale of markers in the legend
|
||||
"legend.handleheight": 0.5, # Height of legend handles
|
||||
"legend.handlelength": 1.5, # Length of legend handles
|
||||
"legend.handletextpad": 0.25, # Padding between handle and text
|
||||
"legend.columnspacing": 1.5, # Spacing between legend columns
|
||||
"legend.labelspacing": 0.25, # Spacing between legend entries
|
||||
"legend.fontsize": 7, # Legend font size
|
||||
"legend.borderpad": 0.2, # Padding inside the legend box
|
||||
"axes.labelsize": 8, # Axis label font size
|
||||
"axes.linewidth": 0.5, # Axis line width
|
||||
"axes.grid": True, # Enable grid
|
||||
"axes.formatter.limits": (-3, 3),
|
||||
"axes.formatter.useoffset": True,
|
||||
"grid.color": '#0F0F0F', # Grid color
|
||||
"grid.alpha": 0.25, # Grid transparency
|
||||
"grid.linestyle": ':', # Grid line style
|
||||
"grid.linewidth": 0.5, # Grid line width
|
||||
"xtick.direction": "in", # Direction of x-ticks
|
||||
"ytick.direction": "in", # Direction of y-ticks
|
||||
"xtick.labelsize": 7, # Font size for x-tick labels
|
||||
"ytick.labelsize": 7 # Font size for y-tick labels
|
||||
}
|
||||
|
||||
# Override or extend the default parameters if custom settings are provided
|
||||
if custom_rcparams is not None:
|
||||
default_rcparams.update(custom_rcparams)
|
||||
|
||||
# Apply the final configuration to matplotlib
|
||||
# -- use plt.rcParams.keys() to list all available parameters
|
||||
plt.rcParams.update(default_rcparams)
|
||||
|
||||
def plt_export(filename=None, format='pdf', folder='.', dpi=96, fig=None):
|
||||
"""
|
||||
Save the current Matplotlib figure or a specified figure in the selected image format.
|
||||
|
||||
This function should be a standard part of chart scripts, enabling the "Export image"
|
||||
functionality of the IDE and `opp_charttool` to work seamlessly.
|
||||
|
||||
Parameters:
|
||||
- filename (str, optional): The output file name. If it has no extension, one will be added based on the format.
|
||||
If missing or empty, a sanitized version of the chart name is used.
|
||||
- format (str): The desired file format for the export. Default is 'pdf'.
|
||||
Accepted formats are those supported by Matplotlib (e.g., 'png', 'jpg', 'svg', 'pdf', etc.).
|
||||
- folder (str): The directory where the image file will be saved. Default is the current directory.
|
||||
- dpi (int): Dots per inch setting, default is 96. For raster image formats, the image dimensions are
|
||||
produced as width (or height) times dpi.
|
||||
- fig (matplotlib.figure.Figure, optional): The Matplotlib Figure object to export. If None, the current active figure is used.
|
||||
|
||||
Notes:
|
||||
- For export, even IDE-native charts are rendered using Matplotlib.
|
||||
- `dpi` controls the resolution for raster image formats while maintaining the chart's aspect ratio and relative sizes.
|
||||
"""
|
||||
|
||||
# Validate the format
|
||||
valid_formats = mpl.figure.Figure().savefig.__code__.co_varnames
|
||||
if format.lower() not in mpl.figure.Figure().canvas.get_supported_filetypes().keys():
|
||||
raise ValueError(f"Unsupported format '{format}'. Supported formats are: {', '.join(mpl.figure.Figure().canvas.get_supported_filetypes().keys())}")
|
||||
|
||||
# Handle filename
|
||||
if not filename:
|
||||
# If filename is None or empty, use a default name
|
||||
filename = 'exported_plot'
|
||||
|
||||
if not re.match(r".+\.\w{3,4}$", filename):
|
||||
filename += f".{format.lower()}"
|
||||
|
||||
filepath = os.path.normpath(os.path.join(folder, filename))
|
||||
os.makedirs(os.path.dirname(filepath) or ".", exist_ok=True)
|
||||
|
||||
# Function to create metadata
|
||||
def _make_image_metadata():
|
||||
creator_info = (
|
||||
f"Matplotlib {mpl.__version__}; "
|
||||
f"Pandas {pd.__version__}; "
|
||||
f"Python {platform.python_version()}; "
|
||||
f"{platform.platform()}"
|
||||
)
|
||||
return {"Creator": creator_info}
|
||||
|
||||
# Select the figure to save
|
||||
target_fig = fig or plt.gcf()
|
||||
|
||||
# Prepare metadata if supported
|
||||
metadata = _make_image_metadata() if format.lower() in ['pdf', 'svg', 'eps'] else None
|
||||
|
||||
# Attempt to save the figure
|
||||
try:
|
||||
target_fig.savefig(filepath, format=format, dpi=dpi, metadata=metadata, bbox_inches='tight')
|
||||
print(f'Exported image to: "{filepath}" as {format}')
|
||||
except Exception as e:
|
||||
print(f"Failed to save with metadata due to: {e}. Retrying without metadata.")
|
||||
try:
|
||||
target_fig.savefig(filepath, format=format, dpi=dpi, bbox_inches='tight')
|
||||
print(f'Exported image to: "{filepath}" as {format} without metadata')
|
||||
except Exception as e:
|
||||
print(f"Failed to save the figure: {e}")
|
||||
|
||||
def plt_export(filename=None, format='pdf', folder='.', dpi=96, fig=None):
|
||||
"""
|
||||
Save the current Matplotlib figure or a specified figure in the selected image format.
|
||||
|
||||
This function should be a standard part of chart scripts, enabling the "Export image"
|
||||
functionality of the IDE and `opp_charttool` to work seamlessly.
|
||||
|
||||
Parameters:
|
||||
- filename (str, optional): The output file name. If it has no extension, one will be
|
||||
added based on the format. If missing or empty, a default
|
||||
name 'exported_plot' is used.
|
||||
- format (str): The desired file format for the export. Default is 'pdf'.
|
||||
Accepted formats are those supported by Matplotlib (e.g., 'png', 'jpg',
|
||||
'svg', 'pdf', etc.).
|
||||
- folder (str): The directory where the image file will be saved. Default is the current directory.
|
||||
- dpi (int): Dots per inch setting, default is 96. For raster image formats, the image
|
||||
dimensions are produced as width (or height) times dpi.
|
||||
- fig (matplotlib.figure.Figure, optional): The Matplotlib Figure object to export. If None,
|
||||
the current active figure is used.
|
||||
|
||||
Notes:
|
||||
- For export, even IDE-native charts are rendered using Matplotlib.
|
||||
- `dpi` controls the resolution for raster image formats while maintaining the chart's
|
||||
aspect ratio and relative sizes.
|
||||
"""
|
||||
# Select the figure to save
|
||||
target_fig = fig or plt.gcf()
|
||||
|
||||
# Validate the format
|
||||
supported_formats = target_fig.canvas.get_supported_filetypes()
|
||||
if format.lower() not in supported_formats:
|
||||
raise ValueError(
|
||||
f"Unsupported format '{format}'. Supported formats are: "
|
||||
f"{', '.join(supported_formats.keys())}"
|
||||
)
|
||||
|
||||
# Handle filename
|
||||
if not filename:
|
||||
# Generate a default filename with a timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f'exported_plot_{timestamp}'
|
||||
|
||||
if not re.match(r".+\.\w{3,4}$", filename):
|
||||
filename += f".{format.lower()}"
|
||||
|
||||
filepath = os.path.normpath(os.path.join(folder, filename))
|
||||
os.makedirs(os.path.dirname(filepath) or ".", exist_ok=True)
|
||||
|
||||
def _get_mpl_backend_for_image_format(format):
|
||||
return {
|
||||
"png": "AGG",
|
||||
"jpg": "AGG",
|
||||
"jpeg": "AGG",
|
||||
"tif": "AGG",
|
||||
"tiff": "AGG",
|
||||
"raw": "AGG",
|
||||
"svg": "SVG",
|
||||
"svgz": "SVG",
|
||||
"ps" : "PS",
|
||||
"eps": "PS",
|
||||
"pdf": "PDF",
|
||||
"pgf": "PGF",
|
||||
}.get(format.lower(), None)
|
||||
|
||||
# Function to create metadata
|
||||
def _make_image_metadata():
|
||||
creator_info = (
|
||||
f"Matplotlib {mpl.__version__}; "
|
||||
f"Pandas {pd.__version__}; "
|
||||
f"Python {platform.python_version()}; "
|
||||
f"{platform.platform()}"
|
||||
)
|
||||
return {"Creator": creator_info}
|
||||
|
||||
# Prepare metadata if supported
|
||||
metadata = _make_image_metadata()
|
||||
# Explicitly select a non-interactive backend appropriate for the export format,
|
||||
# and keep auto-selection for any other formats not listed here.
|
||||
backend = _get_mpl_backend_for_image_format(format)
|
||||
|
||||
# Attempt to save the figure
|
||||
try:
|
||||
target_fig.savefig(
|
||||
filepath,
|
||||
format=format,
|
||||
dpi=dpi,
|
||||
metadata=metadata,
|
||||
backend=backend
|
||||
)
|
||||
print(f'Exported image to: "{filepath}" as {format}')
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Failed to save with metadata due to: {e}. "
|
||||
"Retrying without metadata."
|
||||
)
|
||||
try:
|
||||
target_fig.savefig(
|
||||
filepath,
|
||||
format=format,
|
||||
dpi=dpi,
|
||||
backend=backend
|
||||
)
|
||||
print(
|
||||
f'Exported image to: "{filepath}" as {format} without metadata'
|
||||
)
|
||||
except Exception as e_inner:
|
||||
print(f"Failed to save the figure: {e_inner}")
|
||||
Reference in New Issue
Block a user