Module phi.vis

Visualization: plotting, interactive user interfaces.

Use view() to show fields or field variables in an interactive user interface.

Use plot() to plot fields using Matplotlib.

See the user interface documentation at https://tum-pbs.github.io/PhiFlow/Visualization.html

Functions

def action(fun)
Expand source code
def action(fun):
    doc = inspect.getdoc(fun)
    ACTIONS[Action(fun.__name__, doc)] = fun
    return fun
def close(figure=None)
Expand source code
def close(figure=None):
    """
    Close and destroy a figure.

    Args:
        figure: (Optional) A figure that was created using `plot()`.
            If not specified, closes the figure created most recently.
    """
    if figure is None:
        figure = LAST_FIGURE[0]
    if isinstance(figure, Tensor):
        for fig in figure:
            close(fig)
    else:
        plots = get_plots_by_figure(figure)
        plots.close(figure)

Close and destroy a figure.

Args

figure
(Optional) A figure that was created using plot(). If not specified, closes the figure created most recently.
def control(value, range: tuple = None, description='', **kwargs)
Expand source code
def control(value, range: tuple = None, description="", **kwargs):
    """
    Mark a variable as controllable by any GUI created via `view()`.

    Example:
    >>> dt = control(1.0, (0.1, 10), name="Time increment (dt)")

    This will cause a control component (slider, checkbox, text field, drop-down, etc.) to be generated in the user interface.
    Changes to that component will immediately be reflected in the Python variable assigned to the control.
    The Python variable will always hold a primitive type, such as `int`, `float´, `bool` or `str`.

    Args:
        value: Initial value. Must be either `int`, `float`, `bool` or `str`.
        range: (Optional) Specify range of possible values as `(min, max)`. Only for `int`, `float` and `str` values.
        description: Human-readable description.
        **kwargs: Additional arguments to determine the appearance of the GUI component,
            e.g. `rows` for text fields or `log=False` for float sliders.

    Returns:
        `value`
    """
    assert type(value) in (int, float, bool, str), f"Value must be one of (int, float, bool, str) but {type(value)}"
    calling_code = inspect.stack()[1].code_context[0]
    assert 'control' in calling_code and '=' in calling_code, f"control() must be used in a variable assignment statement but context is: {calling_code}"
    calling_code = calling_code[:calling_code.index('control')]
    var_names = [var.strip() for var in calling_code.split('=')[:-1]]
    var_names = [n for n in var_names if n]
    for var_name in var_names:
        ctrl = Control(var_name, type(value), value, range, description, kwargs)
        value_range(ctrl)  # checks if valid
        CONTROL_VARS[var_name] = ctrl
    return value

Mark a variable as controllable by any GUI created via view().

Example:

>>> dt = control(1.0, (0.1, 10), name="Time increment (dt)")

