Change RGB color in matplotlib animation - python

I seems that it is not possible to change colors of a Matplotlib scatter plot through a RGB definition. Am I wrong?
Here is a code (already given in stack overflow) which work with colors indexed in float:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def main():
numframes = 100
numpoints = 10
color_data = np.random.random((numframes, numpoints))
x, y, c = np.random.random((3, numpoints))
fig = plt.figure()
scat = plt.scatter(x, y, c=c, s=100)
ani = animation.FuncAnimation(fig, update_plot, frames=range(numframes),
fargs=(color_data, scat))
plt.show()
def update_plot(i, data, scat):
scat.set_array(data[i])
return scat,
main()
But if color_data is defined through RGB colors, I get an error:
ValueError: Collections can only map rank 1 arrays
The related code is the following (in this code, I just change the color of one sample each time):
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def main():
numframes = 100
numpoints = 10
rgb_color_data = np.random.random((numpoints, 3))
x, y = np.random.random((2, numpoints))
fig = plt.figure()
scat = plt.scatter(x, y, c=rgb_color_data, s=100) #this work well at this level
ani = animation.FuncAnimation(fig, update_plot2, frames=range(numframes),
fargs=(rgb_color_data, scat))
plt.show()
def update_plot2(i,data,scat):
data[ i%10 ] = np.random.random((3))
scat.set_array(data) # this fails
return scat,
main()
Is there a means to use set_array with RGB color array?

Not sure what you are trying to achieve. But if you are trying to change the color, why not use the set_color() function of Collection?
def update_plot2(i,data,scat):
data[ i%10 ] = np.random.random((3))
scat.set_color(data) # <<<<<<<<<<<<<<<<<<<
return scat,

Related

Fading animated scatterplot with multiple colours

I have 3 columns of data representing 3 pixels (x1, x2, x3), that update live.
I want to:
animate a scatter with x1 at x=1, x2 at x=2, x3 at x=3
have a distinct colour for each of the pixels (x1=red, x2=blue, x3=green)
when updating the figure with new data, have previous scatter data fade.
I am trying to modify from: Matplotlib Plot Points Over Time Where Old Points Fade
However I am unable to assign a different colour to each value of x (x=1, x=2, x=3):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.animation import PillowWriter
fig, ax = plt.subplots()
ax.set_xlabel('X Axis', size = 12)
ax.set_ylabel('Y Axis', size = 12)
ax.axis([0,4,0,1])
x_vals = []
y_vals = []
intensity = []
iterations = 100
t_vals = np.linspace(0,1, iterations)
colors = [[0,0,1,0],[0,0,1,0.5],[0,0.2,0.4,1], [1,0.2,0.4,1]]
cmap = LinearSegmentedColormap.from_list("", colors)
scatter = ax.scatter(x_vals,y_vals, c=[], cmap=cmap, vmin=0,vmax=1)
def get_new_vals():
x = np.arange(1,4) # TODO: ASSOCIATE COLOUR WITH EACH X VALUE
y = np.random.rand(3)
return list(x), list(y)
def update(t):
global x_vals, y_vals, intensity
# Get intermediate points
new_xvals, new_yvals = get_new_vals()
x_vals.extend(new_xvals)
y_vals.extend(new_yvals)
# Put new values in your plot
scatter.set_offsets(np.c_[x_vals,y_vals])
#calculate new color values
intensity = np.concatenate((np.array(intensity)*0.96, np.ones(len(new_xvals))))
scatter.set_array(intensity)
# Set title
ax.set_title('Different colours for each x value')
ani = matplotlib.animation.FuncAnimation(fig, update, frames=t_vals,interval=50)
plt.show()
It looks like you took the right approach, the only change I would suggest is creating 3 different scatter plots (one for each x values) instead of one.
See code below:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.animation import PillowWriter
import matplotlib.cm as cm
fig, ax = plt.subplots()
ax.set_xlabel('X Axis', size = 12)
ax.set_ylabel('Y Axis', size = 12)
ax.axis([0,4,0,1])
x_vals = []
y_vals = []
iterations = 100
t_vals = np.linspace(0,1, iterations)
cmaps=[cm.get_cmap('Reds'),cm.get_cmap('Blues'),cm.get_cmap('Greens')] #declaring colormaps
scatters=[ax.scatter(x_vals,y_vals,c=[],cmap=cmaps[i],vmin=0,vmax=1) for i in range(len(cmaps))] #initializing the 3 scatter plots
intensities=[[] for i in range(len(cmaps))] #initializing intensities array
def get_new_vals():
x = np.arange(1,4)
y = np.random.rand(3)
return x,y
def update(t):
global x_vals, y_vals, intensities
# Get intermediate points
new_xvals, new_yvals = get_new_vals()
x_vals=np.hstack((x_vals,new_xvals))
y_vals=np.hstack((y_vals,new_yvals))
# Put new values in your plot
for i in range(3):
scatters[i].set_offsets(np.c_[x_vals[x_vals==i+1],y_vals[x_vals==i+1]])
intensities[i]=np.concatenate((np.array(intensities[i])*0.96, np.ones(len(new_xvals[new_xvals==i+1]))))
scatters[i].set_array(intensities[i])
ax.set_title('Different colours for each x value')
ani = matplotlib.animation.FuncAnimation(fig, update, frames=t_vals,interval=50)
plt.show()

