Search code examples
python-3.xmatplotlibbar-chartrgbarose-plot

"ValueError: Invalid RGBA argument: None" when plotting via matplotlib ax.bar() for creating a windrose using custom colormaps


The goal is to create a wind rose like in this graphic: enter image description here

The error itself occurs upon saving the plot via fig.savefig(), but I got so far to realize that the error lies in the colormaps employed (e.g. taking into account this discussion).

The bar-plot was tried to be added to a WindroseAxes() - axis - instance, which worked previously just fine. This can be implemented via from windrose import WindroseAxes. I've mentioned it here for if it plays a role in the problem.

# * Instantiate windrose-axis related to the global figure and the just created rectangle
# NOTE on passing theta-labels: need to be passed, otherwise an error will be thrown like 'keyword "theta_labels" does not exist'
# --> from windrose.py: self.theta_labels = kwargs.pop("theta_labels") or ["E", "N-E", "N", "N-W", "W", "S-W", "S", "S-E"]
ax = WindroseAxes(fig, rect, theta_labels=theta_labels)
fig.add_axes(ax)

The specifics of the data-frame employed:

sub_df.info()
None
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 7130 entries, 2018-07-01 00:05:00+02:00 to 2018-07-31 23:55:00+02:00
Data columns (total 8 columns):
 #   Column                          Non-Null Count  Dtype  
---  ------                          --------------  -----  
 0   Atm. pressure mean              7130 non-null   float64
 1   Daily cumulative precipitation  7130 non-null   float64
 2   Precipitation                   7130 non-null   float64
 3   RH_mean                         7130 non-null   float64
 4   T_mean                          7130 non-null   float64
 5   Wind direction mean             7130 non-null   float64
 6   Wind speed max                  7130 non-null   float64
 7   Wind speed mean                 7130 non-null   float64
dtypes: float64(8)
memory usage: 821.3 KB

In what follows, I'm going to delineate how the ax.bar() - function (with the intent to plot a windrose) and the associated colorbar were plotted:

# * Define colormap
# Colormap increases the contrast from 0 to 1 in blue (or the color of choice)
# NOTE on type: <matplotlib.colors.LinearSegmentedColormap object at 0x7fd8b7d3d850>
cmap = plt.cm.Blues  # pylint: disable=no-member


## * Get bins for colormap used for all subplots equally * ##
# i) Get maximum value
# Use .max() instead of max(), otherwise NaN could be considered the maximum
real_max_val = df[var_name].max()
# Maximum value here: 11 (m/s)
max_val = real_max_val.round()
# NOTE on scope: when rounded down, add 1 to make it greater than the actual maximum value
if max_val < real_max_val:
    max_val += 1

# ii) Set the bins linearly (not logarithmically as often seen)
# NOTE: units are assumed in meters per second (m/s)
# NOTE on bins: divides each wind-bar into e.g. 6 bins == stepnumber in linspace (default)
# NOTE on minimum value: the lowest possible is 0 m/s
bins_bar_plot = np.linspace(0, max_val, num_of_bins)  # (start, end, stepnumber)
cmap_norm = mpl.colors.Normalize(vmin=0, vmax=max_val)


# * Create plot based on the current sub-df
ax.bar(sub_df['Wind direction mean'],
        sub_df['Wind speed mean'],
        normed=True,
        facecolor=(0.03137254901960784, 0.18823529411764706, 0.4196078431372549, 1.0),
        bins=array([ 0. ,  2.2,  4.4,  6.6,  8.8, 11. ]),
        cmap=cmap,
        alpha=1.0,
        opening=1.0,
        "grey"="grey",
        linewidth=0.1)

The colorbar involved was then implemented like so:

fig.subplots_adjust(right=0.8)
# [x_coord start, y_coord start, total width, total height] - all in percent of the figure dimensions
cbar_ax = fig.add_axes([0.95, 0.15, 0.025, 0.7])
cbar = mpl.colorbar.ColorbarBase(cbar_ax,
                                    cmap=cmap,
                                    norm=cmap_norm,
                                    spacing='proportional',
                                    alpha=1.0)
cbar.set_label("{} {}".format('Wind speed mean', tools.units("Wind speed")),
                color=None,
                weight=None,
                fontsize=None)