This will cause a control component (slider, checkbox, text field, drop-down, etc.) to be generated in the user interface. Changes to that component will immediately be reflected in the Python variable assigned to the control. The Python variable will always hold a primitive type, such as int, float´,bool or str`.

Args

value
Initial value. Must be either int, float, bool or str.
range
(Optional) Specify range of possible values as (min, max). Only for int, float and str values.
description
Human-readable description.
**kwargs
Additional arguments to determine the appearance of the GUI component, e.g. rows for text fields or log=False for float sliders.

Returns

value

def load_scalars(scene: phi.field._scene.Scene,
name: str,
prefix='log_',
suffix='.txt',
x: str | None = 'steps',
entries_dim=(iterationˢ=None),
batch_dim=(batchᵇ=None))
Expand source code
@math.broadcast
def load_scalars(scene: Scene or str,
                 name: str,
                 prefix='log_',
                 suffix='.txt',
                 x: Optional[str]='steps',
                 entries_dim=spatial('iteration'),
                 batch_dim=batch('batch')):
    """
    Read one or a `Tensor` of scalar logs as curves.

    Args:
        scene: `Scene` or `str`. Directory containing the log files.
        name: Log file base name.
        prefix: Log file prefix.
        suffix: Log file suffix.
        x: 'steps'  or 'time'
        entries_dim: Curve dimension.

    Returns:
        `Tensor` containing `entries_dim` and `vector`.
    """
    assert x in (None, 'steps', 'time')
    if isinstance(scene, str):
        scene = Scene.at(scene)
    assert isinstance(scene, Scene), f"scene must be a Scene or str but got {type(scene)}"
    assert shape(scene).rank == 0, f"Use math.map(load_scalars, ...) to load data from multiple scenes"
    ML_LOGGER.debug(f"Reading {os.path.join(scene.path, f'{prefix}{name}{suffix}')}")
    curve = numpy.loadtxt(os.path.join(scene.path, f"log_{name}.txt"))
    if curve.ndim == 2:
        x_values = curve[:, 0]
        values = curve[:, 1:]
    elif curve.ndim == 1 and numpy.floor(curve[0]) == curve[0]:  # new format but only one entry
        x_values = curve[None, 0]
        values = curve[None, 1:]
    else:
        values = curve[:, None]
        x_values = numpy.arange(len(values))
    if x == 'time':
        assert x == 'time', f"x must be 'steps' or 'time' but got {x}"
        ML_LOGGER.debug(f"Reading {os.path.join(scene.path, 'log_step_time.txt')}")
        _, x_values, *_ = numpy.loadtxt(os.path.join(scene.path, "log_step_time.txt")).T
        values = values[:len(x_values + 1)]
        x_values = numpy.cumsum(x_values[:len(values) - 1])
        x_values = numpy.concatenate([[0.], x_values])
    x_values = wrap(x_values, entries_dim)
    values = wrap(values, entries_dim, batch_dim)
    if x is not None:
        return math.stack([x_values, values], channel(vector=[x, name]))
    return values

Read one or a Tensor of scalar logs as curves.

Args

scene
Scene or str. Directory containing the log files.
name
Log file base name.
prefix
Log file prefix.
suffix
Log file suffix.
x
'steps' or 'time'
entries_dim
Curve dimension.

Returns

Tensor containing entries_dim and vector.

def overlay(*fields: phi.field._field.Field | phi.geom._geom.Geometry | phiml.math._tensors.Tensor) ‑> phiml.math._tensors.Tensor
Expand source code
def overlay(*fields: Union[Field, Tensor, Geometry]) -> Tensor:
    """
    Specify that multiple fields should be drawn on top of one another in the same figure.
    The fields will be plotted in the order they are given, i.e. the last field on top.

    >>> plot(vis.overlay(heatmap, points, velocity))

    Args:
        *fields: `Field` or `Tensor` instances

    Returns:
        Plottable object
    """
    return layout(fields, math.channel('overlay'))

Specify that multiple fields should be drawn on top of one another in the same figure. The fields will be plotted in the order they are given, i.e. the last field on top.

>>> plot(vis.overlay(heatmap, points, velocity))

Args

*fields
Field or Tensor instances

Returns

Plottable object

def plot(*fields: phi.field._field.Field | phiml.math._tensors.Tensor | phi.geom._geom.Geometry | list | tuple | dict,
lib: str | phi.vis._vis_base.PlottingLibrary = None,
row_dims: str | Sequence | set | ForwardRef('Shape') | Callable | None = None,
col_dims: str | Sequence | set | ForwardRef('Shape') | Callable | None = <function batch>,
animate: str | Sequence | set | ForwardRef('Shape') | Callable | None = None,
overlay: str | Sequence | set | ForwardRef('Shape') | Callable | None = 'overlay',
title: str | phiml.math._tensors.Tensor | list | tuple = None,
size=None,
same_scale: bool | phiml.math._shape.Shape | tuple | list | str = True,
log_dims: str | phiml.math._shape.Shape | tuple | list = '',
show_color_bar=True,
color: str | int | phiml.math._tensors.Tensor | list | tuple = None,
alpha: float | phiml.math._tensors.Tensor | list | tuple = 1.0,
err: float | phiml.math._tensors.Tensor | list | tuple = 0.0,
frame_time=100,
repeat=True,
plt_params: Dict = None,
max_subfigures=20)
Expand source code
def plot(*fields: Union[Field, Tensor, Geometry, list, tuple, dict],
         lib: Union[str, PlottingLibrary] = None,
         row_dims: DimFilter = None,
         col_dims: DimFilter = batch,
         animate: DimFilter = None,
         overlay: DimFilter = 'overlay',
         title: Union[str, Tensor, list, tuple] = None,
         size=None,  # (12, 5),
         same_scale: Union[bool, Shape, tuple, list, str] = True,
         log_dims: Union[str, tuple, list, Shape] = '',
         show_color_bar=True,
         color: Union[str, int, Tensor, list, tuple] = None,
         alpha: Union[float, Tensor, list, tuple] = 1.,
         err: Union[Tensor, tuple, list, float] = 0.,
         frame_time=100,
         repeat=True,
         plt_params: Dict = None,
         max_subfigures=20):
    """
    Creates one or multiple figures and sub-figures and plots the given fields.

    To show the figures, use `show()`.

    The arguments `row_dims`, `col_dims`, `animate` and `overlay` control how data is presented.
    Each accepts dimensions as a `str`, `Shape`, tuple, list or type function.
    In addition to the dimensions present on the data to be plotted, the dimensions `args` is created if multiple arguments are passed,
    and `tuple`, `list`, `dict` are generated for corresponding objects to be plotted.

    Args:
        fields: Fields or Tensors to plot.
        lib: Plotting library name or reference. Valid names are `'matplotlib'`, `'plotly'` and `'console'`.
        row_dims: Batch dimensions along which sub-figures should be laid out vertically.
            `Shape` or comma-separated names as `str`, `tuple` or `list`.
        col_dims: Batch dimensions along which sub-figures should be laid out horizontally.
            `Shape` or comma-separated names as `str`, `tuple` or `list`.
        title: `str` for figures with a single subplot.
            For subplots, pass a string `Tensor` matching the content dimensions, i.e. `row_dims` and `col_dims`.
            Passing a `tuple`, `list` or `dict`, will create a tensor with these names internally.
        size: Figure size in inches, `(width, height)`.
        same_scale: Whether to use the same axis limits for all sub-figures.
        log_dims: Dimensions for which the plot axes should be scaled logarithmically.
            Can be given as a comma-separated `str`, a sequence of dimension names or a `Shape`.
            Use `'_'` to scale unnamed axes logarithmically, e.g. the y-axis of scalar functions.
        show_color_bar: Whether to display color bars for heat maps.
        color: `Tensor` of line / marker colors.
            The color can be specified either as a cycle index (int tensor) or as a hex code (str tensor).
            The color of different lines and markers can vary.
        alpha: Opacity as `float` or `Tensor`.
            This affects all elements, not only line plots.
            Opacity can vary between lines and markers.
        err: Expected deviation from the value given in `fields`.
            For supported plots, adds error bars of size *2·err*.
            If the plotted data is the mean of some distribution, a good choice for `err` is the standard deviation along the mean dims.
        animate: Time dimension to animate.
            If not present in the data, will produce a regular plot instead.
        overlay: Dimensions along which elements should be overlaid in the same subplot.
            The default is only the `overlay` dimension which is created by `overlay()`.
        frame_time: Interval between frames in the animation.
        repeat: Whether the animation should loop.

    Returns:
        `Tensor` of figure objects.
        The tensor contains those dimensions of `fields` that were not reduced by `row_dims`, `col_dims` or `animate`.
        Currently, only single-figure plots are supported.

        In case of an animation, a displayable animation object will be returned instead of a `Tensor`.
    """
    data = layout([layout_pytree_node(f) for f in fields], batch('args'))
    overlay = data.shape.only(overlay)
    animate = data.shape.only(animate).without(overlay)
    row_dims: Shape = data.shape.only(row_dims).without(animate).without(overlay)
    col_dims = data.shape.only(col_dims).without(row_dims).without(animate).without(overlay)
    fig_shape = batch(data).without(row_dims).without(col_dims).without(animate).without(overlay)
    reduced_shape = row_dims & col_dims & animate & overlay
    nrows = uniform_bound(row_dims).volume
    ncols = uniform_bound(col_dims).volume
    assert nrows * ncols <= max_subfigures, f"Too many subfigures ({nrows * ncols}) for max_subfigures={max_subfigures}. If you want to plot this many subfigures, increase the limit."
    positioning, indices = layout_sub_figures(data, row_dims, col_dims, animate, overlay, 0, 0)
    # --- Process arguments ---
    plots = default_plots(positioning) if lib is None else get_plots(lib)
    plt_params = {} if plt_params is None else dict(**plt_params)
    size = (None, None) if size is None else size
    if title is None:
        title_by_subplot = {pos: title_label(common_index(*i, exclude=reduced_shape.singleton)) for pos, i in indices.items()}
    elif isinstance(title, Tensor) and ('rows' in title.shape or 'cols' in title.shape):
        title_by_subplot = {(row, col): title.rows[row].cols[col].native() for (row, col) in positioning}
    else:
        title = layout_pytree_node(title, wrap_leaf=True)
        title_by_subplot = {pos: _title(title, i[0]) for pos, i in indices.items()}
    log_dims = parse_dim_order(log_dims) or ()
    color = layout_pytree_node(color, wrap_leaf=True)
    color = layout_color(positioning, indices, color)
    alpha = layout_pytree_node(alpha, wrap_leaf=True)
    alpha = tree_map(lambda x: 1 if x is None else x, alpha)
    err = layout_pytree_node(err, wrap_leaf=True)
    if same_scale is True:
        same_scale = '_'
    elif same_scale is False or same_scale is None:
        same_scale = ''
    same_scale = parse_dim_order(same_scale)
    if '_' in same_scale:
        if any([f.values.dtype.kind == complex for l in positioning.values() for f in l]):
            min_val = 0
            max_val = max([float(abs(f.values).finite_max) for l in positioning.values() for f in l] or [0])
        else:
            fin_min = lambda t: float(math.map(lambda f: math.finite_min(f.values, shape), t, dims=object).finite_min)
            fin_max = lambda t: float(math.map(lambda f: math.finite_max(f.values, shape), t, dims=object).finite_max)
            min_val = min([fin_min(f) for l in positioning.values() for f in l] or [0])
            max_val = max([fin_max(f) for l in positioning.values() for f in l] or [0])
            if min_val != min_val:  # NaN
                min_val = None
            if max_val != max_val:  # NaN
                max_val = None
    else:
        min_val = max_val = None
    # --- Layout ---
    subplots = {pos: _space(*fields, ignore_dims=animate, log_dims=log_dims, errs=[err[i] for i in indices[pos]]) for pos, fields in positioning.items()}
    subplots = {pos: _insert_value_dim(space, pos, subplots, min_val, max_val) for pos, space in subplots.items()}
    if same_scale:
        shared_lim: Box = share_axes(*subplots.values(), axes=same_scale)
        subplots = {pos: replace_bounds(lim, shared_lim) for pos, lim in subplots.items()}
    # --- animate or plot ---
    figures = []
    for plot_idx in fig_shape.meshgrid():
        figure, axes = plots.create_figure(size, nrows, ncols, subplots, log_dims, plt_params)
        if animate:
            def plot_frame(figure, frame: int):
                for pos, fields in positioning.items():
                    plots.set_title(title_by_subplot[pos], figure, axes[pos])
                    plots.set_title(display_name(animate.item_names[0][frame]) if animate.item_names[0] else None, figure, None)
                    for i, f in enumerate(fields):
                        idx = indices[pos][i]
                        f = f[{animate.name: frame}]
                        plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i], alpha[idx], err[idx])
                plots.finalize(figure)
            anim = plots.animate(figure, animate.size, plot_frame, frame_time, repeat, interactive=True, time_axis=animate.name)
            if is_jupyter():
                plots.close(figure)
            LAST_FIGURE[0] = anim
            if fig_shape.volume == 1:
                return anim
            figures.append(anim)
        else:  # non-animated plot
            for pos, fields in positioning.items():
                plots.set_title(title_by_subplot[pos], figure, axes[pos])
                for i, f in enumerate(fields):
                    idx = indices[pos][i]
                    plots.plot(f, figure, axes[pos], subplots[pos], min_val, max_val, show_color_bar, color[pos][i], alpha[idx], err[idx])
            plots.finalize(figure)
            LAST_FIGURE[0] = figure
            figures.append(figure)
    return stack([layout(f) for f in figures], fig_shape) if fig_shape else figures[0]

Creates one or multiple figures and sub-figures and plots the given fields.

To show the figures, use show().

The arguments row_dims, col_dims, animate and overlay() control how data is presented. Each accepts dimensions as a str, Shape, tuple, list or type function. In addition to the dimensions present on the data to be plotted, the dimensions args is created if multiple arguments are passed, and tuple, list, dict are generated for corresponding objects to be plotted.

Args

fields
Fields or Tensors to plot.
lib
Plotting library name or reference. Valid names are 'matplotlib', 'plotly' and 'console'.
row_dims
Batch dimensions along which sub-figures should be laid out vertically. Shape or comma-separated names as str, tuple or list.
col_dims
Batch dimensions along which sub-figures should be laid out horizontally. Shape or comma-separated names as str, tuple or list.
title
str for figures with a single subplot. For subplots, pass a string Tensor matching the content dimensions, i.e. row_dims and col_dims. Passing a tuple, list or dict, will create a tensor with these names internally.
size
Figure size in inches, (width, height).
same_scale
Whether to use the same axis limits for all sub-figures.
log_dims
Dimensions for which the plot axes should be scaled logarithmically. Can be given as a comma-separated str, a sequence of dimension names or a Shape. Use '_' to scale unnamed axes logarithmically, e.g. the y-axis of scalar functions.
show_color_bar
Whether to display color bars for heat maps.
color
Tensor of line / marker colors. The color can be specified either as a cycle index (int tensor) or as a hex code (str tensor). The color of different lines and markers can vary.
alpha
Opacity as float or Tensor. This affects all elements, not only line plots. Opacity can vary between lines and markers.
err
Expected deviation from the value given in fields. For supported plots, adds error bars of size 2·err. If the plotted data is the mean of some distribution, a good choice for err is the standard deviation along the mean dims.
animate
Time dimension to animate. If not present in the data, will produce a regular plot instead.
overlay
Dimensions along which elements should be overlaid in the same subplot. The default is only the overlay() dimension which is created by overlay().
frame_time
Interval between frames in the animation.
repeat
Whether the animation should loop.

Returns

Tensor of figure objects. The tensor contains those dimensions of fields that were not reduced by row_dims, col_dims or animate. Currently, only single-figure plots are supported.

In case of an animation, a displayable animation object will be returned instead of a Tensor.

def plot_scalars(*args, **kwargs)
Expand source code
def plot_scalars(*args, **kwargs):
    from ._matplotlib import plot_scalars
    return plot_scalars(*args, **kwargs)
def savefig(path: str, figure=None, dpi=120.0, close=False, transparent=True)
Expand source code
def write_image(path: str, figure=None, dpi=120., close=False, transparent=True):
    """
    Save a figure to an image file.

    Args:
        figure: Matplotlib or Plotly figure or text.
        path: File path.
        dpi: Pixels per inch.
        close: Whether to close the figure after saving it.
        transparent: Whether to save the figure with transparent background.
    """
    figure = figure or LAST_FIGURE[0]
    if figure is None:
        warnings.warn("No plot yet created with phi.vis; nothing to save.", RuntimeWarning)
        return
    assert figure is not None, "No figure to save."
    lib = get_plots_by_figure(figure)
    path = os.path.expanduser(path)
    directory = os.path.abspath(os.path.dirname(path))
    os.path.isdir(directory) or os.makedirs(directory)
    lib.save(figure, path, dpi, transparent)
    if close:
        close_(figure=figure)

Save a figure to an image file.

Args

figure
Matplotlib or Plotly figure or text.
path
File path.
dpi
Pixels per inch.
close
Whether to close the figure after saving it.
transparent
Whether to save the figure with transparent background.
def show(*fields: phi.field._field.Field | phiml.math._tensors.Tensor | phi.geom._geom.Geometry | list | tuple | dict,
lib: str | phi.vis._vis_base.PlottingLibrary = None,
row_dims: str | Sequence | set | ForwardRef('Shape') | Callable | None = None,
col_dims: str | Sequence | set | ForwardRef('Shape') | Callable | None = <function batch>,
animate: str | Sequence | set | ForwardRef('Shape') | Callable | None = None,
overlay: str | Sequence | set | ForwardRef('Shape') | Callable | None = 'overlay',
title: str | phiml.math._tensors.Tensor | list | tuple = None,
size=None,
same_scale: bool | phiml.math._shape.Shape | tuple | list | str = True,
log_dims: str | phiml.math._shape.Shape | tuple | list = '',
show_color_bar=True,
color: str | int | phiml.math._tensors.Tensor | list | tuple = None,
alpha: float | phiml.math._tensors.Tensor | list | tuple = 1.0,
err: float | phiml.math._tensors.Tensor | list | tuple = 0.0,
frame_time=100,
repeat=True,
plt_params: Dict = None,
max_subfigures=20)
Expand source code
def show(*fields: Union[Field, Tensor, Geometry, list, tuple, dict],
         lib: Union[str, PlottingLibrary] = None,
         row_dims: DimFilter = None,
         col_dims: DimFilter = batch,
         animate: DimFilter = None,
         overlay: DimFilter = 'overlay',
         title: Union[str, Tensor, list, tuple] = None,
         size=None,  # (12, 5),
         same_scale: Union[bool, Shape, tuple, list, str] = True,
         log_dims: Union[str, tuple, list, Shape] = '',
         show_color_bar=True,
         color: Union[str, int, Tensor, list, tuple] = None,
         alpha: Union[float, Tensor, list, tuple] = 1.,
         err: Union[Tensor, tuple, list, float] = 0.,
         frame_time=100,
         repeat=True,
         plt_params: Dict = None,
         max_subfigures=20):
    """
    Args:
        See `plot()`.
    """
    if not fields:  # only show, no plot
        if lib is not None:
            plots = get_plots(lib)
        else:
            if not LAST_FIGURE:
                warnings.warn("No plot yet created with phi.vis; nothing to show.", RuntimeWarning)
                return
            plots = get_plots_by_figure(LAST_FIGURE[0])
        return plots.show(plots.current_figure)
    else:
        kwargs = locals()
        del kwargs['fields']
        fig = plot(*fields, **kwargs)
        plots = get_plots_by_figure(fig)
        if isinstance(fig, Tensor):
            for fig in fig:
                plots.show(fig)
        else:
            return plots.show(fig)

Args

See plot().

def show_hist(data: phiml.math._tensors.Tensor,
bins=(binsⁱ=20),
weights=1,
same_bins: str | Sequence | set | ForwardRef('Shape') | Callable | None = None)
Expand source code
def show_hist(data: Tensor, bins=math.instance(bins=20), weights=1, same_bins: DimFilter = None):
    hist, edges, center = math.histogram(data, bins, weights, same_bins)
    show(PointCloud(center, hist))
def smooth(curves: phiml.math._tensors.Tensor,
n: int,
ext: phiml.math.extrapolation.Extrapolation = <phiml.math.extrapolation._SymmetricGradientExtrapolation object>) ‑> phiml.math._tensors.Tensor
Expand source code
@map_c2b
def smooth(curves: Tensor, n: int, ext: extrapolation.Extrapolation = extrapolation.SYMMETRIC_GRADIENT) -> Tensor:
    """
    Applies a smoothing kernel to curves, all channels independently.

    Args:
        curves: `Tensor` containing at least one spatial dimension
        n: Kernel size, i.e. number of values to average.

    Returns:
        Smoothed curves as `Tensor`
    """
    assert isinstance(n, int), f"n must be an int but got {n}"
    assert n >= 1, f"n must be at least 1 but got {n}"
    if n == 1:
        return curves
    kernel = ones(spatial(curves).with_sizes(n)) / n ** spatial(curves).rank
    return convolve(curves, kernel, extrapolation=ext)

Applies a smoothing kernel to curves, all channels independently.

Args

curves
Tensor containing at least one spatial dimension
n
Kernel size, i.e. number of values to average.

Returns

Smoothed curves as Tensor

def write_image(path: str, figure=None, dpi=120.0, close=False, transparent=True)
Expand source code
def write_image(path: str, figure=None, dpi=120., close=False, transparent=True):
    """
    Save a figure to an image file.

    Args:
        figure: Matplotlib or Plotly figure or text.
        path: File path.
        dpi: Pixels per inch.
        close: Whether to close the figure after saving it.
        transparent: Whether to save the figure with transparent background.
    """
    figure = figure or LAST_FIGURE[0]
    if figure is None:
        warnings.warn("No plot yet created with phi.vis; nothing to save.", RuntimeWarning)
        return
    assert figure is not None, "No figure to save."
    lib = get_plots_by_figure(figure)
    path = os.path.expanduser(path)
    directory = os.path.abspath(os.path.dirname(path))
    os.path.isdir(directory) or os.makedirs(directory)
    lib.save(figure, path, dpi, transparent)
    if close:
        close_(figure=figure)

Save a figure to an image file.

Args

figure
Matplotlib or Plotly figure or text.
path
File path.
dpi
Pixels per inch.
close
Whether to close the figure after saving it.
transparent
Whether to save the figure with transparent background.