Matplotlib 3d scatter _facecolors3d not working

I am trying to save a 3d scatter plot animation where points appear one at a time. I made the animation work, but when I set the face colors of the points they do not take effect and all points appear blue. When I use the same color array but on static image, colors work well.
Animation Code:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation
import random
import seaborn as sns
import pandas as pd
import json
import os
from matplotlib.animation import FuncAnimation
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import rc
from IPython.display import HTML
from itertools import product
x=[]
y=[]
for i in range(-80, 80, 10):
x.append(i)
y.append(i)
combs = list(product(x,y))
def obj(x, y):
global HISTORY
e = 2.718
res = 7*x*y/(e**(0.001*x**2 + 0.001*y**2))
return res
z = [obj(x,y) for x, y in combs]
x = [obj[0] for obj in combs]
y = [obj[1] for obj in combs]
data = [[x[i],y[i],z[i]] for i in range(len(x))]
cmap = sns.cubehelix_palette(as_cmap=True)
m = max(z) # Get the worst score so we can use it as the darkest area of the plot.
face_colors = np.array([cmap(i/m) for i in z]) # Map all of the values with cmap colors.
df = pd.DataFrame(data, columns=["x","y","z"])
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
sc = ax.scatter([],[],[], alpha=0.5)
def update(i):
sc._offsets3d = (df.x.values[:i], df.y.values[:i], df.z.values[:i])
sc._facecolors3d = face_colors[:i]
sc._facecolors2d=sc._facecolors3d
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(min(x),max(x))
ax.set_ylim(min(y),max(y))
ax.set_zlim(min(z),max(z))
ani = matplotlib.animation.FuncAnimation(fig, update, frames=len(df), interval=70)
HTML(ani.to_html5_video())
When I do not use the animation and just call plt.scatter like this:
sc = ax.scatter(df.x.values,df.y.values,df.z.values, facecolors=face_colors)
My image works well:
How can I keep these colors in my animation as well?
Code for static image:
x=[]
y=[]
for i in range(-80, 80, 10):
x.append(i)
y.append(i)
combs = list(product(x,y))
def obj(x, y):
global HISTORY
e = 2.718
res = 7*x*y/(e**(0.001*x**2 + 0.001*y**2))
return res
z = [obj(x,y) for x, y in combs]
x = [obj[0] for obj in combs]
y = [obj[1] for obj in combs]
data = [[x[i],y[i],z[i]] for i in range(len(x))]
cmap = sns.cubehelix_palette(as_cmap=True)
m = max(z) # Get the worst score so we can use it as the darkest area of the plot.
face_colors = [cmap(i/m) for i in z] # Map all of the values with cmap colors.
df = pd.DataFrame(data, columns=["x","y","z"])
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
sc = ax.scatter(df.x.values,df.y.values,df.z.values, facecolors=face_colors)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(min(x),max(x))
ax.set_ylim(min(y),max(y))
ax.set_zlim(min(z),max(z))
plt.show()
Might just be a typo. _facecolor3d instead of _facecolors3d try this:
def update(i):
sc._offsets3d = (df.x.values[:i], df.y.values[:i], df.z.values[:i])
sc._facecolor3d = face_colors[:i]
sc._edgecolor3d = face_colors[:i]

