windrose icon indicating copy to clipboard operation
windrose copied to clipboard

Display relative frequency with "%" format

Open ttllttttlltt opened this issue 5 years ago • 2 comments

Hi,

First of all, windrose package is great! I would like to suggest to add a fmt argument for the relative frequency. I showed the windrose plot to others and the main question was what is the number shown here with a radii_angle. Then I explained I used the normed=True and the numbers there were relative frequency. So I modified the windrose.py a little bit in section class WindroseAxes(PolarAxes) to add self.normed arg. I am attaching only the modified class and function below. Just add normed arg and fmtnum = "%.1f",fmtnorm = "%.1f%%" in set_radii_angle and change a little bit in _init_plot. I believe you could make it looks way better.

class WindroseAxes(PolarAxes):
    """

    Create a windrose axes

    """

    name = "windrose"

    def __init__(self, *args, **kwargs):
        """
        See Axes base class for args and kwargs documentation
        """

        # Uncomment to have the possibility to change the resolution directly
        # when the instance is created
        # self.RESOLUTION = kwargs.pop('resolution', 100)
        self.rmax = kwargs.pop("rmax", None)
        self.theta_labels = kwargs.pop("theta_labels", ["E", "N-E", "N", "N-W", "W", "S-W", "S", "S-E"])
        PolarAxes.__init__(self, *args, **kwargs)
        self.set_aspect("equal", adjustable="box", anchor="C")
        self.radii_angle = 67.5
        self.cla()
        self.normed = False
    @staticmethod
    def set_radii_angle(self, fmtnum = "%.1f",fmtnorm = "%.1f%%",**kwargs):
        """
        Set the radii labels angle
        """
        normed = self.normed

        kwargs.pop("labels", None)
        angle = kwargs.pop("angle", None)
        if angle is None:
            angle = self.radii_angle
        self.radii_angle = angle
        N = 5
        rmax = self.get_rmax()
        radii = np.linspace(0, rmax, N + 1)
        if rmax % N == 0:
            fmt = "%d"
        elif normed:
            fmt = fmtnorm
        else:
            fmt = fmtnum
        radii_labels = [fmt % r for r in radii]
        # radii_labels[0] = ""  # Removing label 0
        self.set_rgrids(
            radii=radii[1:], labels=radii_labels[1:], angle=self.radii_angle, **kwargs
        )
    def _init_plot(self, direction, var, **kwargs):
        """
        Internal method used by all plotting commands

        Parameters
        ----------
        direction : 1D array,
            directions the wind blows from, North centred
        var : 1D array,
            values of the variable to compute. Typically the wind speeds

        Other Parameters
        ----------------
        normed : boolean, default False
        blowto : boolean, default False
        colors : str or list of str, default None
            The colors of the plot.
        cmap : color map, default `jet`
            A :obj:`matplotlib.cm` colormap for the plot.
            Warning! It overrides `colors`.
        weibull_factors :
        mean_values :
        frequency :
        kwarg
            Any argument accepted by :obj:`matplotlib.pyplot.plot`.
        """

        # if weibull factors are entered overwrite direction and var
        if "weibull_factors" in kwargs or "mean_values" in kwargs:
            if "weibull_factors" in kwargs and "mean_values" in kwargs:
                raise TypeError("cannot specify both weibull_factors and mean_values")
            statistic_type = "unset"
            if "weibull_factors" in kwargs:
                statistic_type = "weibull"
                val = kwargs.pop("weibull_factors")
            elif "mean_values" in kwargs:
                statistic_type = "mean"
                val = kwargs.pop("mean_values")
            if val:
                if "frequency" not in kwargs:
                    raise TypeError(
                        "specify 'frequency' argument for statistical input"
                    )
                windFrequencies = kwargs.pop("frequency")
                if len(windFrequencies) != len(direction) or len(direction) != len(var):
                    if len(windFrequencies) != len(direction):
                        raise TypeError("len(frequency) != len(direction)")
                    elif len(direction) != len(var):
                        raise TypeError("len(frequency) != len(direction)")
                windSpeeds = []
                windDirections = []
                for dbin in range(len(direction)):
                    for _ in range(int(windFrequencies[dbin] * 10000)):
                        if statistic_type == "weibull":
                            windSpeeds.append(
                                random.weibullvariate(var[dbin][0], var[dbin][1])
                            )
                        elif statistic_type == "mean":
                            windSpeeds.append(
                                random.weibullvariate(var[dbin] * 2 / np.sqrt(np.pi), 2)
                            )
                        windDirections.append(direction[dbin])
                var, direction = windSpeeds, windDirections

        # self.cla()
        kwargs.pop("zorder", None)

        # Init of the bins array if not set
        bins = kwargs.pop("bins", None)
        if bins is None:
            bins = np.linspace(np.min(var), np.max(var), 6)
        if isinstance(bins, int):
            bins = np.linspace(np.min(var), np.max(var), bins)
        bins = np.asarray(bins)
        nbins = len(bins)

        # Number of sectors
        nsector = kwargs.pop("nsector", None)
        if nsector is None:
            nsector = 16

        # Sets the colors table based on the colormap or the "colors" argument
        colors = kwargs.pop("colors", None)
        cmap = kwargs.pop("cmap", None)
        if colors is not None:
            if isinstance(colors, str):
                colors = [colors] * nbins
            if isinstance(colors, (tuple, list)):
                if len(colors) != nbins:
                    raise ValueError("colors and bins must have same length")
        else:
            if cmap is None:
                cmap = mpl.cm.jet
            colors = self._colors(cmap, nbins)

        # Building the angles list
        angles = np.arange(0, -2 * np.pi, -2 * np.pi / nsector) + np.pi / 2

        normed = kwargs.pop("normed", False)
        self.normed = normed
        blowto = kwargs.pop("blowto", False)

        # Calm condition
        calm_limit = kwargs.pop("calm_limit", None)
        if calm_limit is not None:
            mask = var > calm_limit
            self.calm_count = len(var) - np.count_nonzero(mask)
            if normed:
                self.calm_count = self.calm_count * 100 / len(var)
            var = var[mask]
            direction = direction[mask]

        # Set the global information dictionnary
        self._info["dir"], self._info["bins"], self._info["table"] = histogram(
            direction, var, bins, nsector, normed, blowto
        )

        return bins, nbins, nsector, colors, angles, kwargs

ttllttttlltt avatar Nov 06 '20 22:11 ttllttttlltt

@ttllttttlltt your solution works a treat, thanks! I agree, it is something worth adding to the release version of windrose.py. Below is an example of your suggestion.

image

swartmilan avatar Mar 31 '21 14:03 swartmilan

Will something like this be merged? It is not possible to understand the plot without the percentage sign at all.

FerusAndBeyond avatar Mar 15 '22 09:03 FerusAndBeyond

Then I explained I used the normed=True and the numbers there were relative frequency.

@ttllttttlltt to be fair I never felt the need to explain these. With that said, if you want to put a pull request, I can review it and help you out with the details.

ocefpaf avatar Sep 21 '22 21:09 ocefpaf

Never mind. Sorry for keeping the issue open for a long time.

ttllttttlltt avatar Sep 24 '22 01:09 ttllttttlltt

Never mind. Sorry for keeping the issue open for a long time.

Not a problem. Sometimes I'll do some house cleaning and, hopefully, address some low hanging fruit. It is fine to leave issues open.

ocefpaf avatar Sep 24 '22 12:09 ocefpaf