# NOTE: vertically oriented colorbar: y-label
# Length of ticklabels here: 6
new_yticklabels = np.linspace(0, max_val,
                                len(list(cbar.ax.get_yticklabels())))
new_yticklabels = np.around(new_yticklabels, decimals=1)

# New ticklabels here: array([ 0. ,  2.2,  4.4,  6.6,  8.8, 11. ])
cbar.ax.set_yticklabels(new_yticklabels,
                        color=None,
                        weight=None,
                        fontsize=None)

The entire error-traceback printed in the console is:

   ValueError                                Traceback (most recent call last)
    ~/Desktop/Programming/Python/Scripts/General/Plotting/windrose_plotting.py in <module>
        184             textcolor="blue")
        185         # * Carry out plot of the current sub-df
    --> 186         plot.windrose_subplot(
        187             df=df_plot.loc[m_loc],
        188             desired_years=desired_years,

~/Dokumente/Allgemeines_material/Sonstiges/Programming/Python/Scripts/General/Plotting/plotting.py in windrose_subplot(df, figsize, groupby_freq, desired_years, desired_months, theta_labels, max_cols_per_row, ax_title_pos, cmap, y_coord_suptitle_dict, title_font_size, savepath, plot_file_extensions, normed, opening_bar_plot, num_of_bins, edgecolor, edge_linewidth, meteo_loc, add_info, transparent, alpha, label_color, font_weight, special_textfont_size, save_plotting_data_to_plain_text_AS_WELL, save_plotting_data_to_plain_text_ONLY)
   6957 
   6958     # * Finally, either show or save the current plot
-> 6959     aux_plot.show_or_save_plot(path=savepath,
   6960                                basename=titlestr,
   6961                                transparent=transparent,

~/Dokumente/Allgemeines_material/Sonstiges/Programming/Python/Scripts/General/Plotting/auxiliary_plotting_functions.py in show_or_save_plot(fig, path, basename, file_extensions, dpi, legend, bbox_inches, transparent, legend_is_inside, remove_surrounding_whitespace, use_plotly, plotly_show_renderer, plotly_default_template, plotly_axlabel_size, plotly_title_label_size, plotly_font_family, plotly_font_color, linux_dir_sep, save_plotting_data_to_plain_text_AS_WELL, save_plotting_data_to_plain_text_ONLY)
   3549 
   3550         # Carry out the saving procedure building up on the save-fig function
-> 3551         save_figs(path,
   3552                   fig=fig,
   3553                   file_extensions=file_extensions,

~/Dokumente/Allgemeines_material/Sonstiges/Programming/Python/Scripts/General/Plotting/auxiliary_plotting_functions.py in save_figs(filename, fig, file_extensions, dpi, standard_dpi, legend, bbox_inches, transparent, remove_surrounding_whitespace, legend_is_inside, linux_dir_sep, use_plotly, plotly_default_template, plotly_axlabel_size, plotly_title_label_size, plotly_font_family, plotly_font_color, save_plotting_data_to_plain_text_AS_WELL, save_plotting_data_to_plain_text_ONLY)
   2395                     if not remove_surrounding_whitespace:
   2396                         # Call savefig-method pertaining to a pyplot-fig-object with its provided kwargs
-> 2397                         fig.savefig(filename + t,
   2398                                     dpi=dpi,
   2399                                     bbox_inches=bbox_inches,

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/figure.py in savefig(self, fname, transparent, **kwargs)
   2309                 patch.set_edgecolor('none')
   2310 
-> 2311         self.canvas.print_figure(fname, **kwargs)
   2312 
   2313         if transparent:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2191                            else suppress())
   2192                     with ctx:
-> 2193                         self.figure.draw(renderer)
   2194 
   2195                     bbox_inches = self.figure.get_tightbbox(

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40 
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/figure.py in draw(self, renderer)
   1861 
   1862             self.patch.draw(renderer)
-> 1863             mimage._draw_list_compositing_images(
   1864                 renderer, self, artists, self.suppressComposite)
   1865 

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    409                          else deprecation_addendum,
    410                 **kwargs)
--> 411         return func(*inner_args, **inner_kwargs)
    412 
    413     return wrapper

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    409                          else deprecation_addendum,
    410                 **kwargs)
--> 411         return func(*inner_args, **inner_kwargs)
    412 
    413     return wrapper

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/projections/polar.py in draw(self, renderer, *args, **kwargs)
    993             self.yaxis.set_clip_path(self.patch)
    994 
--> 995         Axes.draw(self, renderer, *args, **kwargs)
    996 
    997     def _gen_axes_patch(self):

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40 
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs)
    409                          else deprecation_addendum,
    410                 **kwargs)