Add a vertical label to matplotlib colormap legend

This code enables me to plot a colormap of a "3d" array [X,Y,Z] (they are 3 simple np.array of elements). But I can't succeed in adding a vertical written label at the right of the colorbar legend.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure("Color MAP 2D+")
contour = plt.tricontourf(X, Y, Z, 100, cmap="bwr")
plt.xlabel("X")
plt.ylabel("Y")
plt.title("Color MAP 2D+")
#Legend
def fmt(x, pos):
a, b = '{:.2e}'.format(x).split('e')
b = int(b)
return r'${} \times 10^{{{}}}$'.format(a, b)
import matplotlib.ticker as ticker
plt.colorbar(contour, format=ticker.FuncFormatter(fmt))
plt.show()
It's anoying to not get an easy answer from google... can someone help me ?
You are looking to add a label to the colorbar object. Thankfully, colorbar has a set_label function.
in short:
cbar = plt.colorbar(contour, format=ticker.FuncFormatter(fmt))
cbar.set_label('your label here')
In a minimal script:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
X = np.random.uniform(-2, 2, 200)
Y = np.random.uniform(-2, 2, 200)
Z = X*np.exp(-X**2 - Y**2)
contour = plt.tricontourf(X, Y, Z, 100, cmap="bwr")
def fmt(x, pos):
a, b = '{:.2e}'.format(x).split('e')
b = int(b)
return r'${} \times 10^{{{}}}$'.format(a, b)
cbar = plt.colorbar(contour, format=ticker.FuncFormatter(fmt))
cbar.set_label('your label here')
plt.show()
I believe your code is working. See this example:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris().data
X = iris[:,0]
Y = iris[:,1]
Z = iris[:,2]
fig = plt.figure("Color MAP 2D+")
contour = plt.tricontourf(X, Y, Z, 100, cmap="bwr")
plt.xlabel("X")
plt.ylabel("Y")
plt.title("Color MAP 2D+")
#Legend
def fmt(x, pos):
a, b = '{:.2e}'.format(x).split('e')
b = int(b)
return r'${} \times 10^{{{}}}$'.format(a, b)
import matplotlib.ticker as ticker
plt.colorbar(contour, format=ticker.FuncFormatter(fmt))
plt.show()
Output:

Update interactive plot in Jupyter Notebook [duplicate]

