I need to plot a 7x7 scatterplot diagram by metplotlib.pyplot (no seaborn at this moment). I try to make it semi automatics, so I use an array of ax names ax11, ax12, ......, ax77 to present the subplots. mean while when I use them to call scatter, it is rejected, I think python recognize them as strings but not keywords for the subplot. The Error Message is "AttributeError: 'str' object has no attribute 'scatter'". Here is the part of the code:
import matplotlib.pyplot as plt
import numpy as np
characters = ['A','B','C','D','E','F']
box = dict(facecolor ='yellow', pad = 5, alpha = 0.2)
fig, ((ax11,ax12,ax13,ax14,ax15,ax16,ax17),\
(ax21,ax22,ax23,ax24,ax25,ax26,ax27),\
(ax31,ax32,ax33,ax34,ax35,ax36,ax37),\
(ax41,ax42,ax43,ax44,ax45,ax46,ax47),\
(ax51,ax52,ax53,ax54,ax55,ax56,ax57),\
(ax61,ax62,ax63,ax64,ax65,ax66,ax67),\
(ax71,ax72,ax73,ax74,ax75,ax76,ax77),\
) = plt.subplots(7,7)
fig.subplots_adjust(left = 0.2, wspace =0.2,)
fig.tight_layout(pad=1, w_pad=2, h_pad=4.0)
st = fig.suptitle("Scatterplot diagram", \
fontsize="x- large")
for i in range(7):
for j in range(7):
no_ax = str(i)+str(j)
nm_ax = "ax"+str(no_ax)
nm_ax.scatter(data[caracters[i]],data[caracters[i]])
nm_ax.set_title('xy')
nm_ax.set_xlabel('x')
nm_ax.set_ylabel('y')
continue
st.set_y(0.95)
fig.subplots_adjust(top=0.85)
plt.show()
I believe there is a method to convert the string to a right format, but I do not know how. Please help. Thanks.
In general the approach of building variable names from strings should be avoided. While this can be done using the eval function, it's not even necessary.
The problem lies in the lines
no_ax = str(i)+str(j) #this is a string
nm_ax = "ax"+str(no_ax) # this is still a string
nm_ax.scatter(data[caracters[i]],data[caracters[i]])
# a string cannot be plotted to
A string does not have a scatter method. What you need is the axes object to which you plot.
A solution is to just use the axes that are created in the call to plt.subplots()directly in the loop.
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(ncols=7,nrows=7)
for i in range(7):
for j in range(7):
axes[i,j].scatter(np.random.rand(5),np.random.rand(5))
axes[i,j].set_title('{},{}'.format(i,j))
plt.show()
Related
My situation is this: I am developing a Jupyter-lab notebook to exemplify engineering topics. I find myself in the need of plotting something in an axes object within a figure, and then using a slider interact changing a value to update the plot.
Here is a MWE (or at least a shorter Working Example):
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
global ax1
global fig
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
plt.show()
return (rh)
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
In this example I create the figure fig and its axes ax1 declared as global variables (so that they will be available within function h. Then using RAD and ANG I create a scatter plot c.
Afterwards using the interact widget I would like to have three crosses change position along the r axis by changing the value of rh with the slider.
I don't get any error, but neither get I any crosses at all.
In the actual code I use pcolormesh instead of scatter.
I hope I made myself clear. I had got ti working by creating the figure and ax1 each time the function is called, but then I added some more suff thath don't need to be plotted each time.
Thanks for taking the time to read!
A very limited answer is that you function should return fig not rh.
Also note that you don't need the lines with global, and plt.show()
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
# plt.show()
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
I say limited because I think you want to update rather than add point?
A version which is hopefully more in line with what you want
the key point being the use of set_offsets method to update the positions.
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
new = [
[-np.pi/2, rh],
[np.pi*(2/3-1/2), rh],
[np.pi*(-1/2-2/3), rh],
]
c.set_offsets(new)
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
I have an array of numbers and I want its elements as the titles of subplots in a figure in Python. I want to format these titles in latex style.
I'm afraid this might be a simple question but I have not seen the answer elsewhere. I've seen that people use raw strings r'$ ... $' for latex expressions but I could not apply it successfully to solve this problem.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rc
#LaTeX font
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
rc('text', usetex=True)
# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
#The numbers I want as titles for the subplots
Nums = [0.01,0.10,0.30,0.75,1.00,2.00,10.0,12.0]
nrows=2
ncols=4
fig, ax = plt.subplots(nrows=2,ncols=4)
for row in range(nrows):
for col in range(ncols):
ax[row,col].set_title(repr(Nums[col+row*ncols]) + '$\\ \\mathrm{GeV}$')
ax[row,col].plot(x, y)
plt.show()
The best I managed to do is write the units in latex style and the numbers in "text" style. This is how it looks.
What do you say about that?
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import rc
#LaTeX font
rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
rc('text', usetex=True)
# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
#The numbers I want as titles for the subplots
Nums = [0.01,0.10,0.30,0.75,1.00,2.00,10.0,12.0]
nrows=2
ncols=4
fig, ax = plt.subplots(nrows=2,ncols=4)
for row in range(nrows):
for col in range(ncols):
#ax[row,col].set_title(repr(Nums[col+row*ncols]) + '$\\ \\mathrm{GeV}$')
ax[row,col].set_title(r'${:.2f}\mathrm{{GeV}}$'.format(Nums[col+row*ncols]))
ax[row,col].plot(x, y)
plt.show()
giving you this output
In you solution you juste encoded the GeV part as laTex, while the number was encoded as plain test (not in dollars).
You find more info about the used format statement here.
Note: You should replace Nums with nums. Capitalized variables are used for classes. Read the PEP8 Style guide for more info.
I'm doing some curve fitting with the wonderful scipy curve fit. When plotting the data and adding a legend label to display the parameters calculated, using $^{}$ to make the between bit superscript only works when the string is written and not when called from the string format. i.e, $x^{}$.format(3) doesn't format correctly but $x^3$ does.
Should this work? Do i need to do something else if i'm providing input to the legend label?
Example code and plot below. Thanks.
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
x_data = np.linspace(0.05,1,101)
y_data = 1/x_data
noise = np.random.normal(0, 1, y_data.shape)
y_data2 = y_data + noise
def func_power(x, a, b):
return a*x**b
popt, pcov= curve_fit(func_power, x_data, y_data2)
plt.figure()
plt.scatter(x_data, y_data2, label = 'data')
plt.plot(x_data, popt[0] * x_data ** popt[1], label = ("$y = {}x^{}$").format(round(popt[0],2), round(popt[1],2)))
plt.plot(x_data, x_data**3, label = '$x^3$')
plt.legend()
plt.show()
In order to have MathText interprete the curly brackets they still need to be present after formatting. So you will want to use a pair of curly brackets, the inner ones for formatting, the outer ones for MathText functionality. The outer ones then still need to be escaped in order not to be used for formatting. This leads to 3 curly brackets.
label = ("$y = {{{}}}x^{{{}}}$").format(round(popt[0],2), round(popt[1],2))
This seems like it should be an easy fix but I can't get it to work. I would like 40°N to display in the attached plot, but setting the labels argument in drawparallels to [1,0,1,1] isn't doing the trick. That should plot the parallels lables where they intersect the left, top and bottom of the plot according to the documentation. I would also like for 0° to once again show up in the bottom right corner. Any idea of how I can fix those 2 issues?
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
from mpl_toolkits.basemap import addcyclic
nc = NetCDFFile('C:/myfile.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
olr = nc.variables['olr'][:]
olr,lon = addcyclic(olr,lon)
map = Basemap(llcrnrlon=0.,llcrnrlat=-40.,urcrnrlon=360.,urcrnrlat=40.,resolution='l')
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
levels = np.arange(-19.5,20.0,0.5)
levels = levels[levels!=0]
ticks = np.arange(-20.0,20.0,4.0)
cs = map.contourf(x,y,olr[0],levels, cmap='bwr')
cbar = plt.colorbar(cs, orientation='horizontal', cmap='bwr', spacing='proportional', ticks=ticks)
cbar.set_label('Outgoing Longwave Radiation Anomalies $\mathregular{(W/m^2)}$')
map.drawcoastlines()
map.drawparallels(np.arange(-40,40,20),labels=[1,0,1,1], linewidth=0.5, fontsize=7)
map.drawmeridians(np.arange(0,360,40),labels=[1,1,0,1], linewidth=0.5, fontsize=7)
The first part of the question is easy. In order for the label to show up, you have to actually draw the parallel, but np.arange(-40,40,20) does not include 40. So, if you change that statement to np.arange(-40,41,20) your 40N label will show up.
The second part should in principle be solvable in the same way, but Basemap apparently uses the modulo of the longitudes to compute the position of the labels, so just using np.arange(0,361,40) when drawing the meridians will result in two 0 labels on top of each other. However, we can capture the labels that drawmeridians generates and manually change the position of the second 0 label. The labels are stored in a dictionary, so they are easy to deal with. To compute the x position of the last label, I compute the difference in x-position between the first and the second label, multiply that with the amount of meridians to be drawn (360/40) and add the x-position of the first label.
Here the complete example:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
map = Basemap(llcrnrlon=0.,llcrnrlat=-40.,urcrnrlon=360.,urcrnrlat=40.,resolution='l')
map.drawcoastlines()
yticks = map.drawparallels(
np.arange(-40,41,20),labels=[1,0,1,1], linewidth=0.5, fontsize=7
)
xticks = map.drawmeridians(
np.arange(0,361,40),labels=[1,1,0,1], linewidth=0.5, fontsize=7
)
first_pos = xticks[0][1][0].get_position()
second_pos = xticks[40][1][0].get_position()
last_x = first_pos[0]+(second_pos[0]-first_pos[0])*360/40
xticks[360][1][0].set_position((last_x,first_pos[1]))
plt.show()
Here the resulting plot:
Hope this helps.
I'm trying to use seaborn to set axes properties for a (potentially large) number of matplotlib subfigures. What I would like to be able to do is generate all the plots with a single call to plt.subplots, and then set the subplot style when each actual plot is generated. Unfortunately it seems that the sns style only matters when the subplot is generated.
The code below is a minimum (non)working example. Ideally the two subfigures would have two different styles, but they do not.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def makeplt(sub, dat):
sub.contour(dat)
def makepltwith(sub, dat, style):
with sns.axes_style(style) as sty:
sub.contour(dat)
dat = np.arange(100).reshape(10, 10)
with sns.axes_style('ticks'):
fig, subs = plt.subplots(ncols=2)
makeplt(subs[0], dat)
makepltwith(subs[1], dat, 'darkgrid')
plt.show()
Is there a way to ensure that the second plot has the formatting I want it to have? The best idea I have on my own is to make some use of the sty object to manually reformat the sub object, but I can't come up with a pithy way of running through the formatting.
seaborn.__version__=0.7,
matplotlib.__version__=1.5 if that matters.
I encountered a similar problem and solved it like this:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
def add_sp_default(fig,pos):
ax = fig.add_subplot(pos)
return ax
def add_sp_image(fig,pos):
ax = fig.add_subplot(pos)
img=mpimg.imread('http://static.wixstatic.com/media/4afb41_998a1c7c0835c6eae5e159be3c2cfc07.png_1024')
ax.imshow(img)
ax.set_axis_off()
return ax
def add_sp_polar(fig,pos):
ax = fig.add_subplot(pos,projection='polar')
return ax
def add_sp_xkcd(fig,pos):
with plt.xkcd():
ax = fig.add_subplot(pos)
return ax
fig = plt.figure(figsize=(10,7))
ax1 = add_sp_default(fig,221)
ax2 = add_sp_image(fig,222)
ax3 = add_sp_polar(fig,223)
ax4 = add_sp_xkcd(fig,224)
plt.show()
No, it is not possible to do that. Axes styles are applied when the axes is created.
Of course, there are other ways to set up the subplots that don't involve making them all in one line of code, which would be more amenable to subplot-specific styles.