--> 411         return func(*inner_args, **inner_kwargs)
    412 
    413     return wrapper

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe)
   2746             renderer.stop_rasterizing()
   2747 
-> 2748         mimage._draw_list_compositing_images(renderer, self, artists)
   2749 
   2750         renderer.close_group('axes')

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130     if not_composite or not has_images:
    131         for a in artists:
--> 132             a.draw(renderer)
    133     else:
    134         # Composite any adjacent images together

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40 
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer, *args, **kwargs)
   1167 
   1168         for tick in ticks_to_draw:
-> 1169             tick.draw(renderer)
   1170 
   1171         # scale up the axis label box to also find the neighbors, not

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40 
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/axis.py in draw(self, renderer)
    289         for artist in [self.gridline, self.tick1line, self.tick2line,
    290                        self.label1, self.label2]:
--> 291             artist.draw(renderer)
    292         renderer.close_group(self.__name__)
    293         self.stale = False

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     39                 renderer.start_filter()
     40 
---> 41             return draw(artist, renderer, *args, **kwargs)
     42         finally:
     43             if artist.get_agg_filter() is not None:

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/text.py in draw(self, renderer)
    695 
    696             gc = renderer.new_gc()
--> 697             gc.set_foreground(textobj.get_color())
    698             gc.set_alpha(textobj.get_alpha())
    699             gc.set_url(textobj._url)

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backend_bases.py in set_foreground(self, fg, isRGBA)
    953             self._rgb = fg
    954         else:
--> 955             self._rgb = colors.to_rgba(fg)
    956 
    957     def set_joinstyle(self, js):

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/colors.py in to_rgba(c, alpha)
    187         rgba = None
    188     if rgba is None:  # Suppress exception chaining of cache lookup failure.
--> 189         rgba = _to_rgba_no_colorcycle(c, alpha)
    190         try:
    191             _colors_full_map.cache[c, alpha] = rgba

/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/colors.py in _to_rgba_no_colorcycle(c, alpha)
    261     # tuple color.
    262     if not np.iterable(c):
--> 263         raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
    264     if len(c) not in [3, 4]:
    265         raise ValueError("RGBA sequence should have length 3 or 4")

ValueError: Invalid RGBA argument: None

