Pyplot legend index error: tuple index out of range - python

After defining ax1=fig1.add_subplot(111) and plotting 8 data series with associated label values, I used the following line of code to add a legend.
ax1.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))
I have used this method many times before without a problem, but on this occasion it produces an error saying IndexError: tuple index out of range
Traceback (most recent call last):
File "interface_tension_adhesion_plotter.py", line 45, in <module>
ax1.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/matplotlib/axes/_axes.py", line 564, in legend
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/matplotlib/legend.py", line 386, in __init__
self._init_legend_box(handles, labels, markerfirst)
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/matplotlib/legend.py", line 655, in _init_legend_box
fontsize, handlebox))
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/matplotlib/legend_handler.py", line 119, in legend_artist
fontsize, handlebox.get_transform())
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/matplotlib/legend_handler.py", line 476, in create_artists
self.update_prop(coll, barlinecols[0], legend)
IndexError: tuple index out of range
I have no idea why this is happening and would really appreciate suggestions.

1. If data is intact and arrays are not empty, this code works perfectly.
fig = plt.gcf()
ax=fig.add_subplot(111)
for i in range(8):
x = np.arange(10)
y = i + random.rand(10)
yerr = .1*y
l = .1*i
ax.errorbar(x,y,yerr=yerr,label="adhsion={:02.1f}".format(l))
ax.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))
2. I had same error when I applied a filter to my data and got empty arrays. This could be reproduced as follows:
fig = plt.gcf()
ax=fig.add_subplot(111)
for i in range(8):
x = np.arange(10)
y = i + random.rand(10)
yerr = .1*y
l = .1*i
if i == 7:
ind = np.isnan(y)
y = y[ind]
x = x[ind]
yerr = yerr[ind]
ax.errorbar(x,y,yerr=yerr,label="adhsion={:02.1f}".format(l))
ax.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))
This code gives identical Traceback as in the question. Empty array for errors results in wrong handles for errorbars.
Workaround mentioned by #crevell:
handles, labels = ax.get_legend_handles_labels()
handles = [h[0] for h in handles]
ax.legend(handles, labels,loc='center left', bbox_to_anchor=(1.0, 0.5))
It works, but legend appears without errorbar lines.
So one should check the data supplied to the matplotlib errorbar function.

Related

Nested bar plots with three y axis using seaborn package

