I have the following matplotlib script. I want to replace the points on the plot with images. Let's say 'red.png' for the red points and 'blue.png' for the blue points. How can I adjust the following to plot these images instead of the default points?
from scipy import linalg
import numpy as np
import pylab as pl
import matplotlib as mpl
import matplotlib.image as image
from sklearn.qda import QDA
###############################################################################
# load sample dataset
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data[:, 0:2] # Take only 2 dimensions
y = iris.target
X = X[y > 0]
y = y[y > 0]
y -= 1
target_names = iris.target_names[1:]
###############################################################################
# QDA
qda = QDA()
y_pred = qda.fit(X, y, store_covariances=True).predict(X)
###############################################################################
# Plot results
# constants
dpi = 72; imageSize = (32,32)
# read in our png file
im_red = image.imread('red.png')
im_blue = image.imread('blue.png')
def plot_ellipse(splot, mean, cov, color):
v, w = linalg.eigh(cov)
u = w[0] / linalg.norm(w[0])
angle = np.arctan(u[1] / u[0])
angle = 180 * angle / np.pi # convert to degrees
# filled gaussian at 2 standard deviation
ell = mpl.patches.Ellipse(mean, 2 * v[0] ** 0.5, 2 * v[1] ** 0.5,
180 + angle, color=color)
ell.set_clip_box(splot.bbox)
ell.set_alpha(0.5)
splot.add_artist(ell)
xx, yy = np.meshgrid(np.linspace(4, 8.5, 200), np.linspace(1.5, 4.5, 200))
X_grid = np.c_[xx.ravel(), yy.ravel()]
zz_qda = qda.predict_proba(X_grid)[:, 1].reshape(xx.shape)
pl.figure()
splot = pl.subplot(1, 1, 1)
pl.contourf(xx, yy, zz_qda > 0.5, alpha=0.5)
pl.scatter(X[y == 0, 0], X[y == 0, 1], c='b', label=target_names[0])
pl.scatter(X[y == 1, 0], X[y == 1, 1], c='r', label=target_names[1])
pl.contour(xx, yy, zz_qda, [0.5], linewidths=2., colors='k')
print(xx)
pl.axis('tight')
pl.show()
You can plot images instead of markers in a figure using BboxImage as in this tutorial.
from matplotlib import pyplot as plt
from matplotlib.image import BboxImage
from matplotlib.transforms import Bbox, TransformedBbox
# Load images.
redMarker = plt.imread('red.jpg')
blueMarker = plt.imread('blue.jpg')
# Data
blueX = [1, 2, 3, 4]
blueY = [1, 3, 5, 2]
redX = [1, 2, 3, 4]
redY = [3, 2, 3, 4]
# Create figure
fig = plt.figure()
ax = fig.add_subplot(111)
# Plots an image at each x and y location.
def plotImage(xData, yData, im):
for x, y in zip(xData, yData):
bb = Bbox.from_bounds(x,y,1,1)
bb2 = TransformedBbox(bb,ax.transData)
bbox_image = BboxImage(bb2,
norm = None,
origin=None,
clip_on=False)
bbox_image.set_data(im)
ax.add_artist(bbox_image)
plotImage(blueX, blueY, blueMarker)
plotImage(redX, redY, redMarker)
# Set the x and y limits
ax.set_ylim(0,6)
ax.set_xlim(0,6)
plt.show()
Related
I got some sort of a problem with a pendulum animation, I tried to display my animation (the pendulum's movement) next to a graph in two separate axes, but when I try my code, it barely works displaying two axes that overlap on one another... Here is what I tried:
PS: best would be that the circles I was intended to add at the end of my pendulum appear on the final animation, but I really have no idea how to put them only on a particular ax
from numpy import sin, cos, pi, array
import numpy as np
import scipy.integrate
import matplotlib.pyplot as plt
import matplotlib.animation as animation
g = 10
y0 = np.array([np.pi / 2.0, 0]) # angle, vitesse
j = 0.2
def f(y, t):
return np.array([y[1], -g * np.sin(y[0])-j*y[1]])
t = np.linspace(0, 100, 10000)
y = scipy.integrate.odeint(f, y0, t)
theta, thetadot = y[:, 0], y[:, 1]
fig, axs = plt.subplots(1,2)
axs[0] = fig.add_subplot(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5))
axs[0].grid()
axs[0].set_box_aspect(1)
# anchor = plt.Circle((0, 0), 0.01, color='black')
# mass = plt.Circle((sin(y0[0]),-cos(y0[0])), 0.2, color='black')
pendulums = axs[0].plot((0, sin(y0[0])), (0, -cos(y0[0])), 'o-', color = 'black')
# plt.gca().add_patch(weight) # adding circles
# plt.gca().add_patch(attach)
phase = axs[1].plot(theta,thetadot)
def animate(i):
angle = theta[i]
x = (0, sin(angle))
y = (0, -cos(angle))
#mass.center = (x[1],y[1])
pendulums[0].set_data(x, y)
anim = animation.FuncAnimation(fig, animate, interval=10)
plt.show()
I would like to reverse a grouped data and use group name as xtick label to draw it side by side. below demo mostly good but the label position not as expected.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
def main():
data = [['AAAAAA',8],['AAAAAA',9],['AAAAAA',10],['BBBBBB',5],['BBBBBB',6],['BBBBBB',7],['CCCCCC',1],['CCCCCC',2],['CCCCCC',3],['CCCCCC',4]]
df = pd.DataFrame(data,columns=['name','value'])
dfg = df.groupby('name')
fig, ax = plt.subplots(figsize=(8, 4))
i = 0
ymin = df['value'].min()
c1='#ececec'
c2='#bcbcbc'
color=c1
for ix, row in reversed(tuple(dfg)):
print(ix,row)
n = len(row['name'])
x = np.linspace(i,i + n,n)
ax.stem(x,row['value'])
font_dict = {'family':'serif','color':'darkred', 'size':8}
ax.text(i + n/2,ymin,ix,ha='right',va='top',rotation=90, fontdict=font_dict)
if color == c1:
color = c2
else:
color = c1
plt.axvspan(i, i+n, facecolor=color, alpha=0.5)
i += len(row)
ax.xaxis.set_ticks_position('none')
plt.setp( ax.get_xticklabels(), visible=False)
ax.grid(axis='y',color='gray', linestyle='dashed', alpha=1)
ax.spines[["top", "right"]].set_visible(False)
fig.tight_layout()
plt.show()
return
main()
Output:
Welcome to comment any other proper way to do this, or how to improve the xticks down, use ymin properly not good way to do it.
If my understanding of what you are trying to achieve is correct, here is one way to do it:
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
df = pd.DataFrame(
[
["AAAAAA", 8],
["AAAAAA", 9],
["AAAAAA", 10],
["BBBBBB", 5],
["BBBBBB", 6],
["BBBBBB", 7],
["CCCCCC", 1],
["CCCCCC", 2],
["CCCCCC", 3],
["CCCCCC", 4],
],
columns=["name", "value"],
)
fig, ax = plt.subplots(figsize=(8, 4))
i = 0
c1 = "#ececec"
c2 = "#bcbcbc"
color = c1
ticks = {}
for ix, row in reversed(tuple(df.groupby("name"))):
# Create stem plot
n = len(row["name"])
x = np.linspace(i, i + n, n)
ax.stem(x, row["value"])
# Create axvspan plot
if color == c1:
color = c2
else:
color = c1
ax.axvspan(i, i + n, facecolor=color, alpha=0.5)
# Save positions and names in a dict
for key, name in zip(x, row["name"]):
if key not in ticks.keys():
ticks[key] = name
else:
# Deal with multiple names for same tick
ticks[key] += f"\n{name}"
i += len(row)
# Add ticks and ticks labels
ax.set_xticks(ticks=list(ticks.keys()))
ax.set_xticklabels(list(ticks.values()), fontsize=10, rotation="vertical")
# In Jupyter notebook
fig
Output:
And to avoid repeating the labels, you can, for instance, do:
ax.set_xticklabels(
[
"",
"CCCCCC",
"",
"CCCCCC\nBBBBBB",
"BBBBBB",
"BBBBBB\nAAAAAA",
" " * 20 + "AAAAAA",
"",
],
fontsize=10,
)
# In Jupyter notebook
fig
Output:
I try to make 3 subplot which share one colorbar and the xaxis, as already explained by spinup in
Matplotlib 2 Subplots, 1 Colorbar
Using maps (with coastlines) in the subplots, it seems that a sharex is not supported.
However, is there a way, to apply a shared axis?
import cartopy.crs as ccrs
from cartopy.mpl.geoaxes import GeoAxes
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid
import numpy as np
def sample_data_3d(shape):
"""Returns `lons`, `lats`, `times` and fake `data`"""
ntimes, nlats, nlons = shape
lats = np.linspace(-np.pi / 2, np.pi / 2, nlats)
lons = np.linspace(0, 2 * np.pi, nlons)
lons, lats = np.meshgrid(lons, lats)
wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons)
mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2)
lats = np.rad2deg(lats)
lons = np.rad2deg(lons)
data = wave + mean
times = np.linspace(-1, 1, ntimes)
new_shape = data.shape + (ntimes, )
data = np.rollaxis(data.repeat(ntimes).reshape(new_shape), -1)
data *= times[:, np.newaxis, np.newaxis]
return lons, lats, times, data
def main():
projection = ccrs.PlateCarree()
axes_class = (GeoAxes,
dict(map_projection=projection))
lons, lats, times, data = sample_data_3d((6, 73, 145))
fig = plt.figure()
axgr = AxesGrid(fig, 111, axes_class=axes_class,
nrows_ncols=(3, 1),
axes_pad=0.6,
share_all=True, #doesn't change anything
cbar_location='bottom',
cbar_mode='single',
cbar_pad=0.2,
cbar_size='3%',
label_mode='') # note the empty label_mode
for i, ax in enumerate(axgr):
ax.coastlines()
ax.add_feature(cartopy.feature.LAND, zorder=100,
edgecolor='k',facecolor='w')
ax.set_xticks(np.linspace(-180, 180, 5), crs=projection)
ax.set_yticks(np.linspace(-90, 90, 5), crs=projection)
p = ax.contourf(lons, lats, data[i, ...],
transform=projection,
cmap='RdBu')
axgr.cbar_axes[0].colorbar(p)
plt.show()
I am trying to get matplotlib to create a dynamic 3d graph based on user input - but I can't get the graph to update. If I use the exact same code but without the "projection='3d'" setting, the program works correctly - but as soon as the graph is changed to display in 3d - it doesn't work.
Any help would be greatly appreciated.
3D Graph Code (graph doesn't update)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.1)
a0 = 5
b0 = 1
y = a0 * x + b0
z = np.zeros(10)
l, = plt.plot(x, y, z)
# Set size of Axes
plt.axis([0, 1, -10, 10])
# Place Sliders on Graph
ax_a = plt.axes([0.25, 0.1, 0.65, 0.03])
ax_b = plt.axes([0.25, 0.15, 0.65, 0.03])
# Create Sliders & Determine Range
sa = Slider(ax_a, 'a', 0, 10.0, valinit=a0)
sb = Slider(ax_b, 'b', 0, 10.0, valinit=b0)
def update(val):
a = sa.val
b = sb.val
l.set_ydata(a*x+b)
fig.canvas.draw_idle()
sa.on_changed(update)
sb.on_changed(update)
plt.show()
2D Graph Code (graph updates properly)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111)
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.1)
a0 = 5
b0 = 1
y = a0 * x + b0
l, = plt.plot(x, y)
# Set size of Axes
plt.axis([0, 1, -10, 10])
# Place Sliders on Graph
ax_a = plt.axes([0.25, 0.1, 0.65, 0.03])
ax_b = plt.axes([0.25, 0.15, 0.65, 0.03])
# Create Sliders & Determine Range
sa = Slider(ax_a, 'a', 0, 10.0, valinit=a0)
sb = Slider(ax_b, 'b', 0, 10.0, valinit=b0)
def update(val):
a = sa.val
b = sb.val
l.set_ydata(a*x+b)
fig.canvas.draw_idle()
sa.on_changed(update)
sb.on_changed(update)
plt.show()
The line in the 3D case needs to be updated in all 3 dimensions (even the data in some dimension stays the same). In order to do so, you have to set the 2D data using set_data and the third dimension using set_3d_properties. So updating y would look like this:
l.set_data(x, a*x+b)
l.set_3d_properties(z)
I'm wondering if there are some templates for viewing confusion matrices in matplotlib with a similar rendering, of which I ignore the specific nomenclature.
I have tried doing something similar with your fig 2. Here is my code using hand written digits data.
import numpy as np
from scipy import ndimage
from matplotlib import pyplot as plt
from sklearn import manifold, datasets
from scipy.spatial.distance import pdist, squareform
from scipy.cluster.hierarchy import leaves_list, linkage
def get_small_Xy(X, y, n=8):
X = np.vstack([X[y==e][0:n] for e in np.unique(y)])
y = np.hstack([[e]*n for e in np.unique(y)])
return X, y
# Load digit data
X_, y_ = datasets.load_digits(return_X_y=True)
# get a small set of data
X, y = get_small_Xy(X_, y_)
# Get similarity matrix
D = 1-squareform(pdist(X, metric='cosine'))
Z = linkage(D, method='ward')
ind = leaves_list(Z)
D = D[ind, :]
D = D[:, ind]
# labels and colors related
lbs = np.array([i if i==j else 10 for i in y for j in y])
colors = np.array(['C{}'.format(i) for i in range(10)]+['gray'])
colors[7] = '#413c39'
c = colors[lbs]
font1 = {'family': 'Arial',
'weight': 'normal',
'size': 8,
}
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
n = np.product(X.shape[0])
xx, yy = np.meshgrid(range(n), range(n))
xy = np.stack([xx.ravel(), yy.ravel()]).T
ax.scatter(xy[:, 0], xy[:, 1], s=D**4*30, fc=c, ec=None, alpha=0.8)
ax.set_xlim(-1, n)
ax.set_ylim(n, -1)
ax.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)
# place text
for i, e in enumerate(y):
ax.text(-1.2, i, e, ha='right', va='center', fontdict=font1, c=colors[e])
for i, e in enumerate(y):
ax.text(i, -1, e, ha='center', va='bottom', fontdict=font1, c=colors[e])
# draw lines
for e in np.where(np.diff(y))[0]:
ax.axhline(e+0.5, color='gray', lw=0.5, alpha=0.8)
ax.axvline(e+0.5, color='gray', lw=0.5, alpha=0.8)
One issue is the alpha of all points, which seems not to possible to set with different values with plot scatters in one run.