Solution

  • As @JohanC pointed out, passing None to the color-kwarg of set_yticklabels led to the quite counterintuitive error message of my question.

    Avoiding None did the trick, now it works.

    To play it safe, I wrote a function which avoids passing None to any of the key-value arguments ("kwargs") involved, which can be found below.

    It is to automize everything and avoid too much coding in every plotting function where colored labels are desired. The function includes the cases of labeling axes, titles and colorbars.

    def set_ticklabels_or_title_of_current_ax(ax_or_cbar=None,
                                              labels=None,
                                              label_type=None,
                                              ax_title_pos=(0.5, 0.985),
                                              label_color=None,
                                              font_weight=None,
                                              special_textfont_size=None):
        """Function docs:\n
        Avoid too much code in the main plotting function.
    
        # NOTE on scope: not all of them are accepted as None since they could override the initial value
        # Docs: https://stackoverflow.com/questions/63245556/valueerror-invalid-rgba-argument-none-when-plotting-via-matplotlib-ax-bar?noredirect=1#comment111839077_63245556
        """
    
        # Check tick type passed
        if label_type is None:
            raise Exception("The user must pass a tick type (x or y).")
    
        # Check whether an axis has been passed by the user
        if ax_or_cbar is None and label_type.lower() in [
                "x", "x-axis", "y", "y-axis", "title"
        ]:
            ax_or_cbar = plt.gca()
    
        # In the case that all variables were passed as None, return the axis unchanged
    # NOTE: the function used here "check_if_any_or_all_of_the_passed_vars_are_None()" is a custom function, not included here: can be ignored
        if tools.check_if_any_or_all_of_the_passed_vars_are_None(
            [label_color, font_weight, special_textfont_size],
                raise_error_within_function=False,
                any_is_None=False):
            # Return the ax unchanged
            return ax_or_cbar
    
        # * Check if labels were passed * #
        if labels is None:
            if label_type.lower() in ["x", "x-axis"]:
                labels = [elem.get_text() for elem in list(ax_or_cbar.get_xticklabels())]
            elif label_type.lower() in ["y", "y-axis"]:
                labels = [elem.get_text() for elem in list(ax_or_cbar.get_yticklabels())]
            elif label_type.lower() in ["title", "cbar title"]:
                raise Exception("No title label has been passed.")
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                labels = list(ax_or_cbar.ax.get_yticklabels())
    
        # * Check which variables were passed as None * #
        if label_color is not None and font_weight is not None and special_textfont_size is not None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels,
                                           color=label_color,
                                           weight=font_weight,
                                           fontsize=special_textfont_size)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels,
                                           color=label_color,
                                           weight=font_weight,
                                           fontsize=special_textfont_size)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     color=label_color,
                                     weight=font_weight,
                                     fontsize=special_textfont_size)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels,
                               color=label_color,
                               weight=font_weight,
                               fontsize=special_textfont_size)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels,
                                              color=label_color,
                                              weight=font_weight,
                                              fontsize=special_textfont_size)
    
        elif label_color is not None and font_weight is not None and special_textfont_size is None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels,
                                           color=label_color,
                                           weight=font_weight)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels,
                                           color=label_color,
                                           weight=font_weight)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     color=label_color,
                                     weight=font_weight)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels, color=label_color, weight=font_weight)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels,
                                              color=label_color,
                                              weight=font_weight)
    
        elif label_color is not None and font_weight is None and special_textfont_size is not None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels,
                                           color=label_color,
                                           fontsize=special_textfont_size)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels,
                                           color=label_color,
                                           fontsize=special_textfont_size)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     color=label_color,
                                     fontsize=special_textfont_size)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels,
                               color=label_color,
                               fontsize=special_textfont_size)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels,
                                              color=label_color,
                                              fontsize=special_textfont_size)
    
        elif label_color is None and font_weight is not None and special_textfont_size is not None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels,
                                           weight=font_weight,
                                           fontsize=special_textfont_size)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels,
                                           weight=font_weight,
                                           fontsize=special_textfont_size)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     weight=font_weight,
                                     fontsize=special_textfont_size)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels,
                               weight=font_weight,
                               fontsize=special_textfont_size)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels,
                                              weight=font_weight,
                                              fontsize=special_textfont_size)
    
        elif label_color is None and font_weight is None and special_textfont_size is not None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels, fontsize=special_textfont_size)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels, fontsize=special_textfont_size)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     fontsize=special_textfont_size)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels, fontsize=special_textfont_size)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels,
                                              fontsize=special_textfont_size)
    
        elif label_color is None and font_weight is not None and special_textfont_size is None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels, weight=font_weight)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels, weight=font_weight)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     weight=font_weight)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels, weight=font_weight)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels, weight=font_weight)
    
        elif label_color is not None and font_weight is None and special_textfont_size is None:
            if label_type.lower() in ["x", "x-axis"]:
                ax_or_cbar.set_xticklabels(labels, color=label_color)
            elif label_type.lower() in ["y", "y-axis"]:
                ax_or_cbar.set_yticklabels(labels, color=label_color)
            elif label_type.lower() == "title":
                ax_or_cbar.set_title(labels,
                                     position=ax_title_pos,
                                     color=label_color)
            elif label_type.lower() == "cbar title":
                ax_or_cbar.set_label(labels, color=label_color)
            elif label_type.lower() in [
                    "cbar yticklabels", "cbar yticks", "cbar y"
            ]:
                ax_or_cbar.ax.set_yticklabels(labels, color=label_color)
    
        # Return updated axis to user
        return ax_or_cbar