# Path: metatlas/io/write_utils.py
# def make_dir_for(file_path):
# def check_existing_file(file_path, overwrite=False):
# def export_dataframe(dataframe, file_path, description, overwrite=False, **kwargs):
# def raise_on_diff(dataframe, file_path, description, **kwargs):
# def export_dataframe_die_on_diff(dataframe, file_path, description, overwrite=False, **kwargs):
# 
# Path: metatlas/plots/utils.py
# BACKGROUND_COLORS = ["white", "lightyellow", "whitesmoke", "lavenderblush"]
# def colors() -> Generator:
# def subplot_dimensions(num: int) -> Tuple[int, int]:
# def wrap_subplots(num: int, **kwargs) -> Tuple[matplotlib.figure.Figure, List[matplotlib.axes.Axes]]:
# def is_in_range(a: List[float], start: float, stop: float) -> List[bool]:
# def fill_under(
#     ax: matplotlib.axes.Axes,
#     x: List[float],
#     y: List[float],
#     between: Optional[Tuple[float, float]] = None,
#     **kwargs,
# ) -> None:
# def pdf_with_text(text: str, file_name: str, size: int = 24):


import datetime
import logging
import math
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import numpy.typing as npt
from abc import ABC
from typing import Optional, Sequence, Tuple
from matplotlib.backends.backend_pdf import PdfPages
from metatlas.io import write_utils
from metatlas.plots import utils
def _autoscale(ax: matplotlib.axes.Axes, axis: str = "y", sides: str = "both", margin: float = 0.1) -> None:
    """Autoscales the x or y axis of a given matplotlib ax object
    to fit the margins set by manually limits of the other axis,
    with margins in fraction of the width of the plot
    if sides is 'max' or 'min' then only adjust the limit on that side of axis"""
    assert axis in ["x", "y"]
    assert sides in ["both", "min", "max"]
    low, high = np.inf, -np.inf
    for artist in ax.collections + ax.lines:
        if axis == "y":
            set_lim = ax.set_ylim
            get_lim = ax.get_ylim
            cur_fixed_limit = ax.get_xlim()
            fixed, dependent = _get_xy(artist)
        else:
            set_lim = ax.set_xlim
            get_lim = ax.get_xlim
            cur_fixed_limit = ax.get_ylim()
            dependent, fixed = _get_xy(artist)
        low, high = _update_limts(low, high, fixed, dependent, cur_fixed_limit)
    margin = margin * (high - low)
    if low == np.inf and high == -np.inf:
        return
    assert low != np.inf and high != -np.inf
    new_min = (low - margin) if sides in ["both", "min"] else get_lim()[0]
    new_max = (high + margin) if sides in ["both", "max"] else get_lim()[1]
    set_lim(new_min, new_max)


def _update_limts(

# Path: metatlas/io/write_utils.py
# def make_dir_for(file_path):
# def check_existing_file(file_path, overwrite=False):
# def export_dataframe(dataframe, file_path, description, overwrite=False, **kwargs):
# def raise_on_diff(dataframe, file_path, description, **kwargs):
# def export_dataframe_die_on_diff(dataframe, file_path, description, overwrite=False, **kwargs):
# 
# Path: metatlas/plots/utils.py
# BACKGROUND_COLORS = ["white", "lightyellow", "whitesmoke", "lavenderblush"]
# def colors() -> Generator:
# def subplot_dimensions(num: int) -> Tuple[int, int]:
# def wrap_subplots(num: int, **kwargs) -> Tuple[matplotlib.figure.Figure, List[matplotlib.axes.Axes]]:
# def is_in_range(a: List[float], start: float, stop: float) -> List[bool]:
# def fill_under(
#     ax: matplotlib.axes.Axes,
#     x: List[float],
#     y: List[float],
#     between: Optional[Tuple[float, float]] = None,
#     **kwargs,
# ) -> None:
# def pdf_with_text(text: str, file_name: str, size: int = 24):


# Path: metatlas/plots/plot_set.py
import datetime
import logging
import math
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import numpy.typing as npt
from abc import ABC
from typing import Optional, Sequence, Tuple
from matplotlib.backends.backend_pdf import PdfPages
from metatlas.io import write_utils
from metatlas.plots import utils

def _autoscale(ax: matplotlib.axes.Axes, axis: str = "y", sides: str = "both", margin: float = 0.1) -> None:
    """Autoscales the x or y axis of a given matplotlib ax object
    to fit the margins set by manually limits of the other axis,
    with margins in fraction of the width of the plot
    if sides is 'max' or 'min' then only adjust the limit on that side of axis"""
    assert axis in ["x", "y"]
    assert sides in ["both", "min", "max"]
    low, high = np.inf, -np.inf
    for artist in ax.collections + ax.lines:
        if axis == "y":
            set_lim = ax.set_ylim
            get_lim = ax.get_ylim
            cur_fixed_limit = ax.get_xlim()
            fixed, dependent = _get_xy(artist)
        else:
            set_lim = ax.set_xlim
            get_lim = ax.get_xlim
            cur_fixed_limit = ax.get_ylim()
            dependent, fixed = _get_xy(artist)
        low, high = _update_limts(low, high, fixed, dependent, cur_fixed_limit)
    margin = margin * (high - low)
    if low == np.inf and high == -np.inf:
        return
    assert low != np.inf and high != -np.inf
    new_min = (low - margin) if sides in ["both", "min"] else get_lim()[0]
    new_max = (high + margin) if sides in ["both", "max"] else get_lim()[1]
    set_lim(new_min, new_max)


def _update_limts(

    low: float,