Animation is not working on a matplotlib gridspec - python-3.x

I have reviewed the answer to the question:
how to use GridSpec() with FuncAnimation in matplotlib?
however I believe I am doing something wrong but do not know what, my mission here is to create multiple animations for each of the grids and I also used a code mode for a gauge to animate the count of used data.
x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000)+7
x4 = np.random.uniform(14,20, 10000)
xrand = np.random.random(size = 10000)
x = [x1, x2, x3, x4]
plt.figure()
gspec = gridspec.GridSpec(3,3, wspace = 0.25)
GS1 = plt.subplot(gspec[0, 0])
GS2 = plt.subplot(gspec[0, 1])
GS3 = plt.subplot(gspec[1, 0])
GS4 = plt.subplot(gspec[1, 1])
GS5 = plt.subplot(gspec[0:,2:])
GS6 = plt.subplot(gspec[2,:2])
GS = [GS1, GS2, GS3, GS4, GS5, GS6]
bins1 = np.arange(-7.5, 2.5, 0.2)
bins2 = np.arange(0, 10, 0.2)
bins3 = np.arange(7, 17, 0.2)
bins4 = np.arange(12, 22, 0.2)
bins = [bins1, bins2, bins3, bins4]
axis1 = [-7.5, 2.5, 0, 0.6]
axis2 = [0, 10, 0, 0.6]
axis3 = [7, 17, 0, 0.6]
axis4 = [12, 22, 0, 0.6]
axis = [axis1, axis2, axis3, axis4]
GS1.hist(x1, bins = 1000)
GS2.hist(x2, bins = 1000)
GS3.hist(x3, bins = 1000)
GS4.hist(x4, bins = 1000)
GS5.scatter(x1, xrand, norm = True, c = 'r', s= 0.7)
GS5.scatter(x2, xrand, norm = True, c = 'g', s= 0.7)
GS5.scatter(x3, xrand, norm = True, c = 'b', s= 0.7)
GS5.scatter(x4, xrand, norm = True, c = 'y', s= 0.7)
for s in GS:
s.spines['right'].set_visible(False)
s.spines['top'].set_visible(False)
gspec.update(wspace = .6, hspace = .6)
fig, ((GS1,GS2),(GS3, GS4)) = plt.subplots(2, 2, sharey = True)
GS = [GS1, GS2, GS3, GS4]
def update(curr):
if curr == 500:
a.event_source.stop()
for i in range(0, len(GS)):
GS[i].hist(x[i][:curr], bins = bins[i], normed = True)
GS[i].axis(axis[i])
GS[i].gca().set_title('Sampling random distribution')
GS[i].gca().set_ylabel('Frequency')
GS[i].gca().set_xlabel('Value')
GS[i].annotate('n = {}'.format(curr), [3,27])
plt.tight_layout()
plt.show()
fig = plt.gcf()
a = animation.FuncAnimation(fig, update, interval = 10) #, blit = True, repeat = True)
I do not understand what is the problem with the code.

Problems:
You cannot use plt.show() inside the updating function.
GS[i].gca().set_title does not make sense, because GS[i] already is an axes, which does not have any gca() method. Same for set_ylabel and set_xlabel.
You need to call tight_layout() only once outside the loop.
You will need to remove the old histogram before plotting a new one on the axes.
Possibly the following is what you're after:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000)+7
x4 = np.random.uniform(14,20, 10000)
xrand = np.random.random(size = 10000)
x = [x1, x2, x3, x4]
bins1 = np.arange(-7.5, 2.5, 0.2)
bins2 = np.arange(0, 10, 0.2)
bins3 = np.arange(7, 17, 0.2)
bins4 = np.arange(12, 22, 0.2)
bins = [bins1, bins2, bins3, bins4]
axis1 = [-7.5, 2.5, 0, 0.6]
axis2 = [0, 10, 0, 0.6]
axis3 = [7, 17, 0, 0.6]
axis4 = [12, 22, 0, 0.6]
axis = [axis1, axis2, axis3, axis4]
fig, ((GS1,GS2),(GS3, GS4)) = plt.subplots(2, 2, sharey = True)
GS = [GS1, GS2, GS3, GS4]
def update(curr):
if curr == 500:
a.event_source.stop()
for i in range(0, len(GS)):
GS[i].clear()
GS[i].hist(x[i][:curr], bins = bins[i], normed = True)
GS[i].axis(axis[i])
GS[i].set_title('Sampling random distribution')
GS[i].set_ylabel('Frequency')
GS[i].set_xlabel('Value')
GS[i].annotate('n = {}'.format(curr), [3,27])
plt.tight_layout()
fig = plt.gcf()
a = animation.FuncAnimation(fig, update, interval = 10)
plt.show()