I am trying to animate a pcolormesh in matplotlib. I have seen many of the examples using the package animation, most of them using a 1D plot routine, and some of them with imshow().
First, I wan to use the FuncAnimation routine. My problem is, first, that I do not know if I can initialize the plot
fig,ax = plt.subplots()
quad = ax.pcolormesh(X,Y,Z)
I have tried a few simple lines:
fig,ax = plt.subplots()
quad = ax.pcolormesh([])
def init():
quad.set_array([])
return quad,
def animate(ktime):
quad.set_array(X,Y,np.sin(Z)+ktime)
return quad,
anim = animation.FuncAnimation(fig,animate,init_func=init,frames=Ntime,interval=200,blit=True)
plt.show()
By the way, How do I set labels into and animated plot? Can I animate the title, if it is showing a number that changes in time?
Thanks
The problem was that I was wrongly using set_array() routine. It is very important to note that you must pass a 1D array to this routine. To do so, regarding that color, pcolormesh and so on usually plots multidimensional arrays, you should use .ravel() .
One more important thing: In order to animate different plots at the same time, the blitz option at animate.FuncAnimation must be False (See section "Animating selected plot elements" of this link).
Here I post the code that simple program with various subplots:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.animation as animation
y, x = np.meshgrid(np.linspace(-10, 10,100), np.linspace(-10, 10,100))
z = np.sin(x)*np.sin(x)+np.sin(y)*np.sin(y)
v = np.linspace(-10, 10,100)
t = np.sin(v)*np.sin(v)
tt = np.cos(v)*np.cos(v)
###########
fig = plt.figure(figsize=(16, 8),facecolor='white')
gs = gridspec.GridSpec(5, 2)
ax1 = plt.subplot(gs[0,0])
line, = ax1.plot([],[],'b-.',linewidth=2)
ax1.set_xlim(-10,10)
ax1.set_ylim(0,1)
ax1.set_xlabel('time')
ax1.set_ylabel('amplitude')
ax1.set_title('Oscillationsssss')
time_text = ax1.text(0.02, 0.95, '', transform=ax1.transAxes)
#############################
ax2 = plt.subplot(gs[1:3,0])
quad1 = ax2.pcolormesh(x,y,z,shading='gouraud')
ax2.set_xlabel('time')
ax2.set_ylabel('amplitude')
cb2 = fig.colorbar(quad1,ax=ax2)
#########################
ax3 = plt.subplot(gs[3:,0])
quad2 = ax3.pcolormesh(x, y, z,shading='gouraud')
ax3.set_xlabel('time')
ax3.set_ylabel('amplitude')
cb3 = fig.colorbar(quad2,ax=ax3)
############################
ax4 = plt.subplot(gs[:,1])
line2, = ax4.plot(v,tt,'b',linewidth=2)
ax4.set_xlim(-10,10)
ax4.set_ylim(0,1)
def init():
line.set_data([],[])
line2.set_data([],[])
quad1.set_array([])
return line,line2,quad1
def animate(iter):
t = np.sin(2*v-iter/(2*np.pi))*np.sin(2*v-iter/(2*np.pi))
tt = np.cos(2*v-iter/(2*np.pi))*np.cos(2*v-iter/(2*np.pi))
z = np.sin(x-iter/(2*np.pi))*np.sin(x-iter/(2*np.pi))+np.sin(y)*np.sin(y)
line.set_data(v,t)
quad1.set_array(z.ravel())
line2.set_data(v,tt)
return line,line2,quad1
gs.tight_layout(fig)
anim = animation.FuncAnimation(fig,animate,frames=100,interval=50,blit=False,repeat=False)
plt.show()
print 'Finished!!'
There is an ugly detail you need to take care when using QuadMesh.set_array(). If you intantiate your QuadMesh with X, Y and C you can update the values C by using set_array(). But set_array does not support the same input as the constructor. Reading the source reveals that you need to pass a 1d-array and what is even more puzzling is that depending on the shading setting you might need to cut of your array C.
Edit: There is even a very old bug report about the confusing array size for shading='flat'.
That means:
Using QuadMesh.set_array() with shading = 'flat'
'flat' is default value for shading.
# preperation
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
y = np.linspace(-10, 10, num=1000)
x = np.linspace(-10, 10, num=1000)
X, Y = np.meshgrid(x, y)
C = np.ones((1000, 1000)) * float('nan')
# intantiate empty plot (values = nan)
pcmesh = plt.pcolormesh(X, Y, C, vmin=-100, vmax=100, shading='flat')
# generate some new data
C = X * Y
# necessary for shading='flat'
C = C[:-1, :-1]
# ravel() converts C to a 1d-array
pcmesh.set_array(C.ravel())
# redraw to update plot with new data
plt.draw()
Looks like:
Note that if you omit C = C[:-1, :-1] your will get this broken graphic:
Using QuadMesh.set_array() with shading = 'gouraud'
# preperation (same as for 'flat')
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
y = np.linspace(-10, 10, num=1000)
x = np.linspace(-10, 10, num=1000)
X, Y = np.meshgrid(x, y)
C = np.ones((1000, 1000)) * float('nan')
# intantiate empty plot (values = nan)
pcmesh = plt.pcolormesh(X, Y, C, vmin=-100, vmax=100, shading='gouraud')
# generate some new data
C = X * Y
# here no cut of of last row/column!
# ravel() converts C to a 1d-array
pcmesh.set_array(C.ravel())
# redraw to update plot with new data
plt.draw()
If you cut off the last row/column with shade='gouraud' you will get:
ValueError: total size of new array must be unchanged
I am not sure why your quad = ax.pcolormesh(X,Y,Z) function is giving an error. Can you post the error?
Below is what I would do to create a simple animation using pcolormesh:
import matplotlib.pyplot as plt
import numpy as np
y, x = np.meshgrid(np.linspace(-3, 3,100), np.linspace(-3, 3,100))
z = np.sin(x**2+y**2)
z = z[:-1, :-1]
ax = plt.subplot(111)
quad = plt.pcolormesh(x, y, z)
plt.colorbar()
plt.ion()
plt.show()
for phase in np.linspace(0,10*np.pi,200):
z = np.sin(np.sqrt(x**2+y**2) + phase)
z = z[:-1, :-1]
quad.set_array(z.ravel())
plt.title('Phase: %.2f'%phase)
plt.draw()
plt.ioff()
plt.show()
One of the frames:
Does this help? If not, maybe you can clarify the question.
There is another answer presented here that looks simpler thus better (IMHO)
Here is a copy & paste of the alternative solution :
import matplotlib.pylab as plt
from matplotlib import animation
fig = plt.figure()
plt.hold(True)
#We need to prime the pump, so to speak and create a quadmesh for plt to work with
plt.pcolormesh(X[0:1], Y[0:1], C[0:1])
anim = animation.FuncAnimation(fig, animate, frames = range(2,155), blit = False)
plt.show()
plt.hold(False)
def animate( self, i):
plt.title('Ray: %.2f'%i)
#This is where new data is inserted into the plot.
plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])