Using the python seaborn package I was trying to plot the nested bar graphs with three different y-axes as shown in the below figure:
And the code that I have used is :
import matplotlib.pyplot as plt
from matplotlib import rc
import numpy as np
import seaborn as sns
#plt.style.use(['science'])
rc('font', **{'family': 'serif', 'serif': ['Computer Modern']})
rc('text', usetex=True)
HV = [388, 438]
YS = [1070, 1200]
UTS = [1150, 1400]
Z = [15, 12.5]
x = [1, 2]
fig, ax1 = plt.subplots(figsize=(5, 5.5))
colors=sns.color_palette("rocket",4)
ax1 = sns.barplot(x[0],YS[0],color="blue")
ax1 = sns.barplot(x[0],color="blue")
ax1 = sns.barplot(x[1],YS[1],color="blue")
ax1 = sns.barplot(x[1],UTS[1],color="blue")
ax2 = ax1.twinx()
ax2 = sns.barplot(x[0], HV[0],color="green")
ax2 = sns.barplot(x[1], HV[1],color="green")
ax3 = ax1.twinx()
ax3 = sns.barplot(x[0],Z[0],color="red")
ax3 = sns.barplot(x[1],Z[1],color="red")
#ax3.spines['right'].set_position(('outward',60))
ax3.spines['right'].set_position(('axes',1.15))
ax1.set_ylabel("First",color="blue")
ax2.set_ylabel("Second",color="green")
ax3.set_ylabel("Third",color="red")
ax1.tick_params(axis='y',colors="blue")
ax2.tick_params(axis='y',colors="green")
ax3.tick_params(axis='y',colors="red")
ax2.spines['right'].set_color("green")
ax3.spines['right'].set_color("red")
ax3.spines['left'].set_color("blue")
plt.show()
And I'm getting the following error:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/seaborn/utils.py", line 531, in categorical_order
order = values.cat.categories
AttributeError: 'int' object has no attribute 'cat'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/seaborn/utils.py", line 534, in categorical_order
order = values.unique()
AttributeError: 'int' object has no attribute 'unique'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/sspenkulinti/these/thesis_E185_fatigue/test_matrix/E185_properties_AB_HT.py", line 21, in <module>
ax1 = sns.barplot(x[0],YS[0],color="blue")
File "/usr/lib/python3/dist-packages/seaborn/categorical.py", line 3147, in barplot
plotter = _BarPlotter(x, y, hue, data, order, hue_order,
File "/usr/lib/python3/dist-packages/seaborn/categorical.py", line 1614, in __init__
self.establish_variables(x, y, hue, data, orient,
File "/usr/lib/python3/dist-packages/seaborn/categorical.py", line 200, in establish_variables
group_names = categorical_order(groups, order)
File "/usr/lib/python3/dist-packages/seaborn/utils.py", line 536, in categorical_order
order = pd.unique(values)
File "/usr/lib/python3/dist-packages/pandas/core/algorithms.py", line 395, in unique
values = _ensure_arraylike(values)
File "/usr/lib/python3/dist-packages/pandas/core/algorithms.py", line 204, in _ensure_arraylike
inferred = lib.infer_dtype(values, skipna=False)
File "pandas/_libs/lib.pyx", line 1251, in pandas._libs.lib.infer_dtype
TypeError: 'int' object is not iterable
The error is because you can't call sns.barplot with a single number as first parameter. The x-values need to be a list.
To get want you want using seaborn, the data needs to be presented as if it comes from a dataframe. hue_order is needed to preserve enough space for each of the bars, even when nothing is plotted there.
import matplotlib.pyplot as plt
from matplotlib import rc
import numpy as np
import seaborn as sns
HV = [388, 438]
YS = [1070, 1200]
UTS = [1150, 1400]
Z = [15, 12.5]
x = ["As built", "200ºC-850ºC"]
names = ['YS', 'UTS', 'HV', 'Z']
fig, ax1 = plt.subplots(figsize=(9, 5.5))
colors = sns.color_palette("tab10", len(names))
sns.barplot(x=x + x, y=YS + UTS, hue=[names[0]] * len(x) + [names[1]] * len(x),
hue_order=names, palette=colors, alpha=0.7, ax=ax1)
# ax1 will already contain the full legend, the third handle needs to
# be updated to show the hatching
ax1.legend_.legendHandles[2].set_hatch('///')
ax2 = ax1.twinx()
sns.barplot(x=x, y=HV, hue=[names[2]] * len(x), hue_order=names, palette=colors, hatch='//', alpha=0.7, ax=ax2)
ax2.legend_.remove() # seaborn automatically creates a new legend
ax3 = ax1.twinx()
sns.barplot(x=x, y=Z, hue=[names[3]] * len(x), hue_order=names, palette=colors, alpha=0.7, ax=ax3)
ax3.legend_.remove()
ax3.spines['right'].set_position(('axes', 1.15))
ax1.set_ylabel("First", color=colors[0])
ax2.set_ylabel("Second", color=colors[2])
ax3.set_ylabel("Third", color=colors[3])
ax1.tick_params(axis='y', colors=colors[0])
ax2.tick_params(axis='y', colors=colors[2])
ax3.tick_params(axis='y', colors=colors[3])
ax2.spines['right'].set_color(colors[2])
ax3.spines['right'].set_color(colors[3])
plt.tight_layout()
plt.show()

Unpack plot made with seaborn [duplicate]

I am making a function in python which allows me to create two parallel graphs and they share their 2 axes:
def PlotManager(data1,data2,fig):
f, (ax1, ax2) = fig.subplots(2, 1, sharey=True,sharex=True)
#Plot1 sopra
x_axis = data1.index
#Plot and shade the area between the upperband and the lower band grey
ax1.fill_between(x_axis,data1['Upper'],data1['Lower'], color = 'grey', alpha= 0.5)
#Plot the closing price and the moving average
ax1.plot(x_axis,data1['Close'],color = 'gold',lw = 3,label = 'Close Price', alpha= 0.5)
ax1.plot(x_axis,data1['SMA'],color = 'blue',lw = 3,label = 'Simple Moving Average', alpha= 0.5)
ax1.scatter(x_axis,data1['Buy'],color="green", lw=3,label="Buy",marker = "^", alpha=1)
ax1.scatter(x_axis,data1['Sell'],color="red", lw=3,label="Sell",marker = "v", alpha = 1)
#Set the title and show the image
ax1.set_title("Bollinger Band for Amazon")
plt.xticks(rotation = 45)
#Plot 2 Sotto
ax2.set_title('RSI_Plot')
ax2.plot(x_axis,data2['RSI'])
ax2.axhline(0,linestyle='--',alpha=0.5, color="grey")
ax2.axhline(10,linestyle='--',alpha=0.5, color="orange")
ax2.axhline(20,linestyle='--',alpha=0.5, color="green")
ax2.axhline(30,linestyle='--',alpha=0.5, color="red")
ax2.axhline(70,linestyle='--',alpha=0.5, color="red")
ax2.axhline(80,linestyle='--',alpha=0.5, color="green")
ax2.axhline(90,linestyle='--',alpha=0.5, color="orange")
ax2.axhline(100,linestyle='--',alpha=0.5, color="grey")
But gives me the cannot unpack non-iterable AxesSubplot object error:
[Command: python -u C:\Users\Nicolò\Documents\Git\ProgettoTradingBot\ProgettoTradeBot\GUIprova2.py]
C:\Users\Nicolò\Documents\Git\ProgettoTradingBot\ProgettoTradeBot\BollingerBandsFinal.py:63: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
ax = f.add_subplot(111)
C:\Users\Nicolò\Documents\Git\ProgettoTradingBot\ProgettoTradeBot\BollingerBandsFinal.py:63: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
ax = f.add_subplot(111)
Traceback (most recent call last):
File "C:\Users\Nicolò\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\cbook\__init__.py", line 196, in process
func(*args, **kwargs)
File "C:\Users\Nicolò\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\animation.py", line 951, in _start
self._init_draw()
File "C:\Users\Nicolò\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\animation.py", line 1743, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\Nicolò\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\animation.py", line 1766, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
File "C:\Users\Nicolò\Documents\Git\ProgettoTradingBot\ProgettoTradeBot\GUIprova2.py", line 48, in animate
PlotManager(BollingerBands(df,f),RSI(df,f2),f)
File "C:\Users\Nicolò\Documents\Git\ProgettoTradingBot\ProgettoTradeBot\mostraGrafici.py", line 7, in PlotManager
f, (ax1, ax2) = fig.subplots(2, 1, sharey=True,sharex=True)
TypeError: cannot unpack non-iterable AxesSubplot object
How can i handle to this error?
The value of plt.subplots(2, 1, ...) is a tuple figure, array(subplot0, subplot1) so that you can unpack correctly to a figure and two subplots.
On the contrary the value of fig.subplots(2, 1, ...) is subplot0, subplot1 (because you ALREADY have the figure…) and when you try to unpack it's equivalent to
f = subplot0
ax0, ax1 = subplot1
and this leads to TypeError: cannot unpack non-iterable AxesSubplot object
Because you are not using the object labeled as f in the following, you should write
ax1, ax2 = fig.subplots(2, 1, sharey=True,sharex=True)

QhullError When Plotting Wind Barbs

When attempting to plot wind barbs using matplotlib on a cartopy map, I get a QhullError. I've never seen this error before and my code hasn't changed since I last used it. I've also made sure the packages are up to date and the grib2 file being used is valid by printing the xarray variables. Below is the code:
file = xr.open_dataset('/Users/victoralvarez/prog2/grib/&var_UGRD=on&var_VGRD=on.grb',
engine='cfgrib')
# Mask the barbs where winds < 50.
masknum = int(input('BARB THRESHOLD: '))
# Extract the lon/lat.
x = file.variables['longitude'].values
y = file.variables['latitude'].values
# Extract the desired data.
u_wind = file.variables['u'].values * units('m/s')
v_wind = file.variables['v'].values * units('m/s')
# Calculate the wind speed.
wndspd = mpcalc.wind_speed(u_wind, v_wind).to('kt')
wnds_f = wndspd.astype(float)
mask = np.ma.masked_less_equal(wnds_f, masknum).mask
u_wind[mask] = np.nan
v_wind[mask] = np.nan
fig = plt.figure(1, figsize=(15,15))
ax = plt.axes(projection=ccrs.LambertConformal(central_longitude=-100,
central_latitude=35,
standard_parallels=(30, 60)))
ax.set_extent([-121, -75, 25, 50], ccrs.PlateCarree())
ax.add_feature(cfeature.OCEAN.with_scale('50m'), facecolor='#626262',
edgecolor='black',
zorder=0,
linewidth=.5)
ax.add_feature(cfeature.LAND.with_scale('50m'), edgecolor='black',
facecolor='#626262',
zorder=1)
ax.add_feature(cfeature.STATES.with_scale('50m'), linewidth=.5,
edgecolor='black',
zorder=5)
b1 = ax.barbs(x, y, u_wind.to('kt').m, v_wind.to('kt').m,
color='black', length=4.5, regrid_shape=20, pivot='middle',
linewidth=1.5, zorder=103, transform=ccrs.PlateCarree())
b2 = ax.barbs(x, y, u_wind.to('kt').m, v_wind.to('kt').m,
color='white', length=4.5, regrid_shape=10, pivot='middle',
linewidth=0.5, zorder=104, transform=ccrs.PlateCarree())
plt.savefig('img.png', dpi=300, bbox_inches='tight')
When running the script through terminal, the below errors show:
Traceback (most recent call last):
File "winds_barb.py", line 63, in <module>
linewidth=0.5, zorder=104, transform=ccrs.PlateCarree())
File "/anaconda3/lib/python3.7/site-packages/cartopy/mpl/geoaxes.py", line 1826, in barbs
target_extent=target_extent)
File "/anaconda3/lib/python3.7/site-packages/cartopy/vector_transform.py", line 146, in vector_scalar_to_grid
return _interpolate_to_grid(nx, ny, x, y, u, v, *scalars, **kwargs)
File "/anaconda3/lib/python3.7/site-packages/cartopy/vector_transform.py", line 68, in _interpolate_to_grid
method='linear'),)
File "/anaconda3/lib/python3.7/site-packages/scipy/interpolate/ndgriddata.py", line 222, in griddata
rescale=rescale)
File "interpnd.pyx", line 248, in scipy.interpolate.interpnd.LinearNDInterpolator.__init__
File "qhull.pyx", line 1828, in scipy.spatial.qhull.Delaunay.__init__
File "qhull.pyx", line 354, in scipy.spatial.qhull._Qhull.__init__
scipy.spatial.qhull.QhullError: QH6019 qhull input error: can not scale last coordinate. Input is cocircular
or cospherical. Use option 'Qz' to add a point at infinity.
While executing: | qhull d Qz Qt Qbb Q12 Qc
Options selected for Qhull 2015.2.r 2016/01/18:
run-id 1133843321 delaunay Qz-infinity-point Qtriangulate Qbbound-last
Q12-no-wide-dup Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood
This error is most likely caused by your input latitude coordinate containing the pole. The following code will reproduce the error you are seeing:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
lons = np.array([-110, -100, -90])
lats = np.array([-90, 30, 40, 50])
u = np.ones([len(lats), len(lons)]) * 10
v = np.ones_like(u) * 10
p = ccrs.LambertConformal(
central_longitude=-100,
central_latitude=35,
standard_parallels=(30, 60),
)
ax = plt.axes(projection=p)
ax.coastlines()
ax.set_extent([-121, -75, 25, 50], crs=ccrs.PlateCarree())
ax.barbs(lons, lats, u, v, regrid_shape=3, transform=ccrs.PlateCarree())
plt.show()
But when the pole is removed there is no error:
lats = np.array([30, 40, 50])
Since your example is not runnable I cannot suggest the exact fix for your case. However, you can likely exclude such points from your input data prior to plotting to avoid this problem and still use your desired projection.
It appears the solution (sorta) to this was to simply use a different projection than the LambertConformal projection I was using originally. Not exactly sure what was wrong so this is only a circumvention to the original problem.