Related

How to pre-position sankey nodes in plotly?

I started using plotly to draw sankey charts.
I want to display a multi year series with birth and death year by year.
The current code looks like:
`
import plotly.graph_objects as go
fig = go.Figure(data=[go.Sankey(
node = dict(
pad = 15,
thickness = 20,
line = dict(color = "black", width = 0.5),
label = ["v11","v21","v31","v41","out21","out31","out41","in21","in31","in41", "v12", "v22"],
color = "blue"
),
link = dict(
source = [0, 7, 0, 1, 8, 1, 2, 9, 2, 10], #
target = [1, 1, 4, 2, 2, 5, 3, 3, 6,11],
value = [1000, 100, 100, 1000, 150, 50, 1000, 120, 80, 800]
))])
fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()
`
which produces something like:
Sankey
All birth nodes are attached to the left side and the out nodes to the right side.
It should however look like this:
Sankey manually rearranged
Is there a way to pin the x-axis?
Or any d3 example, which does the trick?
Thanks for any hint,
Carl
PS: With the following question I got one step further [https://stackoverflow.com/questions/61152889/plotly-how-to-set-node-positions-in-a-sankey-diagram].
The look like this now:
import plotly.graph_objects as go
unique_list = ["c0_v_2021","c0_v_2022","c0_v_2023","c0_v_2024","c0_o_2022","c0_o_2023","c0_o_2024","c0_i_2022","c0_i_2023","c0_i_2024"]
title_list = ["Vol 2021","Vol 2022","Vol 2023","Vol 2024","Out 2022","Out 2023","Out 2024","In 2022","In 2023","In 2024"]
sources = [0, 7, 0, 1, 8, 1, 2, 9, 2]
targets = [1, 1, 4, 2, 2, 5, 3, 3, 6]
values = [1000, 100, 100, 1000, 150, 50, 1000, 120, 80]
## correction based on type
def my_corr(node_corr, steps):
x_corr = []
y_corr = []
for ftype in node_corr:
xcorr = 0
ycorr = 0
if ftype == 'i':
xcorr = - steps/3
ycorr = -0.2
x_corr.append(xcorr)
y_corr.append(ycorr)
return x_corr , y_corr
def my_nodify (node_names):
# node_names = unique_list.copy()
# unique name endings
## get year
ends = sorted(list(set([e[-4:] for e in node_names])))
## get type
corr = (list(([e[-6] for e in node_names])))
min, max = ends[0], ends[-1]
#intervals
steps = 1/((int(max)-int(min)))
x_corr, y_corr = my_corr(corr, steps)
# x-values for each unique name ending for input as node position
nodes_x = {}
xVal = 0
for e in ends:
nodes_x[str(e)] = xVal
xVal += steps
#x and y values in list form
x_values = [nodes_x[n[-4:]] for n in node_names]
## apply x adjustments
x_values_c = [x_values[i] + x_corr[i] for i in range(len(x_corr))]
y_values = []
y_val = 0
for n in node_names:
y_values.append(y_val)
y_val+=.001
y_values.append(y_val)
## apply y adjustments
y_values_c = [y_values[i] + y_corr[i] for i in range(len(y_corr))]
# y_values_c = y_values
return x_values_c, y_values_c
nodified = my_nodify(unique_list)
# plotly setup
fig = go.Figure(data=[go.Sankey(
arrangement='snap',
# arrangement='perpendicular',
node = dict(
pad = 5,
thickness = 20,
line = dict(color = "black", width = 0.5),
label = title_list,
color = "blue",
x=nodified[0], y=nodified[1]
),
link = dict(
source = sources,
target = targets,
value = values
))])
fig.update_layout(
hovermode = 'x',
title="Some Flow",
font=dict(size = 10, color = 'white'),
plot_bgcolor='black',
paper_bgcolor='black'
)
fig.show()
and produces almost what I want although the In-nodes overlapp and the sorting of link is inconsistent.
How can I influence this behaviour?

Adding image generated from another library as inset in matplotlib

I've generated a network figure using vedo library and I'm trying to add this as an inset to a figure generated in matplotlib
import networkx as nx
import matplotlib.pyplot as plt
from vedo import *
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
G = nx.gnm_random_graph(n=10, m=15, seed=1)
nxpos = nx.spring_layout(G, dim=3, seed=1)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
nx_lines = [(nxpts[i], nxpts[j]) for i, j in G.edges()]
pts = Points(nxpts, r=12)
edg = Lines(nx_lines).lw(2)
# node values
values = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[30, 80, 10, 79, 70, 60, 75, 78, 65, 10],
[1, .30, .10, .79, .70, .60, .75, .78, .65, .90]]
time = [0.0, 0.1, 0.2] # in seconds
vplt = Plotter(N=1)
pts1 = pts.cmap('Blues', values[0])
vplt.show(
pts1, edg,
axes=False,
bg='white',
at=0,
interactive=False,
zoom=1.5
).screenshot("network.png")
ax = plt.subplot(111)
ax.plot(
[1, 2, 3], [1, 2, 3],
'go-',
label='line 1',
linewidth=2
)
arr_img = vplt.screenshot(returnNumpy=True, scale=1)
im = OffsetImage(arr_img, zoom=0.25)
ab = AnnotationBbox(im, (1, 0), xycoords='axes fraction', box_alignment=(1.1, -0.1), frameon=False)
ax.add_artist(ab)
plt.show()
ax.figure.savefig(
"output.svg",
transparent=True,
dpi=600,
bbox_inches="tight"
)
There resolution of the image in the inset is too low. Suggestions on how to add the inset without loss of resolution will be really helpful.
EDIT:
The answer posted below works for adding a 2D network, but I am still looking for ways that will be useful for adding a 3D network in the inset.
I am not familiar with vedo but the general procedure would be to create an inset_axis and plot the image with imshow. However, your code is using networkx which has matplotlib bindings and you can directly do this without vedo
EDIT: code edited for 3d plotting
import networkx as nx
import matplotlib.pyplot as plt
G = nx.gnm_random_graph(n=10, m=15, seed=1)
nxpos = nx.spring_layout(G, dim=3, seed=1)
nxpts = [nxpos[pt] for pt in sorted(nxpos)]
nx_lines = [(nxpts[i], nxpts[j]) for i, j in G.edges()]
# node values
values = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[30, 80, 10, 79, 70, 60, 75, 78, 65, 10],
[1, .30, .10, .79, .70, .60, .75, .78, .65, .90]]
time = [0.0, 0.1, 0.2] # in seconds
fig, ax = plt.subplots()
ax.plot(
[1, 2, 3], [1, 2, 3],
'go-',
label='line 1',
linewidth=2
)
from mpl_toolkits.mplot3d import (Axes3D)
from matplotlib.transforms import Bbox
rect = [.6, 0, .5, .5]
bbox = Bbox.from_bounds(*rect)
inax = fig.add_axes(bbox, projection = '3d')
# inax = add_inset_axes(,
# ax_target = ax,
# fig = fig, projection = '3d')
# inax.axis('off')
# set angle
angle = 25
inax.view_init(10, angle)
# hide axes, make transparent
# inax.set_facecolor('none')
# inax.grid('off')
import numpy as np
# plot 3d
seen = set()
for i, j in G.edges():
x = np.stack((nxpos[i], nxpos[j]))
inax.plot(*x.T, color = 'k')
if i not in seen:
inax.scatter(*x[0], color = 'skyblue')
seen.add(i)
if j not in seen:
inax.scatter(*x[1], color = "skyblue")
seen.add(j)
fig.show()

