Andreas L.
Andreas L.

Reputation: 4531

matplotlib throws error with figure.canvas.draw() and figure.savefig(): "ValueError: Expected 2-dimensional array, got 1"

I wanted to plot a figure where I need to compare legend and figure height. The desired output would look like this:

Multiple subplot with confidence interval bands around each line

Previously it worked like a charm, now I cannot draw the figure stored in the variable fig.

To this end, it was necessary to draw the figure canvas previously to the real plot since this way the real final figure extensions will be obtained.

fig.canvas.draw()

# Get the extensions/dimensions of the current axis and legend
ax_height = ax.get_window_extent().height
ax_width = ax.get_window_extent().width
leg_height = legend.get_window_extent().height
leg_width = legend.get_window_extent().width

At least previously, the line fig.canvas.draw() worked flawlessly, but this time the following error was thrown (entire traceback included):

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/andylu/Dokumente/Allgemeines_material/Sonstiges/Programming/Python/Scripts/General/Plotting/auxiliary_plotting_functions.py", line 1293, in compare_legend_figure_height
    fig.canvas.draw()
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py", line 407, in draw
    self.figure.draw(self.renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/figure.py", line 1863, in draw
    mimage._draw_list_compositing_images(
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py", line 132, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
    return func(*inner_args, **inner_kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/axes/_base.py", line 2748, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py", line 132, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/collections.py", line 931, in draw
    Collection.draw(self, renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/collections.py", line 406, in draw
    renderer.draw_path_collection(
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py", line 172, in draw_path_collection
    return self._renderer.draw_path_collection(
ValueError: Expected 2-dimensional array, got 1

In the debugging console, additional inside into the figure variable could be obtained, but didn't help me any further solving the problem:

fig
<Figure size 1000x750 with 6 Axes>
special variables
function variables
artists:[]
axes:[<AxesSubplot:title={...19-pos4'}>, <AxesSubplot:title={...18-pos1'}>, <AxesSubplot:title={...18-pos1'}>, <AxesSubplot:title={...19-pos4'}>, <AxesSubplot:>, <AxesSubplot:>]
bbox:<matplotlib.transforms.TransformedBbox object at 0x7fc780543310>
bbox_inches:Bbox([[0.0, 0.0], [10.0, 7.5]])
callbacks:<matplotlib.cbook.CallbackRegistry object at 0x7fc7795fa3a0>
canvas:<matplotlib.backends.backend_agg.FigureCanvasAgg object at 0x7fc7811c1850>
clipbox:None
dpi:100.0
dpi_scale_trans:<matplotlib.transforms.Affine2D object at 0x7fc780543640>
eventson:False

figure:None
frameon:True
images:[]
legends:[]
lines:[]
mouseover:False
number:1
patch:<matplotlib.patches.Rectangle object at 0x7fc780554eb0>
patches:[]
stale:False
sticky_edges:_XYPair(x=[], y=[])
subplotpars:<matplotlib.figure.SubplotParams object at 0x7fc7795fa040>
suppressComposite:None
texts:[]
transFigure:<matplotlib.transforms.BboxTransformTo object at 0x7fc7805433a0>
zorder:0
_add_axes_internal:<bound method Figure._add_axes_internal of <Figure size 1000x750 with 6 Axes>>

_agg_filter:None
_align_xlabel_grp:<matplotlib.cbook.Grouper object at 0x7fc7795fac10>
_align_ylabel_grp:<matplotlib.cbook.Grouper object at 0x7fc7795faf10>
_alpha:None
_animated:False
_axobservers:<matplotlib.cbook.CallbackRegistry object at 0x7fc7811c1b50>
_axstack:<matplotlib.figure._AxesStack object at 0x7fc7795fae20>
_cachedRenderer:<matplotlib.backends.backend_agg.RendererAgg object at 0x7fc780d235e0>
_clipon:True
_clippath:None
_constrained:False
_constrained_layout_pads:{'h_pad': 0.04167, 'hspace': 0.02, 'w_pad': 0.04167, 'wspace': 0.02}
_contains:None
_default_contains:<bound method Artist._default_contains of <Figure size 1000x750 with 6 Axes>>
_dpi:100.0
_gci:<bound method Figure._gci of <Figure size 1000x750 with 6 Axes>>
_label:''
_layoutbox:None
_make_key:<bound method Figure._make_key of <Figure size 1000x750 with 6 Axes>>
_mouseover:False
_normalize_grid_string:<function Figure._normalize_grid_string at 0x7fc7d437c3a0>
_oid:0
_path_effects:[]
_picker:None
_process_projection_requirements:<bound method Figure._process_projection_requirements of <Figure size 1000x750 with 6 Axes>>
_propobservers:{}
_rasterized:None
_remove_method:None
_repr_html_:<bound method Figure._repr_html_ of <Figure size 1000x750 with 6 Axes>>
_set_artist_props:<bound method Figure._set_artist_props of <Figure size 1000x750 with 6 Axes>>
_set_dpi:<bound method Figure._set_dpi of <Figure size 1000x750 with 6 Axes>>
_set_gc_clip:<bound method Artist._set_gc_clip of <Figure size 1000x750 with 6 Axes>>
...

With good hope, I tried to simply avoid this error like so:

# NOTE on scope of drawing the figure canvas:
# Crucial in order to get real legend extent afterwards
try:
    fig.canvas.draw()
except Exception as e:
    tools.except_print(f"The exception thrown opon executing fig.canvas.draw was:\n{e}\nExecute the rest of this function nevertheless.")

Nonetheless, upon saving the figure via figure.savefig() a similar error occurs, so I cannot escape solving this problem:

fig.savefig(filename,
            dpi=dpi,
            bbox_inches=bbox_inches,
            transparent=transparent)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/figure.py", line 2311, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backend_bases.py", line 2210, in print_figure
    result = print_method(
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backend_bases.py", line 1639, in wrapper
    return func(*args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py", line 509, in print_png
    FigureCanvasAgg.draw(self)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py", line 407, in draw
    self.figure.draw(self.renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/figure.py", line 1863, in draw
    mimage._draw_list_compositing_images(
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py", line 132, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
    return func(*inner_args, **inner_kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/axes/_base.py", line 2748, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/image.py", line 132, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/collections.py", line 931, in draw
    Collection.draw(self, renderer)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/collections.py", line 406, in draw
    renderer.draw_path_collection(
  File "/home/linuxbrew/.linuxbrew/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py", line 172, in draw_path_collection
    return self._renderer.draw_path_collection(
ValueError: Expected 2-dimensional array, got 1

The plotting function on the innermost level which I utilize in this context is:

def plot_point_estimator_with_CI(
    df_plot_list=None,
    colname=None,
    estimator="mean",
    ci_color=[1, 0, 0, 0.15],
    CI=0.95,
    hor_line=None,
    strftime_str=None,
    outer_index=None,
    all_outer_indexes_subplot=False,
    groupby_freq=None,
    ax_title_pos=None,
    savepath=None,
    filtered_gps=None):

# Check for existence of the savepath
if savepath is not None and not os.path.exists(savepath):
    os.makedirs(savepath)

# * Create a dummy date for later combination with pure time-values (i.e. HH:MM:SS)
# NOTE on scope: this is needed for being able to be plotted on a matplotlib.ax since datetime.time()-arrays will throw errors
# NOTE on implementation: use an extra-weird date in order to make clear that this is certainly not a real date
dummy_date = datetime.date(1000, 10, 10)

if not all_outer_indexes_subplot:
    loop2_list = filtered_gps
else:
    loop2_list = outer_index

## ** SUBPLOTS
"""Prepare the figure dimensions:
# NOTE: the figure size should be decided according to the dimensions of the grid
# Syntax from the documentation under: https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.figure.html
# (float, float), optional, default: None
# width, height in inches. If not provided, defaults to rcParams["figure.figsize"] = [6.4, 4.8]
# -> the relation is 4:3, which will be maintained throughout this
# CAUTION: even when cols=rows, the figure needs to be wider than high."""
if cols >= rows:
    width = 10
    height = width * (3 / 4)
else:  # cols < rows:
    height = 10
    width = height * (3 / 4)

# * Instantiate figure
fig, axs = plt.subplots(rows, cols, figsize=(width, height))

# * Turn off possible extant subplot axes
dim_subplot = rows * cols
if len(loop2_list) < dim_subplot and len(loop2_list) > 1:
    subplot_ax_overshoot = True
    # Could happen when uneven number of subplots, such as "5", is passed, and the rectangular grid
    # has extant subplot axis, e.g. in the case of a 2*3 = 6 grid
    for i in range(len(loop2_list), dim_subplot):
        axs.ravel()[i].axis('off')
else:
    subplot_ax_overshoot = False

# Adapt the fontsize of the axis title strings according to the column number of the subplot
if cols < 3:  # should be the "maximum max_cols_per_row"
    ax_title_fontsize = 9
    if not particular_ax_label_fontsize:
        particular_ax_label_fontsize = 9
    legend_font_size = 8.25
    # NOTE on linewidths: the more columns/subplot graphic number, the finer the lines should be
    if cols < 2:
        linewidth = None  # standard seems ok
    else:
        linewidth = 1.25
else:
    ax_title_fontsize = 7
    if not particular_ax_label_fontsize:
        particular_ax_label_fontsize = 8
    linewidth = 1
    legend_font_size = 7

# NOTE: this is paramount in order to display the CI-band in the end as a part of the legend, even though the last subplot didn't contain it
add_handles_labels = None

# * Loop 2 - Create the output graphics
for i, elem in enumerate(loop2_list):
    if groupby_freq is not None:
        if groupby_freq.lower() == "y":
            # Extract the grouped-by unit of the current pd.Timestamp
            coord_kw = int(elem[0].strftime(
                strftime_str))  # could be year, month, ...
            # Assign a time unit name to the title string of the current sub-plot axis
            current_ax_title = coord_kw
        elif groupby_freq.lower() == "m":
            # Extract the grouped-by unit of the current pd.Timestamp
            coord_kw = int(elem[0].strftime(
                strftime_str))  # could be year, month, ...
            # Assign a time unit name to the title string of the current sub-plot axis
            current_ax_title = calendar.month_name[coord_kw]
        else:
            string = """\nERROR: if the time-groupingby-frequency is neither "Y" nor "M",
it hasn't been implemented yet. Won't execute the plotting code (for this iteration).\nCurrent groupby_freq: '{}'""".format(
                    groupby_freq)
                tools.except_print(string)
                break
        elif all_outer_indexes_subplot:
            # The current outer index / directory / location name of the given meteo or gas measuring data
            current_ax_title = elem
            coord_kw = elem

    # Define a temporary variable for the current sub-set dataframe
    sub_df = df_plot_list[i]
    if type(sub_df) == pd.DataFrame:
        sub_df.dropna(axis=1, how="all", inplace=True)

    # Assign the row and column to the current month accordingly - from dictionary
    # NOTE: it should be ordered from January upper-left to December bottom-right
    row, col = row_col_coords[coord_kw]
    # Pass the info to the axis-dummy variable for the following plotting commands
    if len(loop2_list) > 1:
        # Discerning is paramount for avoiding "IndexError: too many indices for array"
        if rows > 1 and cols > 1:
            ax = axs[row, col]
        elif rows > 1:  # cols = 1
            ax = axs[row]
        else:  # rows = 1
            ax = axs[col]
    # Case of single plot: rows = cols = 1
    else:
        # Avoid error: "is not subscriptable" when it's not a real subplot but only 1 plot
        ax = axs

    # * Check for datetime.time - format of index (overlay of days or similar)
    if isinstance(sub_df.index[0], datetime.time):
        # NOTE on implementation:
        # - Replaced df_dummy.index.time with [datetime.datetime.combine(dummy_date, t) for t in df_dummy.index.time] since datetime.time(22, 35)-objects can't be processed well with ax.plot()
        # Docs: https://stackoverflow.com/questions/24757178/how-plot-datetime-time-in-matplotlib
        sub_df.index = [
            datetime.datetime.combine(dummy_date, t) for t in sub_df.index
        ]
        update_ticks_to_full_fledged_datetime = True
    else:
        update_ticks_to_full_fledged_datetime = False

    # * Create plot on the designated axis based on the current sub-df
    if type(sub_df) == pd.DataFrame:  # contains CI-bands
        # Caution: Colnames are uppercase conventionally
        # NOTE: optionally another kwarg could be added: label=str_man.uppercase(estimator)
        line1, = ax.plot(
            sub_df.loc[:, str_man.uppercase(estimator)],
            label=str_man.uppercase(estimator),
            linewidth=linewidth)  # matplotlib.lines.Line2D object
        # Confidence interval of fit (higher resolution due to generated fit-vals with higher density)
        # NOTE: the comma "," needs to be left out, otherwise: "TypeError: cannot unpack non-iterable PolyCollection object"
        line2_label = "{}% CI".format(str(round(CI * 100)))
        line2 = ax.fill_between(sub_df.index,
                                sub_df.loc[:, "Lower_bound"],
                                sub_df.loc[:, "Upper_bound"],
                                color=ci_color,
                                edgecolor="",
                                label=line2_label)
        if not hor_line or i == len(loop2_list) - 1:
            # NOTE on handles: could also be without "handles=.."
            legend = aux_plot.set_legend_with_sorted_labels(
                fig=fig,
                ax=ax,
                handles=[line1, line2],
                return_legend_n_its_position=True)[0]
            if i == len(loop2_list) - 1:
                # Set to None, as the CI-band (line2) has just been plotted in the last subplot axis
                add_handles_labels = None
        else:
            # NOTE: this is paramount in order to display the CI-band in the end as a part of the legend, even though the last subplot didn't contain it
            if not add_handles_labels:
                add_handles_labels = [line2, line2_label]

    # Contains only the aggregated values of the statistical summary estimator, i.e. type(df) == pd.Series
    else:
        # pd.Series doesn't need any other kwargs to be passed
        line1, = ax.plot(sub_df,
                         label=str_man.uppercase(estimator),
                         linewidth=linewidth)
        if not hor_line or i == len(loop2_list) - 1:
            # NOTE on handles: could also be without "handles=.."
            legend = aux_plot.set_legend_with_sorted_labels(
                fig=fig,
                ax=ax,
                handles=[line1],
                return_legend_n_its_position=True)[0]

    # * SET X-TICKS and -LABELS
    # Create a grid for the times on the x-axis
    times_grid, time_ax_vals, ax_tick_labels, sub_ordinated_unit = dt_man.time_range_grid_and_vals(
        step=step, cols=cols)
    # Delete superfluous variables
    del time_ax_vals

    # * Set the ticks and their associated labels
    if update_ticks_to_full_fledged_datetime:
        # NOTE on implementation: need to adapted with the dummy-date to the current axis
        # -> The date doesn't matter since the ticks will be labeled separately with the "HH:MM:SS" - strings (pure times)
        ax.set_xticks(
            [datetime.datetime.combine(dummy_date, t) for t in times_grid])
    else:
        ax.set_xticks(times_grid)
    # Tick labels remain untouched by the ticks-setting above
    ax.set_xticklabels(ax_tick_labels,
                       fontsize=particular_ax_label_fontsize)

    # * SET Y-TICKS and -LABELS
    y_tick_labels = [str(yt) for yt in list(ax.get_yticks())]
    if decimal_formatter:
        y_tick_labels = [
            decimal_formatter % Decimal(float(t)) for t in y_tick_labels
        ]
    y_tick_labels = tools.round_long_floats_with_many_zeros(
        number_list=y_tick_labels,
        decimal_sep=".",
        undesired_char="0",
        limit_consec_undesired_chars=1)
    ax.set_yticklabels(y_tick_labels,
                       fontsize=particular_ax_label_fontsize)
    ax.grid(which='both', alpha=1)

    # Inserts horizontal line into plot adapted by its value in comparison to the data's values
    # NOTE: only the last element, which is true in case of a subplot or a single plot
    if hor_line is not None:
        remove_legend_from_current_axis = i != len(loop2_list) - 1
        # Loop over all horizontal lines provided
        for hor in hor_line:
            if hor[0].lower() in colname.lower(
            ):  # hor[0] contains the variable name (or vice versa)
                # hor[1] contains tuples (triples)
                for info, val, linestyle in hor[1]:
                    leg_label = str_man.uppercase(info)

                    # Caution 1: In order to obtain pd.Datetime-vals from matplotlib's ax.get_xlim() -> conversion necessary
                    # Caution 2: Set vertical=False since horizontal lines are desired
                    # NOTE on previous implementation of other_ax_vals:
                    # i) time_ax_vals
                    # ii) [datetime.datetime.combine(dummy_date, t) for t in time_ax_vals]
                    ax, legend = aux_plot.add_hor_vert_line_n_legend(
                        ax=ax,
                        compare_ax_vals=ax.get_ylim(),
                        add_handles_labels=add_handles_labels,
                        other_ax_vals=ax.get_xlim(),
                        val=val,
                        alpha=1,
                        linewidth=linewidth,
                        leg_label=leg_label,
                        vertical=False,
                        linestyle=linestyle,
                        remove_legend_from_current_axis=
                        remove_legend_from_current_axis)[:2]

    # * X-axis label
    # NOTE: otherwise, it appears always the string "time"
    ax.set_xlabel("")

    # * Title of current axis
    # Set title with the calendar month above every subplot
    # [x_coord, y_coord] -> slightly above the top and centered
    ax.set_title(current_ax_title,
                 position=ax_title_pos,
                 fontsize=ax_title_fontsize)

# * AFTER 2nd LOOP
# 0.0) Assign global axis label shifts
global_X_ax_label_shift = global_X_ax_label_shift_dict[rows]
global_Y_ax_label_shift = global_Y_ax_label_shift_dict[rows]

# 0.1) Create final and global legend object
if hor_line is not None and dim_subplot > 1:
    # Obtain legend handles and labels from passed axis object
    handles, labels = ax.get_legend_handles_labels()
    # NOTE: sometimes it is necessary to pass former handles and labels from an already plotted axis
    # TIPP: the problem is that when the current axis is accessed for retrieving the handles and labels, these added
    # handles and labels won't be in there since it was nothing plotted in the current axis, but in a former one
    if add_handles_labels:
        # Extract the additional handles and labels from another/former axis
        add_handles, add_labels = add_handles_labels
        # Add these accordingly to the current axis' handles..
        if type(add_handles) != list:
            handles += [add_handles]
        else:
            handles += add_handles
        # .. and labels
        if type(add_labels) != list:
            labels += [add_labels]
        else:
            labels += add_labels

    # CAUTION: Prevent the legend's appearance in the last selected axis of the subplot
    ax.get_legend().remove()

    legend_font_size = 8.25
    # NOTE on the nomenclature of this legend:
    # - bbox_to_anchor = (x, y), alternatively, if a size needs to be determined: (x, y, width, height)
    # - loc == 9 -> upper center, (0, 0) seems to stand for the lower/upper left corner of the legend box
    # - ncol : The number of columns that the legend has. Default is 1.
    # - mode: If mode is set to "expand" the legend will be horizontally expanded to fill the axes area
    # (or bbox_to_anchor if defines the legend's size, which is the case if a 4-tuple was passed to bbox_to_anchor like (x, y, width, height))
    if not subplot_ax_overshoot:
        aux_plot.set_legend_with_sorted_labels(fig=fig,
                                               handles=handles,
                                               labels=labels,
                                               loc="lower center",
                                               ncol=5,
                                               fontsize=legend_font_size)

        global_X_ax_label_shift += 0.01  # shift higher to make room for the footnote-legend
    # * OTHERWISE, use the last free axis to plot the legend
    else:
        legend_ax = axs[rows - 1, cols - 1]
        # Set sorted legend on specific axis
        aux_plot.set_legend_with_sorted_labels(ax=legend_ax,
                                               handles=handles,
                                               labels=labels,
                                               loc="upper left",
                                               ncol=1,
                                               fontsize=legend_font_size)

# 0.2) Set global X- and Y-axis labels
xaxstr = "Time ({})".format(sub_ordinated_unit)
if yaxstr is None:
    yaxstr = str_man.uppercase(estimator)
if len(df_plot_list) == 1:
    ax.set_xlabel(xaxstr, fontsize=ax_label_font_size)
    ax.set_ylabel(yaxstr, fontsize=ax_label_font_size)
else:

    fig.text(0.5,
             global_X_ax_label_shift,
             xaxstr,
             ha='center',
             rotation='horizontal',
             fontsize=ax_label_font_size)  # general x-axis label
    fig.text(global_Y_ax_label_shift,
             0.5,
             yaxstr,
             va='center',
             rotation='vertical',
             fontsize=ax_label_font_size)  # general y-axis label

# 1) Generate a unique title string
# ...

# 2) Set title
if len(df_plot_list) == 1:
    # NOTE: overlapping title strings with the standard .set_title()-function can be fought via the y-kwarg
    ax.set_title(titlestr,
                 y=1.0 + add_to_ycoord / 2,
                 fontsize=title_font_size,
                 weight="bold")
    fig.tight_layout()
else:
    # Now, alter the y-coord of the superior title as a function of the lines the suptitlestring comprises
    y_coord_suptitle = y_coord_suptitle_dict[rows]
    y_coord_suptitle += add_to_ycoord

    # Finally, set the suptitle
    plt.suptitle(titlestr,
                 x=0.5,
                 y=y_coord_suptitle,
                 fontsize=title_font_size,
                 weight="bold")
    # NOTE: Tight layout often produces nice results, but requires the title to be spaced accordingly
    fig.tight_layout()

    if global_Y_ax_label_shift:
        # ...

        fig.subplots_adjust(top=y_coord_suptitle + sub_top_shift -
                            add_to_ycoord,
                            bottom=global_X_ax_label_shift + 0.04,
                            left=global_Y_ax_label_shift + add_left_shift)
    else:
        fig.subplots_adjust(top=y_coord_suptitle + sub_top_shift -
                            add_to_ycoord,
                            bottom=global_X_ax_label_shift + 0.1)

# NOTE: Tight layout often produces nice results but requires the title to be spaced accordingly
# CAUTION: as far as this function (windrose-subplotting) is concerned, it hasn't been necessary (status: 17-08-2019)
if len(df_plot_list) == 1:
    pass
else:  # in case of subplots
    # * FINALLY, set legend to None due to the subplots character
    legend = None

## ** Finally, either show or save the current plot/figure **
aux_plot.show_or_save_plot(fig=fig,
                           path=savepath,
                           basename=titlestr,
                           file_extensions=['.png', '.pdf'],
                           legend=legend)

Upvotes: 2

Views: 2637

Answers (1)

Bethan
Bethan

Reputation: 66

I came across the same error message recently, same as you it popped up when I used fig.savefig. I think perhaps the issue occurs earlier in the code, rather than within the savefig function itself. For my plots, I traced the issue back to a now-deprecated argument in plt.scatter() - I had used edgecolors='', which should now be edgecolors=None, and it caused the same error message of 'ValueError: Expected 2-dimensional array, got 1' when I tried to save the figure.

For example, this code block raises the error, but if the edgecolors argument is changed to =None, the code runs without issue.

fig = plt.figure()
a = [1,2,3,4]
plt.scatter(a,a,edgecolors='')
fig.savefig('test.png')

I wonder if you used a similar deprecated argument when you were plotting? It was easiest to trace back by doing a little test plot in ipython, as the deprecation warning was properly raised in a way it was not when simply executing .py files.

Edit: Just spotted you included your code at the bottom - I believe you do have the same issue, you've used edgecolors="" in your call to fill_between

Upvotes: 3

Related Questions