<reponame>wdobbels/CAAPR<gh_stars>1-10
#!/usr/bin/env python
# -*- coding: utf8 -*-
# *****************************************************************
# **       PTS -- Python Toolkit for working with SKIRT          **
# **       © Astronomical Observatory, Ghent University          **
# *****************************************************************

## \package pts.modeling.plotting.distribution Contains the DistributionPlotter class.

# -----------------------------------------------------------------

# Ensure Python 3 compatibility
from __future__ import absolute_import, division, print_function

# Import standard modules
import math
import matplotlib.pyplot as plt
from collections import OrderedDict
from textwrap import wrap
#import seaborn as sns
import matplotlib.gridspec as gridspec

# Import the relevant PTS classes and modules
from ..tools.logging import log
from ...magic.tools.plotting import line_styles, filled_markers, pretty_colours

# -----------------------------------------------------------------

class DistributionPlotter(object):
    
    """
    This class ...
    """

    def __init__(self, title=None):

        """
        This function ...
        :return:
        """

        # Set the title
        self.title = title

        # The name of the variable
        self.name = None

        # The different distributions
        self.distributions = OrderedDict()

        # Keep track of the minimal and maximal value and count encountered during the plotting
        self._min_value = None
        self._max_value = None
        self._min_count = None
        self._max_count = None

        # The definite axes limits
        self.min_value = None
        self.max_value = None
        self.min_count = None
        self.max_count = None

        # Store the figure and its axes as references
        self._figure = None

        # Properties
        self.transparent = False

    # -----------------------------------------------------------------

    def set_title(self, title):

        """
        This function ...
        :param title:
        :return:
        """

        self.title = title

    # -----------------------------------------------------------------

    def set_variable_name(self, name):

        """
        This function ...
        :param name:
        :return:
        """

        self.name = name

    # -----------------------------------------------------------------

    def add_distribution(self, distribution, label):

        """
        This function ...
        :param distribution:
        :param label:
        :return:
        """

        self.distributions[label] = distribution

    # -----------------------------------------------------------------

    def run(self, output_path, min_value=None, max_value=None, min_count=None, max_count=None, format=None, logscale=False):

        """
        This function ...
        :param output_path:
        :param min_value:
        :param max_value:
        :param min_count:
        :param max_count:
        :param format:
        :param logscale:
        :return:
        """

        # Set the axis limits
        self.min_value = min_value
        self.max_value = max_value
        self.min_count = min_count
        self.max_count = max_count

        # Make the plot
        self.plot(output_path, format=format, logscale=logscale)

    # -----------------------------------------------------------------

    def clear(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Clearing the distribution plotter ...")

        # Set default values for all attributes
        self.title = None
        self.distributions = OrderedDict()
        self._min_value = None
        self._max_value = None
        self._min_count = None
        self._max_count = None
        self.min_value = None
        self.max_value = None
        self.min_count = None
        self.max_count = None

    # -----------------------------------------------------------------

    def plot(self, path, logscale=False, add_smooth=False, add_extrema=False, format=None, add_statistics=True):

        """
        This function ...
        :param path:
        :param logscale:
        :param add_smooth:
        :param add_extrema:
        :param format:
        :param add_statistics:
        :return:
        """

        # Inform the user
        log.info("Making the distribution plot ...")

        # Plot the attenuation curves
        plt.figure(figsize=(10, 10))

        # Create the figure
        self._figure = plt.figure()

        colors = iter(pretty_colours)

        # Plot the distributions
        for label in self.distributions:

            distribution = self.distributions[label]

            # Plot the distribution as a histogram
            plt.bar(distribution.edges[:-1], distribution.counts, linewidth=0, width=distribution.bin_width, alpha=0.5, color=next(colors))

            min_value = distribution.min_value
            max_value = distribution.max_value
            min_count = 0. if not logscale else 0.5 * distribution.min_count_nonzero
            max_count = 1.1 * distribution.max_count if not logscale else 2. * distribution.max_count

            # Keep track of minimum and maximum value
            if self._min_value is None or min_value < self._min_value: self._min_value = min_value
            if self._max_value is None or max_value > self._max_value: self._max_value = max_value

            # Keep track of the minimal and maximal count
            if self._min_count is None or min_count < self._min_count: self._min_count = min_count
            if self._max_count is None or max_count > self._max_count: self._max_count = max_count

            # Add smooth
            if add_smooth:

                if logscale:
                    x_smooth, y_smooth = distribution.smooth_values_log(x_min=self._min_value, x_max=self._max_value)
                    plt.plot(x_smooth, y_smooth, 'red', linewidth=1)
                else:
                    x_smooth, y_smooth = distribution.smooth_values(x_min=self._min_value, x_max=self._max_value)
                    plt.plot(x_smooth, y_smooth, 'red', linewidth=1)

            if add_extrema:

                x, y = distribution.local_maxima
                plt.plot(x, y, 'g^')

                x, y = distribution.local_minima
                plt.plot(x, y, 'rv')

            if add_statistics:

                plt.axvline(distribution.mean, color="green", linestyle="dashed")
                plt.axvline(distribution.median, color="purple", linestyle="dashed")
                plt.axvline(distribution.most_frequent, color="orange", linestyle="dashed")

        # Axis limits are now definite
        if self.min_value is None: self.min_value = self._min_value
        if self.max_value is None: self.max_value = self._max_value
        if self.min_count is None: self.min_count = self._min_count
        if self.max_count is None: self.max_count = self._max_count

        axes = plt.gca()

        # Set the axis limits
        axes.set_xlim(self.min_value, self.max_value)
        axes.set_ylim(self.min_count, self.max_count)

        # Set the axis labels
        if self.name is not None: axes.set_xlabel(self.name)
        axes.set_ylabel("Normalized count")

        # Set the title
        if self.title is not None: axes.set_title("\n".join(wrap(self.title, 60)))

        if logscale: axes.set_yscale("log", nonposx='clip')

        plt.tight_layout()

        # Debugging
        if type(path).__name__ == "BytesIO": log.debug("Saving the distribution plot to a buffer ...")
        else: log.debug("Saving the distribution plot to " + str(path) + " ...")

        # Save the figure
        plt.savefig(path, bbox_inches='tight', pad_inches=0.25, format=format, transparent=self.transparent)
        plt.close()

# -----------------------------------------------------------------

class DistributionGridPlotter(object):

    """
    This class ...
    """

    def __init__(self, title=None):

        """
        The constructor ...
        """

        # Set the title
        self.title = title

        # The different distributions
        self.distributions = OrderedDict()
        self.extra_distributions = dict()

        # Properties
        self.format = None
        self.transparent = False
        self.ncols = 7
        self.width = 16

    # -----------------------------------------------------------------

    def add_distribution(self, distribution, label):

        """
        This function ...
        :return:
        """

        if label in self.distributions: self.extra_distributions[label] = distribution
        else: self.distributions[label] = distribution

    # -----------------------------------------------------------------

    def run(self, path):

        """
        This function ...
        :param path:
        :return:
        """

        # Make the plot
        self.plot(path)

    # -----------------------------------------------------------------

    @property
    def npanels(self):

        """
        This function ...
        :return:
        """

        return len(self.distributions)

    # -----------------------------------------------------------------

    def plot(self, path):

        """
        This function ...
        :parm path:
        :return:
        """

        # Determine the necessary number of rows
        nrows = int(math.ceil(self.npanels / self.ncols))

        ratio = float(nrows) / float(self.ncols)
        height = ratio * self.width

        # Create the figure
        self._figure = plt.figure(figsize=(self.width, height))

        self._figure.subplots_adjust(hspace=0.0, wspace=0.0)


        gs = gridspec.GridSpec(nrows, self.ncols, wspace=0.0, hspace=0.0)

        # Loop over the distributions
        counter = 0
        ax = None
        for label in self.distributions:

            row = int(counter / self.ncols)
            col = counter % self.ncols

            subplotspec = gs[row, col]

            ax = plt.subplot(subplotspec)

            distribution = self.distributions[label]

            # Plot the distribution as a histogram
            ax.bar(distribution.edges[:-1], distribution.counts, linewidth=0, width=distribution.bin_width, alpha=0.5, color=pretty_colours[0])

            if label in self.extra_distributions:

                extra_distribution = self.extra_distributions[label]

                # Plot the distribution as a histogram
                ax.bar(extra_distribution.edges[:-1], extra_distribution.counts, linewidth=0, width=extra_distribution.bin_width, alpha=0.5, color=pretty_colours[1])

            counter += 1

        # Finish
        self.finish_plot(path)

    # -----------------------------------------------------------------

    def finish_plot(self, path):

        """
        This function ...
        :param path:
        :return:
        """

        # Set the title
        if self.title is not None: self._figure.suptitle("\n".join(wrap(self.title, 60)))

        # plt.tight_layout()

        # Debugging
        if type(path).__name__ == "BytesIO": log.debug("Saving the distribution plot to a buffer ...")
        elif path is None: log.debug("Showing the distribution plot ...")
        else: log.debug("Saving the distribution plot to " + str(path) + " ...")

        if path is not None:
            # Save the figure
            plt.savefig(path, bbox_inches='tight', pad_inches=0.25, transparent=self.transparent, format=self.format)
        else: plt.show()
        plt.close()

# -----------------------------------------------------------------

#!/usr/bin/env python
# -*- coding: utf8 -*-
# *****************************************************************
# **       PTS -- Python Toolkit for working with SKIRT          **
# **       © Astronomical Observatory, Ghent University          **
# *****************************************************************

## \package pts.modeling.plotting.distribution Contains the DistributionPlotter class.

# -----------------------------------------------------------------

# Ensure Python 3 compatibility
from __future__ import absolute_import, division, print_function

# Import standard modules
import math
import matplotlib.pyplot as plt
from collections import OrderedDict
from textwrap import wrap
#import seaborn as sns
import matplotlib.gridspec as gridspec

# Import the relevant PTS classes and modules
from ..tools.logging import log
from ...magic.tools.plotting import line_styles, filled_markers, pretty_colours

# -----------------------------------------------------------------

class DistributionPlotter(object):
    
    """
    This class ...
    """

    def __init__(self, title=None):

        """
        This function ...
        :return:
        """

        # Set the title
        self.title = title

        # The name of the variable
        self.name = None

        # The different distributions
        self.distributions = OrderedDict()

        # Keep track of the minimal and maximal value and count encountered during the plotting
        self._min_value = None
        self._max_value = None
        self._min_count = None
        self._max_count = None

        # The definite axes limits
        self.min_value = None
        self.max_value = None
        self.min_count = None
        self.max_count = None

        # Store the figure and its axes as references
        self._figure = None

        # Properties
        self.transparent = False

    # -----------------------------------------------------------------

    def set_title(self, title):

        """
        This function ...
        :param title:
        :return:
        """

        self.title = title

    # -----------------------------------------------------------------

    def set_variable_name(self, name):

        """
        This function ...
        :param name:
        :return:
        """

        self.name = name

    # -----------------------------------------------------------------

    def add_distribution(self, distribution, label):

        """
        This function ...
        :param distribution:
        :param label:
        :return:
        """

        self.distributions[label] = distribution

    # -----------------------------------------------------------------

    def run(self, output_path, min_value=None, max_value=None, min_count=None, max_count=None, format=None, logscale=False):

        """
        This function ...
        :param output_path:
        :param min_value:
        :param max_value:
        :param min_count:
        :param max_count:
        :param format:
        :param logscale:
        :return:
        """

        # Set the axis limits
        self.min_value = min_value
        self.max_value = max_value
        self.min_count = min_count
        self.max_count = max_count

        # Make the plot
        self.plot(output_path, format=format, logscale=logscale)

    # -----------------------------------------------------------------

    def clear(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Clearing the distribution plotter ...")

        # Set default values for all attributes
        self.title = None
        self.distributions = OrderedDict()
        self._min_value = None
        self._max_value = None
        self._min_count = None
        self._max_count = None
        self.min_value = None
        self.max_value = None
        self.min_count = None
        self.max_count = None

    # -----------------------------------------------------------------

    def plot(self, path, logscale=False, add_smooth=False, add_extrema=False, format=None, add_statistics=True):

        """
        This function ...
        :param path:
        :param logscale:
        :param add_smooth:
        :param add_extrema:
        :param format:
        :param add_statistics:
        :return:
        """

        # Inform the user
        log.info("Making the distribution plot ...")

        # Plot the attenuation curves
        plt.figure(figsize=(10, 10))

        # Create the figure
        self._figure = plt.figure()

        colors = iter(pretty_colours)

        # Plot the distributions
        for label in self.distributions:

            distribution = self.distributions[label]

            # Plot the distribution as a histogram
            plt.bar(distribution.edges[:-1], distribution.counts, linewidth=0, width=distribution.bin_width, alpha=0.5, color=next(colors))

            min_value = distribution.min_value
            max_value = distribution.max_value
            min_count = 0. if not logscale else 0.5 * distribution.min_count_nonzero
            max_count = 1.1 * distribution.max_count if not logscale else 2. * distribution.max_count

            # Keep track of minimum and maximum value
            if self._min_value is None or min_value < self._min_value: self._min_value = min_value
            if self._max_value is None or max_value > self._max_value: self._max_value = max_value

            # Keep track of the minimal and maximal count
            if self._min_count is None or min_count < self._min_count: self._min_count = min_count
            if self._max_count is None or max_count > self._max_count: self._max_count = max_count

            # Add smooth
            if add_smooth:

                if logscale:
                    x_smooth, y_smooth = distribution.smooth_values_log(x_min=self._min_value, x_max=self._max_value)
                    plt.plot(x_smooth, y_smooth, 'red', linewidth=1)
                else:
                    x_smooth, y_smooth = distribution.smooth_values(x_min=self._min_value, x_max=self._max_value)
                    plt.plot(x_smooth, y_smooth, 'red', linewidth=1)

            if add_extrema:

                x, y = distribution.local_maxima
                plt.plot(x, y, 'g^')

                x, y = distribution.local_minima
                plt.plot(x, y, 'rv')

            if add_statistics:

                plt.axvline(distribution.mean, color="green", linestyle="dashed")
                plt.axvline(distribution.median, color="purple", linestyle="dashed")
                plt.axvline(distribution.most_frequent, color="orange", linestyle="dashed")

        # Axis limits are now definite
        if self.min_value is None: self.min_value = self._min_value
        if self.max_value is None: self.max_value = self._max_value
        if self.min_count is None: self.min_count = self._min_count
        if self.max_count is None: self.max_count = self._max_count

        axes = plt.gca()

        # Set the axis limits
        axes.set_xlim(self.min_value, self.max_value)
        axes.set_ylim(self.min_count, self.max_count)

        # Set the axis labels
        if self.name is not None: axes.set_xlabel(self.name)
        axes.set_ylabel("Normalized count")

        # Set the title
        if self.title is not None: axes.set_title("\n".join(wrap(self.title, 60)))

        if logscale: axes.set_yscale("log", nonposx='clip')

        plt.tight_layout()

        # Debugging
        if type(path).__name__ == "BytesIO": log.debug("Saving the distribution plot to a buffer ...")
        else: log.debug("Saving the distribution plot to " + str(path) + " ...")

        # Save the figure
        plt.savefig(path, bbox_inches='tight', pad_inches=0.25, format=format, transparent=self.transparent)
        plt.close()

# -----------------------------------------------------------------

class DistributionGridPlotter(object):

    """
    This class ...
    """

    def __init__(self, title=None):

        """
        The constructor ...
        """

        # Set the title
        self.title = title

        # The different distributions
        self.distributions = OrderedDict()
        self.extra_distributions = dict()

        # Properties
        self.format = None
        self.transparent = False
        self.ncols = 7
        self.width = 16

    # -----------------------------------------------------------------

    def add_distribution(self, distribution, label):

        """
        This function ...
        :return:
        """

        if label in self.distributions: self.extra_distributions[label] = distribution
        else: self.distributions[label] = distribution

    # -----------------------------------------------------------------

    def run(self, path):

        """
        This function ...
        :param path:
        :return:
        """

        # Make the plot
        self.plot(path)

    # -----------------------------------------------------------------

    @property
    def npanels(self):

        """
        This function ...
        :return:
        """

        return len(self.distributions)

    # -----------------------------------------------------------------

    def plot(self, path):

        """
        This function ...
        :parm path:
        :return:
        """

        # Determine the necessary number of rows
        nrows = int(math.ceil(self.npanels / self.ncols))

        ratio = float(nrows) / float(self.ncols)
        height = ratio * self.width

        # Create the figure
        self._figure = plt.figure(figsize=(self.width, height))

        self._figure.subplots_adjust(hspace=0.0, wspace=0.0)


        gs = gridspec.GridSpec(nrows, self.ncols, wspace=0.0, hspace=0.0)

        # Loop over the distributions
        counter = 0
        ax = None
        for label in self.distributions:

            row = int(counter / self.ncols)
            col = counter % self.ncols

            subplotspec = gs[row, col]

            ax = plt.subplot(subplotspec)

            distribution = self.distributions[label]

            # Plot the distribution as a histogram
            ax.bar(distribution.edges[:-1], distribution.counts, linewidth=0, width=distribution.bin_width, alpha=0.5, color=pretty_colours[0])

            if label in self.extra_distributions:

                extra_distribution = self.extra_distributions[label]

                # Plot the distribution as a histogram
                ax.bar(extra_distribution.edges[:-1], extra_distribution.counts, linewidth=0, width=extra_distribution.bin_width, alpha=0.5, color=pretty_colours[1])

            counter += 1

        # Finish
        self.finish_plot(path)

    # -----------------------------------------------------------------

    def finish_plot(self, path):

        """
        This function ...
        :param path:
        :return:
        """

        # Set the title
        if self.title is not None: self._figure.suptitle("\n".join(wrap(self.title, 60)))

        # plt.tight_layout()

        # Debugging
        if type(path).__name__ == "BytesIO": log.debug("Saving the distribution plot to a buffer ...")
        elif path is None: log.debug("Showing the distribution plot ...")
        else: log.debug("Saving the distribution plot to " + str(path) + " ...")

        if path is not None:
            # Save the figure
            plt.savefig(path, bbox_inches='tight', pad_inches=0.25, transparent=self.transparent, format=self.format)
        else: plt.show()
        plt.close()

# -----------------------------------------------------------------
#!/usr/bin/env python # -*- coding: utf8 -*- # ***************************************************************** # **       PTS -- Python Toolkit for working with SKIRT          ** # **       © Astronomical Observatory, Ghent University          ** # ***************************************************************** ## \package pts.modeling.plotting.distribution Contains the DistributionPlotter class. # ----------------------------------------------------------------- # Ensure Python 3 compatibility # Import standard modules #import seaborn as sns # Import the relevant PTS classes and modules # ----------------------------------------------------------------- This class ... This function ...         :return: # Set the title # The name of the variable # The different distributions # Keep track of the minimal and maximal value and count encountered during the plotting # The definite axes limits # Store the figure and its axes as references # Properties # ----------------------------------------------------------------- This function ...         :param title:         :return: # ----------------------------------------------------------------- This function ...         :param name:         :return: # ----------------------------------------------------------------- This function ...         :param distribution:         :param label:         :return: # ----------------------------------------------------------------- This function ...         :param output_path:         :param min_value:         :param max_value:         :param min_count:         :param max_count:         :param format:         :param logscale:         :return: # Set the axis limits # Make the plot # ----------------------------------------------------------------- This function ...         :return: # Inform the user # Set default values for all attributes # ----------------------------------------------------------------- This function ...         :param path:         :param logscale:         :param add_smooth:         :param add_extrema:         :param format:         :param add_statistics:         :return: # Inform the user # Plot the attenuation curves # Create the figure # Plot the distributions # Plot the distribution as a histogram # Keep track of minimum and maximum value # Keep track of the minimal and maximal count # Add smooth # Axis limits are now definite # Set the axis limits # Set the axis labels # Set the title # Debugging # Save the figure # ----------------------------------------------------------------- This class ... The constructor ... # Set the title # The different distributions # Properties # ----------------------------------------------------------------- This function ...         :return: # ----------------------------------------------------------------- This function ...         :param path:         :return: # Make the plot # ----------------------------------------------------------------- This function ...         :return: # ----------------------------------------------------------------- This function ...         :parm path:         :return: # Determine the necessary number of rows # Create the figure # Loop over the distributions # Plot the distribution as a histogram # Plot the distribution as a histogram # Finish # ----------------------------------------------------------------- This function ...         :param path:         :return: # Set the title # plt.tight_layout() # Debugging # Save the figure # -----------------------------------------------------------------