Python: Compute Bin-Mean Value of Scatter Plot Bullets

I have three 1D arrays (A, B, C) of equal length/size. I plot a scatter plot of B vs. A where I color each scatter plot bullet by the corresponding value in the C array (see the code below).
# Imports
import matplotlib.cm as cm
import matplotlib.colors as colors
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# Create the Arrays
A = 10 * np.random.random_sample((20, 20))
B = 10 * np.random.random_sample((20, 20))
C = 100 * np.random.random_sample((20, 20))
A = A.reshape(20*20)
B = B.reshape(20*20)
C = C.reshape(20*20)
# Create the Colormap and Define Boundaries
cmap_C = cm.jet
cmap_C.set_bad(color='white')
bounds_C = np.arange(0, 110, 10)
norm_C = mpl.colors.BoundaryNorm(bounds_C, cmap_C.N)
# Plot the Figure
plt.figure()
plt.scatter(A, B, c=C, marker='o', s=100, cmap=cmap_C, norm=norm_C)
plt.xlim([-1, 11])
plt.ylim([-1, 11])
plt.xticks(np.arange(0, 11, 1))
plt.yticks(np.arange(0, 11, 1))
plt.xlabel('A')
plt.ylabel('B')
plt.grid()
plt.colorbar(label='Value of C')
plt.show()
Some bullets overlap in the figure so we cannot see them clearly. Therfore, next I now want to compute and plot the mean C value of all scatter plot bullets within each 1 integer x 1 integer bin in the figure so that each square grid point is colored by one single color (these bins are illustrated by the figure gridding). How can I do this?
It's not totally clear what you are trying to do, but I think there is an analytic result to your question before you work too hard. The expected mean value of color (C vector) is 50 because you have generated a uniformly distributed sample [0, 100]. The coordinates are also uniformly distributed, but that is irrelevant. Of course, there will be some variance in each of the grid squares.
If you need to go forward as an exercise, I'd construct a dictionary of coordinate:color mappings to help set up a screen...
color_map = {(x, y): color for x, y, color in zip(A,B,C)}
Then you could set up a dictionary to gather results for each grid and probably by taking the int() value of the coordinates put the data into the correct data field for the grid
Below is a solution that works for my purposes.
# Imports
import matplotlib.cm as cm
import matplotlib.colors as colors
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from zipfile import ZipFile
# Create the Arrays
xx = 5
yy = 5
A = 10 * np.random.random_sample((xx, yy))
B = 10 * np.random.random_sample((xx, yy))
C = 100 * np.random.random_sample((xx, yy))
A = A.reshape(xx*yy)
B = B.reshape(xx*yy)
C = C.reshape(xx*yy)
color_map = {(x, y): color for x, y, color in zip(A,B,C)}
xedges = np.arange(11)
yedges = np.arange(11)
H, xedges, yedges = np.histogram2d(A, B, bins=(xedges, yedges))
HT = H.T
ca = np.asarray(list(color_map))
print(ca)
cai = ca.astype(int)
print(cai)
# Extracting all dictionary values using loop + keys()
res = []
for key in color_map.keys() :
res.append(color_map[key])
res = np.asarray(res)
resi = res.astype(int)
print(resi)
BMC = np.zeros([10, 10])
for i in np.arange(len(resi)):
BMC[cai[i,1],cai[i,0]] = BMC[cai[i,1],cai[i,0]] + resi[i]
print(cai[i])
print(resi[i])
print(BMC[cai[i,1],cai[i,0]])
print(HT)
print(BMC)
BMC = BMC/HT
print(BMC)
# Create the Colormap and Define Boundaries
cmap_C = cm.jet
cmap_C.set_bad(color='white')
bounds_C = np.arange(-5, 115, 10)
norm_C = mpl.colors.BoundaryNorm(bounds_C, cmap_C.N)
cmap_hist2d = cm.CMRmap_r
cmap_hist2d.set_bad(color='white')
bounds_hist2d = np.arange(-0.5, 4.5, 1)
norm_hist2d = mpl.colors.BoundaryNorm(bounds_hist2d, cmap_hist2d.N)
cmap_C = cm.jet
cmap_C.set_bad(color='white')
BMC_plot = np.ma.array ( BMC, mask=np.isnan(BMC)) # Mask NaN
bounds_C = np.arange(-5, 115, 10)
norm_C = mpl.colors.BoundaryNorm(bounds_C, cmap_C.N)
plt.subplot(311)
plt.scatter(A, B, c=C, marker='o', s=100, cmap=cmap_C, norm=norm_C)
plt.xlim([-1, 11])
plt.ylim([-1, 11])
plt.xticks(np.arange(0, 11, 1))
plt.yticks(np.arange(0, 11, 1))
plt.ylabel('B')
plt.grid()
plt.colorbar(label='Value of C', ticks=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
plt.subplot(312)
x, y = np.meshgrid(xedges, yedges)
plt.pcolor(x, y, HT, cmap=cmap_hist2d, norm=norm_hist2d)
plt.xlim([-1, 11])
plt.ylim([-1, 11])
plt.xticks(np.arange(0, 11, 1))
plt.yticks(np.arange(0, 11, 1))
plt.ylabel('B')
plt.grid()
plt.colorbar(label='Number of Data in Bin', ticks=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
plt.subplot(313)
plt.pcolor(x, y, BMC_plot, cmap=cmap_C, norm=norm_C)
plt.xlim([-1, 11])
plt.ylim([-1, 11])
plt.xticks(np.arange(0, 11, 1))
plt.yticks(np.arange(0, 11, 1))
plt.xlabel('A')
plt.ylabel('B')
plt.grid()
plt.colorbar(label='Bin-Mean C Value', ticks=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
plt.show()

How to bold and to break text in two line of plt.annotate [python]

I calculated R-squared and the regression equation, and then plotted the graph, but my problem is:
1) I can not write my text "plt.annotate" in two lines.
2) I can not write ($R^2$) in bold
here is my script:
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
y = [1, 2, 2, 4, 5, 4, 6, 4, 6, 7, 9, 10, 11, 12, 15]
n = len(x)
x = np.array(x)
y = np.array(y)
sumx = sum(x)
sumy = sum(y)
sumx2 = sum(x*x)
sumy2 = sum(y*y)
sumxy = sum(x*y)
promx = sumx/n
promy = sumy/n
m = (sumx*sumy - n*sumxy)/(sumx**2 - n*sumx2)
b = promy - m*promx
sigmax = np.sqrt(sumx2/n - promx**2)
sigmay = np.sqrt(sumy2/n - promy**2)
sigmaxy = sumxy/n - promx*promy
R2 = (sigmaxy/(sigmax*sigmay))**2
print(m, b)
print(R2)
plt.plot(x, y,'bo', label='H1')
plt.plot(x, m*x + b, 'r-')
plt.xlabel('x')
plt.ylabel('y')
#plt.grid()
plt.annotate('y = ' + str(round(m,4)) + 'x + ' + str(round(b,4)) + ' ; ' + '$R^2$ = ' + str(round(R2,3)), xy=(1.2, 11.5), fontsize=12, fontweight="bold")
plt.legend(loc=4)
plt.show()
I hope the result should be like that :
y = 0.75x + 0.2
R^2 = 0.914
You need to make the font bold inside the MathText command.
plt.annotate("$\mathbf{R}^\mathbf{2}$" ,...)
Example here:
tx = "y = {:.4f}x + {:.4f}\n$\mathbf{{R}}^\mathbf{{2}}$ = {:.4f}"
plt.annotate(tx.format(m,b,R), xy=(1.2, 11.5), fontsize=12, fontweight="bold")
Use \n for line breaks.
\mathbf{} to use bold font in math mode (between $ $).
E.g:
plt.annotate('first line\nsecond line\n$x=42$ $\mathbf{R^2}$ = 0',
xy=(0.5, 0.5), fontsize=12, fontweight="bold")

Loop to change spring lengths doesn't change anything when I run it

The project I'm working on is supposed to model a pulse moving down a set of spheres connected by springs. I am trying to decrease the spring length as the pulse moves down the chain, but when I run it, nothing happens.
Here's my code:
from visual import *
one = sphere(pos=(-10,0,0), radius = 0.5, color = color.red)
two = sphere(pos=(-8,0,0), radius = 0.5, color = color.orange)
three = sphere(pos=(-6,0,0), radius = 0.5, color = color.yellow)
four = sphere(pos=(-4,0,0), radius = 0.5, color = color.green)
five = sphere(pos=(-2,0,0), radius = 0.5, color = color.blue)
six = sphere(pos=(0,0,0), radius = 0.5, color = color.cyan)
seven = sphere(pos=(2,0,0), radius = 0.5, color = color.magenta)
eight = sphere(pos=(4,0,0), radius = 0.5, color = color.white)
nine = sphere(pos=(6,0,0), radius = 0.5, color = color.red)
ten = sphere(pos=(8,0,0), radius = 0.5, color = color.orange)
spring1 = helix(pos = (-10, 0, 0), length = 2, radius = 0.3,
thickness = 0.05, color = color.red)
spring2 = helix(pos = (-8, 0, 0), length = 2, radius = 0.3,
thickness = 0.05, color = color.orange)
spring3 = helix(pos = (-6, 0, 0), length = 2, radius = 0.3,
thickness = 0.05, color = color.yellow)
spring4 = helix(pos = (-4, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.green)
spring5 = helix(pos = (-2, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.blue)
spring6 = helix(pos = (0, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.cyan)
spring7 = helix(pos = (2, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.magenta)
spring8 = helix(pos = (4, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.white)
spring9 = helix(pos = (6, 0, 0), length = 2.0, radius = 0.3,
thickness = 0.05, color = color.red)
masses = [one, two, three, four, five, six, seven, eight, nine, ten]
springs = [spring1, spring2, spring3, spring4, spring5, spring6, spring7,
spring8, spring9]
while True:
n=0
deltax=.2
while n < 10:
rate(30)
masses[n].pos.x = masses[n].pos.x + deltax
if n < 9:
springs[n].pos = masses[n].pos
springs[n].axis = masses[n+1].pos-masses[n].pos
n=n+1
n = n-1
while n >= 0:
rate(30)
masses[n].pos.x = masses[n].pos.x - deltax
if n < 0:
springs[n-1].pos = masses[n-1].pos - deltax
springs[n-1].axis = masses[n].pos-masses[n-1].pos
n = n-1
while True:
m=0
deltat=.2
while m<9:
rate(30)
springs[m].length = springs[m].length - deltat
springs[m].length = springs[m].length + deltat
m=m+1
m=m-1
while n>=0:
rate(30)
springs[m].length = springs[m].length - deltat
springs[m].length = springs[m].length + deltat
m=m-1
Make the masses have opacity = 0.3 and replace the first rate statement with scene.waitfor('click'). You'll see that when you move the nth mass to the right, the spring does in fact shorten, and when the (n+1)th mass is moved, the spring to its right shortens, leaving a gap between the nth spring and the (n+1)th spring.
Incidentally, I don't understand where there is a second "while True". It will never be reached.
I'll advertise that a better place to pose VPython questions is in the VPython forum at
https://groups.google.com/forum/?fromgroups&hl=en#!forum/vpython-users

Resources