Can python make a tile plot?

Can python (eg matplotlib) make a tile plot like the following, where color indicates the intensity at each data point? Thanks!
You only need all of that machinery if you want the mouse to report back the value of the data under your mouse. To generate the image all you really need is (doc):
plt.imshow(data, interpolation='nearest')
You can control the color mapping via the cmap keyword.
Here is an example taken from http://matplotlib.org/examples/api/image_zcoord.html:
"""
Show how to modify the coordinate formatter to report the image "z"
value of the nearest pixel given x and y
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
X = 10*np.random.rand(5,3)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(X, cmap=cm.jet, interpolation='nearest')
numrows, numcols = X.shape
def format_coord(x, y):
col = int(x+0.5)
row = int(y+0.5)
if col>=0 and col<numcols and row>=0 and row<numrows:
z = X[row,col]
return 'x=%1.4f, y=%1.4f, z=%1.4f'%(x, y, z)
else:
return 'x=%1.4f, y=%1.4f'%(x, y)
ax.format_coord = format_coord
plt.show()
You are looking for image_zcode The example given is:
"""
Show how to modify the coordinate formatter to report the image "z"
value of the nearest pixel given x and y
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
X = 10*np.random.rand(5,3)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(X, cmap=cm.jet, interpolation='nearest')
numrows, numcols = X.shape
def format_coord(x, y):
col = int(x+0.5)
row = int(y+0.5)
if col>=0 and col<numcols and row>=0 and row<numrows:
z = X[row,col]
return 'x=%1.4f, y=%1.4f, z=%1.4f'%(x, y, z)
else:
return 'x=%1.4f, y=%1.4f'%(x, y)
ax.format_coord = format_coord
plt.show()

Categories