I am trying to insert PNG images in a matplotlib figure. Based on answers here and here, this is so far my code:
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gs
from matplotlib.offsetbox import OffsetImage,AnnotationBbox
import seaborn as sns
sns.set_style("white")
image1 = "d1.PNG"
image2 = "d2.PNG"
image3 = "d3.PNG"
img1 = plt.imread(image1)
img2 = plt.imread(image2)
img3 = plt.imread(image3)
fig = plt.figure(tight_layout = True)
gs1 = gs.GridSpec(nrows = 2, ncols = 3)
ax1 = plt.subplot(gs1[:, 1])
ax1.text(x= 0.5, y = 0.5, s = "ax1", va = "center", ha = "center")
ax2 = plt.subplot(gs1[0, 2])
ax2.text(x= 0.5, y = 0.5, s = "ax2", va = "center", ha = "center")
ax3 = plt.subplot(gs1[1, 2])
ax3.text(x= 0.5, y = 0.5, s = "ax3", va = "center", ha = "center")
def add_image(img, coord):
im = OffsetImage(img, zoom = 0.07)
im.image.axes = ax1
ab = AnnotationBbox(im, (0.0, coord), xybox=(-100, 0.0), frameon=False, xycoords='data', boxcoords="offset points", pad=0.4)
ax1.add_artist(ab)
add_image(img1, 0.8)
add_image(img2, 0.45)
add_image(img3, 0.1)
sns.despine(ax = ax1, top = False, right = True, bottom = True)
sns.despine(ax = ax2, top = True, right = True)
sns.despine(ax = ax3, top = True, right = True)
plt.savefig("plot.pdf")
This produces:
The PNG images on the left appear pixelled. Is there a way I can insert these images with better quality?
I also have the images in PDF format. Would it be better if I try to transform the PDFs to PNGs and insert them later? According to what I have read, it is not possible to insert PDFs directly in matplotlib.
Thanks!
EDIT: This is what one of the original images look like:
I produced them with PowerPoint (might not be the best tool, I admit) and what I did is to make the slide size large (40cm x 20 cm) trying to improve the quality of the PNGs.
Related
Following the Slider Demo of Matplotlib https://matplotlib.org/gallery/widgets/slider_demo.html, I would like to update the Slider ranges, so that every time I change the slider values, those are re-centred in the Slider.
I have tried to define the Sliders as
sfreq = Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
samp = Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but since the update() function does not return anything, that does not work. I also tried making these variables global inside the function, which also did not work. I finally tried defining the Sliders inside the update function,
def update(val):
amp = samp.val
freq = sfreq.val
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but that overlays more and more Sliders as I change the values. Any suggestions?
So I just decided to make the range of the slider cover several orders of magnitude of the parameter, and display the values in a logarithmic scale. In case anyone wonders, and following the matplotlib demo:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 10
delta_f = 5.0
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', np.log(1), np.log10(1000), valinit=np.log10(f0), valfmt='%4.2E')
samp = Slider(axamp, 'Amp', a0-5, a0+5, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
sfreq.valtext.set_text('{:4.2E}'.format(10**freq))
l.set_ydata(amp*np.sin(2*np.pi*10**freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04] )
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
I learned how to embed a matploblib graph into a Tkinter GUI via this post https://matplotlib.org/examples/user_interfaces/embedding_in_tk.html. It seems working fine.
However, I had a problem with getting multiple matplotlib graphs to work correctly simultaneously, let me explain a bit more here. The code below generates two buttons, each links to a new window with two new buttons (load data and plot data). You may generate some dummy two column data, say data1.txt, data2.txt, data3.txt, data4.txt, for plotting. What I observed is (1) If I invoke the window separately, I have no problem loading and plotting data1.txt, data2.txt, data3.txt, data4.txt. However, if I open the two windows at the same time, I can only plot freely and correctly in the second window, while the first window plots nothing. It seems as if the first window were suppressed by the existence of the second.
Can anyone help me understand what is happening here. My matplotlib version is 2.0.2. Tkinter version is $Revision:81008$. Python version 2.7.15. Thank you!
from Tkinter import *
import Tkinter as tk
import ttk
import tkFileDialog
import numpy
##loading matplotlib modules
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.gridspec as gridspec
class Overall_Look:
def __init__(self, master):
self.master = master
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
##here are the layout for step 1, load structure files
ttk.Button(self.top_frame, text = "Button_1", command = self.plot_one,
style = "TButton").grid(row = 1, column = 0, columnspan = 2, padx = 5, sticky = "sw")
ttk.Button(self.top_frame, text = "Button_2",command = self.plot_two,
style = "TButton").grid(row = 1, column = 2, columnspan = 2, padx = 5, sticky = "sw")
def plot_one(self):
self.plot_one = tk.Toplevel(self.master)
self.GUI = Plot_One(self.plot_one)
def plot_two(self):
self.plot_two = tk.Toplevel(self.master)
self.GUI = Plot_Two(self.plot_two)
class Plot_One():
def __init__(self, master):
self.master = master
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
##here are the layout for step 1, load structure files
ttk.Button(self.top_frame, text = "Load Data 1", command = self.load_data_1,
style = "TButton").grid(row = 1, column = 0, columnspan = 2, padx = 5, sticky = "sw")
ttk.Button(self.top_frame, text = "Plot Data 1",command = self.start_plot_one,
style = "TButton").grid(row = 1, column = 2, columnspan = 2, padx = 5)
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig_1 = plt.figure(figsize=(5, 5), dpi=100) ##create a figure; modify the size here
self.fig_1.add_subplot(111)
self.fig_1.tight_layout()
self.canvas = FigureCanvasTkAgg(self.fig_1, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_data_1(self):
self.data_1 = tkFileDialog.askopenfilename(defaultextension = ".txt",
filetypes = [("Text Documents", "*.txt")])
def start_plot_one(self):
data = numpy.loadtxt(self.data_1).transpose()
x = data[0]
y = data[1]
self.fig_1.clf()
self.fig_1.add_subplot(111)
plt.plot(x, y, 'b-', lw=2)
self.fig_1.tight_layout()
self.canvas.draw()
class Plot_Two():
def __init__(self, master):
self.master = master
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
##here are the layout for step 1, load structure files
ttk.Button(self.top_frame, text = "Load Data 2", command = self.load_data_2,
style = "TButton").grid(row = 1, column = 0, columnspan = 2, padx = 5, sticky = "sw")
ttk.Button(self.top_frame, text = "Plot Data 2",command = self.start_plot_two,
style = "TButton").grid(row = 1, column = 2, columnspan = 2, padx = 5)
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig_2 = plt.figure(figsize=(5, 5), dpi=100) ##create a figure; modify the size here
self.fig_2.add_subplot(111)
self.fig_2.tight_layout()
self.canvas = FigureCanvasTkAgg(self.fig_2, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_data_2(self):
self.data_2 = tkFileDialog.askopenfilename(defaultextension = ".txt",
filetypes = [("Text Documents", "*.txt")])
def start_plot_two(self):
data = numpy.loadtxt(self.data_2).transpose()
x = data[0]
y = data[1]
self.fig_2.clf()
self.fig_2.add_subplot(111)
plt.plot(x, y, 'b-', lw=2)
self.fig_2.tight_layout()
self.canvas.draw()
def main():
root = Tk()
GUI = Overall_Look(root)
root.mainloop()
if __name__ == "__main__": main()
I am trying to use a log scale as the margin plots for my seaborn jointplot. I am usings set_xticks() and set_yticks(), but my changes do not appear. Here is my code below and the resulting graph:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import seaborn as sns
import pandas as pd
tips = sns.load_dataset('tips')
female_waiters = tips[tips['sex']=='Female']
def graph_joint_histograms(df1):
g=sns.jointplot(x = 'total_bill',y = 'tip', data = tips, space = 0.3,ratio = 3)
g.ax_joint.cla()
g.ax_marg_x.cla()
g.ax_marg_y.cla()
for xlabel_i in g.ax_marg_x.get_xticklabels():
xlabel_i.set_visible(False)
for ylabel_i in g.ax_marg_y.get_yticklabels():
ylabel_i.set_visible(False)
x_labels = g.ax_joint.get_xticklabels()
x_labels[0].set_visible(False)
x_labels[-1].set_visible(False)
y_labels = g.ax_joint.get_yticklabels()
y_labels[0].set_visible(False)
y_labels[-1].set_visible(False)
g.ax_joint.set_xlim(0,200)
g.ax_marg_x.set_xlim(0,200)
g.ax_joint.scatter(x = df1['total_bill'],y = df1['tip'],data = df1,c = 'y',edgecolors= '#080808',zorder = 2)
g.ax_joint.scatter(x = tips['total_bill'],y = tips['tip'],data = tips, c= 'c',edgecolors= '#080808')
ax1 =g.ax_marg_x.get_axes()
ax2 = g.ax_marg_y.get_axes()
ax1.set_yscale('log')
ax2.set_xscale('log')
ax1.set_yscale('log')
ax2.set_xscale('log')
ax2.set_xlim(1e0, 1e4)
ax1.set_ylim(1e0, 1e3)
ax2.xaxis.set_ticks([1e0,1e1,1e2,1e3])
ax2.xaxis.set_ticklabels(("1","10","100","1000"), visible = True)
plt.setp(ax2.get_xticklabels(), visible = True)
colors = ['y','c']
ax1.hist([df1['total_bill'],tips['total_bill']],bins = 10, stacked=True,log = True,color = colors, ec='black')
ax2.hist([df1['tip'],tips['tip']],bins = 10,orientation = 'horizontal', stacked=True,log = True,color = colors, ec='black')
ax2.set_ylabel('')
Any ideas would be much appreciated.
Here is the resulting graph:
You should actually get an error from the line g.ax_marg_y.get_axes() since an axes does not have a get_axes() method.
Correcting for that
ax1 =g.ax_marg_x
ax2 = g.ax_marg_y
should give you the desired plot. The ticklabels for the log axis are unfortunately overwritten by the histogram's log=True argument. So you can either leave that out (since you already set the axes to log scale anyways) or you need to set the labels after calling hist.
import matplotlib.pyplot as plt
import seaborn as sns
tips = sns.load_dataset('tips')
def graph_joint_histograms(tips):
g=sns.jointplot(x = 'total_bill',y = 'tip', data = tips, space = 0.3,ratio = 3)
g.ax_joint.cla()
g.ax_marg_x.cla()
g.ax_marg_y.cla()
for xlabel_i in g.ax_marg_x.get_xticklabels():
xlabel_i.set_visible(False)
for ylabel_i in g.ax_marg_y.get_yticklabels():
ylabel_i.set_visible(False)
x_labels = g.ax_joint.get_xticklabels()
x_labels[0].set_visible(False)
x_labels[-1].set_visible(False)
y_labels = g.ax_joint.get_yticklabels()
y_labels[0].set_visible(False)
y_labels[-1].set_visible(False)
g.ax_joint.set_xlim(0,200)
g.ax_marg_x.set_xlim(0,200)
g.ax_joint.scatter(x = tips['total_bill'],y = tips['tip'],data = tips,
c = 'y',edgecolors= '#080808',zorder = 2)
g.ax_joint.scatter(x = tips['total_bill'],y = tips['tip'],data = tips,
c= 'c',edgecolors= '#080808')
ax1 =g.ax_marg_x
ax2 = g.ax_marg_y
ax1.set_yscale('log')
ax2.set_xscale('log')
ax2.set_xlim(1e0, 1e4)
ax1.set_ylim(1e0, 1e3)
ax2.xaxis.set_ticks([1e0,1e1,1e2,1e3])
ax2.xaxis.set_ticklabels(("1","10","100","1000"), visible = True)
plt.setp(ax2.get_xticklabels(), visible = True)
colors = ['y','c']
ax1.hist([tips['total_bill'],tips['total_bill']],bins = 10,
stacked=True, color = colors, ec='black')
ax2.hist([tips['tip'],tips['tip']],bins = 10,orientation = 'horizontal',
stacked=True, color = colors, ec='black')
ax2.set_ylabel('')
graph_joint_histograms(tips)
plt.show()
Here is my script to plot data from a Geogtiff file using basemap. The data is categorical and there are 13 categories within this domain. The problem is that some categories get bunched up into one colour and thus some resolution is lost.
Unfortunately, I do not know how to fix this. I read that plt.cm.get_cmp is better for discrete datasets but I have not gotten it to work unfortunately.
gtif = 'some_dir'
ds = gdal.Open(gtif)
data = ds.ReadAsArray()
gt = ds.GetGeoTransform()
proj = ds.GetProjection()
xres = gt[1]
yres = gt[5]
xmin = gt[0] + xres
xmax = gt[0] + (xres * ds.RasterXSize) - xres
ymin = gt[3] + (yres * ds.RasterYSize) + yres
ymax = gt[3] - yres
xy_source = np.mgrid[xmin:xmax+xres:xres, ymax+yres:ymin:yres]
ds = None
fig2 = plt.figure(figsize=[12, 11])
ax2 = fig2.add_subplot(111)
ax2.set_title("Land use plot")
bm2 = Basemap(ax=ax2,projection='cyl',llcrnrlat=ymin,urcrnrlat=ymax,llcrnrlon=xmin,urcrnrlon=xmax,resolution='l')
bm2.drawcoastlines(linewidth=0.2)
bm2.drawcountries(linewidth=0.2)
data_new=np.copy(data)
data_new[data_new==255] = 0
nbins = np.unique(data_new).size
cb =plt.cm.get_cmap('jet', nbins+1)
img2 =bm2.imshow(np.flipud(data_new), cmap=cb)
ax2.set_xlim(3, 6)
ax2.set_ylim(50,53)
plt.show()
labels = [str(i) for i in np.unique(data_new)]
cb2=bm2.colorbar(img2, "right", size="5%", pad='3%', label='NOAH Land Use Category')
cb2.set_ticklabels(labels)
cb2.set_ticks(np.unique(data_new))
Here are the categories that are found within the domain (numbered classes):
np.unique(data_new)
array([ 0, 1, 4, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17], dtype=uint8)
Thanks so much for any help here. I have also attached the output image that shows the mismatch. (not working)
First, this colormap problem is independent of the use of basemap. The following is therefore applicable to any matplotlib plot.
The problem here is that creating a colormap from n values distributes those values equally over the colormap range. Some values from the image therefore fall into the same colorrange within the colormap.
To prevent this, one can generate a colormap with the initial number of categories as shown below.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors
# generate some data
data = np.array( [ 0, 1, 4, 5, 7, 10]*8 )
np.random.shuffle(data)
data = data.reshape((8,6))
# generate colormap and norm
unique = np.unique(data)
vals = np.arange(int(unique.max()+1))/float(unique.max())
cols = plt.cm.jet(vals)
cmap = matplotlib.colors.ListedColormap(cols, int(unique.max())+1)
norm=matplotlib.colors.Normalize(vmin=-0.5, vmax=unique.max()+0.5)
fig, ax = plt.subplots(figsize=(5,5))
im = ax.imshow(data, cmap=cmap, norm=norm)
for i in range(data.shape[0]):
for j in range(data.shape[1]):
ax.text(j,i,data[i,j], color="w", ha="center", va="center")
cb = fig.colorbar(im, ax=ax, norm=norm)
cb.set_ticks(unique)
plt.show()
This can be extended to exclude the colors not present in the image as follows:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors
# generate some data
data = np.array( [ 0, 1, 4, 5, 7, 10]*8 )
np.random.shuffle(data)
data = data.reshape((8,6))
unique, newdata = np.unique(data, return_inverse=1)
newdata = newdata.reshape(data.shape)
# generate colormap and norm
new_unique = np.unique(newdata)
vals = np.arange(int(new_unique.max()+1))/float(new_unique.max())
cols = plt.cm.jet(vals)
cmap = matplotlib.colors.ListedColormap(cols, int(new_unique.max())+1)
norm=matplotlib.colors.Normalize(vmin=-0.5, vmax=new_unique.max()+0.5)
fig, ax = plt.subplots(figsize=(5,5))
im = ax.imshow(newdata, cmap=cmap, norm=norm)
for i in range(newdata.shape[0]):
for j in range(newdata.shape[1]):
ax.text(j,i,data[i,j], color="w", ha="center", va="center")
cb = fig.colorbar(im, ax=ax, norm=norm)
cb.ax.set_yticklabels(unique)
plt.show()
I am quite the novice at matplotlib, so bear with me. I have the code below that plots a cylindrical equidistant grid of precipitation. I set the 'extents' limits that finally aligned my basemap with the data. Now, it appears to have "broken" my plt.text capability as I can no longer see the text 'Precipitation Rate (mm/hour)'. Thanks for any help.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from pylab import *
import pickle
from mpl_toolkits.basemap import Basemap
fp = open('uneven_rgb.pkl', 'rb')
uneven_rgb = pickle.load(fp)
fp.close()
num_lon = 1440
num_lat = 400
precipfile = "/Users/bolvin/3B43.20111001.7.HDF_precip.bin"
fileobj = open(precipfile, mode='rb') # Open file as read only binary
data = np.fromfile (fileobj, dtype ='f')
datat = np.reshape(data, (num_lon, num_lat), order = 'FORTRAN')
datam = datat * 24.0
my_cmap = matplotlib.colors.LinearSegmentedColormap('my_colormap',uneven_rgb)
plt.figure(figsize = (20,10))
mapproj = Basemap(projection = 'cyl', llcrnrlat=-50.0, llcrnrlon=0.0, urcrnrlat=50.0,urcrnrlon=360.0)
mapproj.drawcoastlines()
mapproj.drawcountries()
mapproj.drawparallels(np.array([-30.0, 0.0, 30.0]), labels=[0,0,0,0])
mapproj.drawmeridians(np.array([90.0, 180.0, 270.0]), labels=[0,0,0,0])
myplot = plt.imshow(datam.T, interpolation = 'nearest', cmap = my_cmap, vmin = 0.0, vmax = 20.0, extent = (0.0, 360.0, -50.0, 50.0))
plt.title('October 2011 3B43 Precipitation', fontsize = 36, y = 1.03)
plt.text(1.0, 435.0, 'Precipitation Rate (mm/hour)', size = 20)
cbar = plt.colorbar(myplot, orientation='horizontal', shrink = 0.5, pad = 0.03)
cbar.ax.tick_params(labelsize=20)
plt.gca().axes.get_xaxis().set_visible(False)
plt.gca().axes.get_yaxis().set_visible(False)
plt.show()
fileobj.close()
plt.text gets as first argument the x and y coordinates on which your text will be put.
As you transformed your imshow plot into the bordes 0-360 for x and -50 to 50 for y, y=435 is not in the plot anymore.
You can check your limits with plt.gca().get_xlim().
You have to move it somewhere in your limits.
Your defining the units you are plotting with this text, right? So the natural place for this would be the label of the colorbar:
cbar = plt.colorbar(myplot, orientation='horizontal', shrink = 0.5,
pad = 0.03, label='Precipitation Rate (mm/hour)')