python - strange error when plotting errorbars

I'm trying to combine 3 datasets in one plot. Each dataset has it's own y and x error. I'm receiving this error message:
Traceback (most recent call last):
File "SED_plot.py", line 310, in <module>
plt.errorbar(x0, y0, xerr=x0err, linestyle='None', ecolor="black", label= "Channel Width")
File "/Library/Python/2.7/site-packages/matplotlib-override/matplotlib/pyplot.py", line 2766, in errorbar
errorevery=errorevery, capthick=capthick, **kwargs)
File "/Library/Python/2.7/site-packages/matplotlib-override/matplotlib/axes/_axes.py", line 2749, in errorbar
in cbook.safezip(x, xerr[0])]
File "/Library/Python/2.7/site-packages/matplotlib-override/matplotlib/cbook.py", line 1479, in safezip
raise ValueError(_safezip_msg % (Nx, i + 1, len(arg)))
ValueError: In safezip, len(args[0])=16 but len(args[1])=48
when I run this code:
x0, y0 = x_val_all[0:16], y_val_all[0:16]
x0err, y0err = x_error_all[0:16], y_error_all[0:16]
x1, y1 = x_val_all[17:33], y_val_all[17:33]
x1err, y1err = x_error_all[17:33], y_error_all[17:33]
x2, y2 = x_val_all[33:49], y_val_all[33:49]
x2err, y2err = x_error_all[33:49], y_error_all[33:49]
plt.errorbar(x0, y0, xerr=x0err, linestyle='None', ecolor="black", label= "Channel Width")
plt.errorbar(x0, y0, yerr=y0err, linestyle='None', ecolor="black", label= "Standard Deviation")
plt.errorbar(x1, y1, xerr=x1err, yerr=y1err, ecolor="red")
plt.errorbar(x2, y2, xerr=x2err, yerr=y2err, ecolor="purple")
plt.show()
Could it be that list slicing isn't working in this case? All the x values and y values are in one list each (x_val_all, y_val_all respectively) and so are the corresponding errors.
Sample code to reproduce:
import matplotlib.pyplot as plt
y = range(0,21,1)
x = range(0,21,1)
y_err = [0.5]*21
x_low = [0.7]*21
x_upper = [1.4]*21
x_err = [x_low, x_upper]
plt.errorbar(x[0:7],y[0:7], xerr=x_err[0:7], yerr=y_err[0:7], linestyle="none", color="black")
plt.errorbar(x[8:15],y[8:15], xerr=x_err[8:15], yerr=y_err[8:15], linestyle="none", color="red")
plt.show()
Indexing x_err is the root cause of your error, as this is a list of two elements. My personal preference to fix this would be to use a list comprehension:
import matplotlib.pyplot as plt
y = range(0,21,1)
x = range(0,21,1)
y_err = [0.5]*21
x_low = [0.7]*21
x_upper = [1.4]*21
x_err = [x_low, x_upper]
plt.errorbar(x[0:7], y[0:7], xerr=[_x[0:7] for _x in x_err], yerr=y_err[0:7], linestyle="none", color="black")
plt.errorbar(x[8:15], y[8:15], xerr=[_x[8:15] for _x in x_err], yerr=y_err[8:15], linestyle="none", color="red")
plt.show()
(Note the use of _x within the list comprehension - list comprehension leaks into the local scope in Python 2.7, which would overwrite the earlier x variable if we used x as the variable within the comprehension.)
You could also do:
plt.errorbar(x[0:7], y[0:7], xerr=[x_err[0][0:7], x_err[1][0:7]], yerr=y_err[0:7], linestyle="none", color="black")
plt.errorbar(x[8:15], y[8:15], xerr=[x_err[0][8:15], x_err[1][8:15]], yerr=y_err[8:15], linestyle="none", color="red")
although this is a little more verbose.
Have a look at the docs you are presenting the x_error wrong, the list needs to be 2x7 however the way you slice it does does not produce that result. You are slicing a len 2 list with range 7. The code below gives you the plot you want
import matplotlib.pyplot as plt
y = range(0,21,1)
x = range(0,21,1)
y_err = [0.5]*21
x_low = [0.7]*21
x_upper = [1.4]*21
x_err = [x_low, x_upper]
fig, ax = plt.subplots()
idx = range(0, 16, 7)
for start, stop in zip(idx[:-1], idx[1:]):
ax.errorbar(x[start:stop], y[start:stop], y_err[start:stop], \
[ i[start:stop] for i in x_err])
Edit: for errors like this I recommend using numpy as its array allow you to easily check dimension and index into them easier than lists of lists.

Donut chart python

So I using this code to create a donut chart with python (inspired in this Donut plot recipe):
def make_pie(sizes, text,colors,labels):
import matplotlib.pyplot as plt
import numpy as np
col = [[i/255. for i in c] for c in colors]
fig, ax = plt.subplots()
ax.axis('equal')
width = 0.35
kwargs = dict(colors=col, startangle=180)
outside, _ = ax.pie(sizes, radius=1, pctdistance=1-width/2,labels=labels,**kwargs)
plt.setp( outside, width=width, edgecolor='white')
kwargs = dict(size=20, fontweight='bold', va='center')
ax.text(0, 0, text, ha='center', **kwargs)
plt.show()
c1 = (226,33,7)
c2 = (60,121,189)
make_pie([257,90], "Gender (AR)",[c1,c2],['M','F'])
which results in:
My problem is that now I want the respective percentages. For that I was simply adding the argument:
autopct='%1.1f%%'
like this:
kwargs = dict(colors=col, startangle=180,autopct='%1.1f%%')
but this results in the following error:
Traceback (most recent call last):
File "draw.py", line 30, in <module>
make_pie([257,90], "Gender (AR)",[c1,c2],['M','F'])
File "draw.py", line 13, in make_pie
outside, _ = ax.pie(sizes, radius=1, pctdistance=1-width/2,labels=labels,**kwargs)
ValueError: too many values to unpack
So, what am I doing wrong?
From the docstring:
If *autopct* is not *None*, return the tuple (*patches*,
*texts*, *autotexts*), where *patches* and *texts* are as
above, and *autotexts* is a list of
:class:`~matplotlib.text.Text` instances for the numeric
labels.
So if you want to unpack the result of pie() using autopct you need 3 values:
kwargs = dict(colors=col, startangle=180, autopct='%1.1f%%')
outside, _, _ = ax.pie(sizes, radius=1, pctdistance=1-width/2,
labels=labels,**kwargs)
Or maybe it will be more robust without unpacking so it works with or without autopct:
outside = ax.pie(sizes, radius=1, pctdistance=1-width/2,
labels=labels,**kwargs)[0]

Categories

Resources