I'm trying to a 3d figure without any plot elements in python. Sort of a 3d version of this.
When I run the code I have added below, I get regular plot.
I want to remove the axes, axes labels, ticks, and background (and remain only with the surface).
How can I remove them.
Also, is there way to add arrows to the plot?
Here is my code:
import random
import math
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
pi = 3.14159
bignum = 3
amp = 0.1
mat = []
X = []
Y = []
class mode:
i=0
j=0
phase=0
amp=0
modes = dict()
for i in range(0,3):
submodes = dict()
for j in range (0,5):
if not (i==0 and j==2):
m = mode()
m.i = i
m.j = j
m.amp = amp*random.random()/(pow(i,2) + pow(j-2,2))
m.phase = random.random()*2*pi
submodes[j] = m
modes[i] = submodes
for x in range (0,bignum):
mat.append([])
for y in range (0,bignum):
dz = 0
for i in range (0,3):
for j in range (0,5):
if not (i == 0 and j == 2):
dz += math.cos(i*x*2*pi/bignum + j *y*2/bignum + modes[i][j].phase)*modes[i][j].amp
mat[x].append(dz)
X = np.mgrid[:bignum,:bignum]
print (len(X[0]))
print (len(mat))
fig = plt.figure(figsize=plt.figaspect(2.))
fig.frameon=True
ax = fig.add_subplot(1,1,1, projection='3d')
ax.frameon=False
ax.xticks=[]
ax.yticks=[]
ax.zticks=[]
surf = ax.plot_surface(X[0],X[1],mat,rstride=1, cstride=1,
linewidth=0, antialiased=False)
ax.set_zlim3d(0, 1)
plt.show()
To eliminate the 3d frame from the figure, use:
ax.set_axis_off()
This doesn't answer much of your question. But you can start turning stuff off with
plt.setp(ax.get_xticklabels(), visible=False)
I did some of it below. Also, the plt.annotate() function is how to add arrows in 2d plots...not sure how it upscales.
import random
import math
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
pi = 3.14159
bignum = 3
amp = 0.1
mat = []
X = []
Y = []
class mode:
i=0
j=0
phase=0
amp=0
modes = dict()
for i in range(0,3):
submodes = dict()
for j in range (0,5):
if not (i==0 and j==2):
m = mode()
m.i = i
m.j = j
m.amp = amp*random.random()/(pow(i,2) + pow(j-2,2))
m.phase = random.random()*2*pi
submodes[j] = m
modes[i] = submodes
for x in range (0,bignum):
mat.append([])
for y in range (0,bignum):
dz = 0
for i in range (0,3):
for j in range (0,5):
if not (i == 0 and j == 2):
dz += math.cos(i*x*2*pi/bignum + j *y*2/bignum + modes[i][j].phase)*modes[i][j].amp
mat[x].append(dz)
X = np.mgrid[:bignum,:bignum]
print (len(X[0]))
print (len(mat))
fig = plt.figure(figsize=plt.figaspect(2.))
fig.frameon=True
ax = fig.add_subplot(1,1,1, projection='3d')
ax.frameon=False
surf = ax.plot_surface(X[0],X[1],mat,rstride=1, cstride=1,
linewidth=0, antialiased=False)
ax.set_zlim3d(0, 1)
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.setp(ax.get_zticklabels(), visible=False)
plt.setp(ax.get_xticklines(), visible=False)
plt.setp(ax.get_yticklines(), visible=False)
plt.setp(ax.get_zticklines(), visible=False)
plt.setp(ax.get_frame(), visible = False)
#plt.annotate(r'Hello', xy = (.5, .5),
# xytext = (10,10),
# textcoords='offset points', arrowprops=dict(arrowstyle='->',
# connectionstyle='arc3,rad=0'))
plt.show()
You didn't ask this...but you should vectorize this code. Most/(all?) of the for loops could be avoided.
Related
Basically, I am looping generation of rays in Python and I'm trying to plot them all on the same graph. They should all be on a circle of radius 0.1. Each ray should be at a position on the circle that is varied by the arg which is in this case the theta. Also, just to mention (although I don't think it's that relevant) I am doing OOP here.
I get correct rays but I can't get them on the same 3d graph and I'm not sure how I'm supposed to do it. I thought using plt.show() would give me a graph with all 24 rays but it just plots 24 graphs.
Here is the relevant bit of code for reference:
r = 0.1
arg = 0
for i in range (0,24):
arg += np.pi/12
x = r*np.sin(arg)
y = r*np.cos(arg)
l = ray.Ray(r=np.array([x,y,0]),v=np.array([0.5,0,5]))
c = ray.SphericalRefraction(z0 = 100, curv = 0.0009, n1 = 1.0, n2 = 1.5, ar = 5)
c.propagate_ray(l)
o = ray.OutputPlane(250)
o.outputintercept(l)
points = np.array(l.vertices())
fig = plt.figure()
ax = plt.axes(projection='3d')
#ax = fig.add_subplot(1,2,1,projection='3d')
#plt.plot(points[:,2],points[:,0])
ax.plot3D(points[:,0],points[:,1],points[:,2])
plt.show()
Expanding on the comment by Mercury, the figure and also axes object must be created outside the loop.
import matplotlib.pyplot as plt
import numpy as np
r = 0.1
arg = 0
fig = plt.figure()
ax = plt.axes(projection='3d')
for i in range(0,24):
arg += np.pi/12 * i
v1 = r*np.sin(arg)
v2 = r*np.cos(arg)
# ...
# using sample data
x = []
y = []
z = []
for j in range(2):
x.append(j*v1)
y.append(j*v2)
z.append(j)
# add vertex to the axes object
ax.plot3D(x, y, z)
plt.show()
I want to animate julia sets. Anyways everything works so far,
I only need to change the axis labeling. When plotting my values the
x- and y axis are both showing the 500x500 linspace. Id rather like to see
the [-1,1]x[-1,1] Intervall ive defined the linspace on. How could I change that?
thank you :)
code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# Parameters and Test-Function
f = lambda z: z ** 2 - 0.1+0.651*1j
N = 1000
R = 2
def pre_greyscale(f, c, N, R):
if np.abs(c) > R:
return 0
else:
for i in range(0, N):
c = f(c)
if np.abs(c) > R:
return i + 1
return N
# fig1 = plt.figure()
real = np.linspace(-1, 1, num=500)
imaginary = np.linspace(-1, 1, num=500)
pre_image = np.empty(shape=(real.size, imaginary.size))
for k, r in enumerate(real):
for p, i in enumerate(imaginary):
pre_image[p, k] = pre_greyscale(f, r + i * 1j, N, R)
def animate(m):
image = np.empty(shape=(real.size, imaginary.size))
for k in range(0, 500):
for p in range(0, 500):
if pre_image[p, k] <= m:
image[p, k] = 1 - pre_image[p, k] / m
# else:
# image[k, p] = 0
# mat = plt.imshow(image, cmap='gray')
# plt.show()
return image
imagelist = [animate(x) for x in range(N)]
fig = plt.figure() # make figure
# Initialize imshow
im = plt.imshow(imagelist[0], cmap=plt.get_cmap('gray'), vmin=0, vmax=1)
# function to update figure
def updatefig(j):
# set the data in the axesimage object
im.set_array(imagelist[j])
# return the artists set
return [im]
# kick off the animation
ani = FuncAnimation(fig, updatefig, frames=N,
interval=20, blit=True)
ani.save('fractal2.gif', writer='pillow')
Adding the extent parameter to plt.imshow will set the correct labels:
# Initialize imshow
im = plt.imshow(imagelist[0], cmap=plt.get_cmap('gray'), vmin=0, vmax=1, extent=[-1,1,-1,1])
I want to embed subplots canvas inside a cartopy projected map. I wrote this code to show the expected result by using rectangles:
#%%
import numpy as np
import cartopy as cr
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from cartopy.io import shapereader
import geopandas
resolution = '10m'
category = 'cultural'
name = 'admin_0_countries'
shpfilename = shapereader.natural_earth(resolution, category, name)
# read the shapefile using geopandas
df = geopandas.read_file(shpfilename)
# read the country borders
usa = df.loc[df['ADMIN'] == 'United States of America']['geometry'].values[0]
can = df.loc[df['ADMIN'] == 'Canada']['geometry'].values[0]
central_lon, central_lat = -80, 60
extent = [-85, -55, 40, 62]
# ax = plt.axes(projection=ccrs.Orthographic(central_lon, central_lat))
#Golden ratio
phi = 1.618033987
h = 7
w = phi*h
fig = plt.figure(figsize=(w,h))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
#Set map extent
ax.set_extent(extent)
ax.set_xticks(np.linspace(extent[0],extent[1],11))
ax.set_yticks(np.linspace(extent[2],extent[3],6))
ax.add_geometries(usa, crs=ccrs.PlateCarree(), facecolor='none',
edgecolor='k')
# ax.gridlines()
ax.coastlines(resolution='50m')
nx, ny = 7,6
#Begin firts rectangle
xi = extent[0] + 0.5
yi = extent[2] + 0.5
x, y = xi, yi
#Loop for create the plots grid
for i in range(nx):
for j in range(ny):
#Inner rect height
in_h = 2.8
#Draw the rect
rect = ax.add_patch(mpatches.Rectangle(xy=[x, y], width=phi*in_h, height=in_h,
facecolor='blue',
alpha=0.2,
transform=ccrs.PlateCarree()))
#Get vertex of the drawn rectangle
verts = rect.get_path().vertices
trans = rect.get_patch_transform()
points = trans.transform(verts)
#Refresh rectangle coordinates
x += (points[1,0]-points[0,0]) + 0.2
if j == ny-1:
x = xi
y += (points[2,1]-points[1,1]) + 0.2
# print(points)
fig.tight_layout()
fig.savefig('Figure.pdf',format='pdf',dpi=90)
plt.show()
This routine prints this figure
What I am looking for is a way to embed plots that match every single rectangle in the figure. I tried with fig.add_axes, but I couldn't get that mini-canvas match with the actual rectangles.
Since you want to embed the axes inside the parent axes is recommend using inset_axes, see the documentation here.
I wrote simple code to demonstrate how it works. Clearly there will be some tweaking of the inset_axes positions and sizes necessary for your desired output, but I think my trivial implementation already does decent.
All created axes instances are stored in a list so that they can be accessed later.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
axis = []
x = np.linspace(-85, -55)
y = np.linspace(40, 62)
ax.plot(x, y)
offset_l = 0.05
offset_h = 0.12
num_x = 6
num_y = 7
xs = np.linspace(offset_l, 1-offset_h, num_x)
ys = np.linspace(offset_l, 1-offset_h, num_y)
for k in range(num_x):
for j in range(num_y):
ax_ins = ax.inset_axes([xs[k], ys[j], 0.1, 0.1])
ax_ins.axhspan(0, 1, color='tab:blue', alpha=0.2)
axis.append(ax_ins)
Alternatively, you can also specify the inset_axes positions using data coordinates, for this you have to set the kwarg transform in the method to transform=ax.transData, see also my code below.
import matplotlib.pyplot as plt
import numpy as np
#Golden ratio
phi = 1.618033987
h = 7
w = phi*h
fig, ax = plt.subplots(figsize=(w, h))
axis = []
x = np.linspace(-85, -55)
y = np.linspace(40, 62)
ax.plot(x, y)
offset_l = 0.05
offset_h = 0.12
num_x = 6
num_y = 7
fig.tight_layout()
extent = [-85, -55, 40, 62]
xi = extent[0] + 0.5
yi = extent[2] + 0.5
in_h = 2.8
in_w = phi * 2.8
spacing = 0.4
for k in range(num_x):
for j in range(num_y):
ax_ins = ax.inset_axes([xi+k*(in_w + phi*spacing), yi+j*(in_h + spacing),
in_w, in_h], transform=ax.transData)
ax_ins.axhspan(0, 1, color='tab:blue', alpha=0.2)
axis.append(ax_ins)
Let me first clarify that I'm not trying to generate random walk lines like in this and many other questions. I'm trying to make a random walk heat map that changes color as points are revisted, like this.
I've been able to create still-lifes like this: but I want to see the process.
I can get the figure to show up, and if I print the array at each step I can see that the walk is working. But the figure itself doesn't animate. My code:
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation as anim
import numpy as np
import sys
import random
length = int(sys.argv[1])
fig = plt.figure()
ax = plt.axes(xlim=(0, length-1), ylim=(0, length-1))
arr = np.zeros((length, length), dtype = int)
cmap = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['black','green','white'],
256)
bounds=[0,0,10,10]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
im=plt.imshow(arr, interpolation='nearest',
cmap = cmap,
origin='lower')
x = int(np.random.random_sample() * length)
y = int(np.random.random_sample() * length)
def walk():
global x, y
rand = np.random.random_sample()
if rand < 0.25 :
if x == length - 1:
x = 0
else: x = x + 1
elif rand < 0.5 :
if x == 0:
x = length - 1
else: x = x - 1
elif rand < 0.75 :
if y == length - 1:
y = 0
else: y = y + 1
else:
if y == 0:
y = length - 1
else: y = y - 1
return
def stand(arr):
global x,y
arr[x][y] = arr[x][y] + 1
return arr
def animate(i):
arr=im.get_array()
walk()
#print(a)
arr = stand(arr)
im.set_array(arr)
return [im]
anim = anim.FuncAnimation(fig, animate, frames=200, interval=20, blit=True)
plt.show()
Running Python 3.6, as you can see by the print.
There are so many videos with these animated grids and I can't find any answers! Somebody must know how to do it. Thanks!
I added animated=True and vmin=0, vmax=255, in the imshow() function below. I also changed the stand() line to arr[x][y] = arr[x][y] + 10.
#!/usr/bin/env python3
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation as anim
import numpy as np
import sys
import random
length = int(sys.argv[1])
fig = plt.figure()
ax = plt.axes(xlim=(0, length-1), ylim=(0, length-1))
arr = np.zeros((length, length), dtype = int)
cmap = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['black','green','white'],
256)
bounds=[0,0,10,10]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
im=plt.imshow(arr, interpolation='nearest',
cmap = cmap, vmin=0, vmax=255,
origin='lower', animated=True) # small changes here
x = int(np.random.random_sample() * length)
y = int(np.random.random_sample() * length)
def walk():
global x, y
rand = np.random.random_sample()
if rand < 0.25 :
if x == length - 1:
x = 0
else: x = x + 1
elif rand < 0.5 :
if x == 0:
x = length - 1
else: x = x - 1
elif rand < 0.75 :
if y == length - 1:
y = 0
else: y = y + 1
else:
if y == 0:
y = length - 1
else: y = y - 1
return
def stand(arr):
global x,y
arr[x][y] = arr[x][y] + 1000
return arr
def animate(i):
global x,y
arr=im.get_array()
walk()
#print(a)
arr = stand(arr)
im.set_array(arr)
return [im]
anim = anim.FuncAnimation(fig, animate, frames=200, interval=20, blit=True)
plt.show()
And I ran it with length = 50 and I get an animation. See it here. So you may have to play around with your color choices a bit.
I'm using Python Matplotlib to plot contours. Here's some code I have below as a basis. If you run this, you'll see that the labels are almost at vertical. I'd like to get the labels orientated horizontal, but I have no idea how can achieve this. I've tried with ClabelText, which the documentation suggests, but don't understand how this is supposed to work. I'd appreciate if someone could suggest a way to orientate the labels, either with or without ClabelText.
import itertools as it
import numpy as np
from matplotlib.ticker import FuncFormatter
from matplotlib.contour import ClabelText
import matplotlib.pyplot as plt
from math import pi, log
def getTime(data):
M = data['weight']
Tei = data['temp']
Twasser = 99.8
Teikl = 86.0 ## max allowed temp
k = 0.262 ## estimate was 0.3 W/(m.K),
Crho = 3.18 # (KJ/kgC)
const = pow(Crho, 1.0/3) / (pi*pi*k*pow(4*pi/3,2.0/3))
Tval = const*pow(M,2.0/3)*log(0.76*(Tei-Twasser)/(Teikl-Twasser))
return Tval # coo time in minutes
def contourFmt(val, posn):
mins = int(val // 1)
secs = int(val % 1 *60)
return '{0:d}mm{1:d}ss'.format(mins, secs)
def labeler(val): #is this any use??
print(val)
return
#weights = np.array(range(40, 80, 5))*1.0
#temps = np.array(range(0, 30, 5))*1.0
weights = np.arange(40.0, 80.0, 5.0)
temps = np.arange(0.0, 25.01, 5.0)
X = temps
Y = weights
Z = np.zeros((len(X), len(Y)))
xx = [{'temp':i} for i in X]
yy = [{'weight':i} for i in Y]
plt.figure()
##zz = it.product(xx,yy)
for i, xdicts in enumerate(xx):
for j, ydicts in enumerate(yy):
zd = {}
zd.update(xdicts)
zd.update(ydicts)
zval = getTime(zd)
Z[i,j] = zval
times = np.arange(4.00, 6.50, 0.25)
CS = plt.contour(Y, X, Z, levels=times, colors='b')
lbl = ClabelText(labeler)
lbl.set_rotation('horizontal')
formatter = FuncFormatter(contourFmt)
#plt.clabel(CS, inline=True, fmt=formatter, fontsize=12)
plt.clabel(CS, inline=True, use_clabeltext=True, fmt=formatter, fontsize=12)
plt.grid(True)
plt.clabel(CS, inline=1, fontsize=12)
plt.show()
You can set the rotation of the individual labels after they have been created. The label Text objects are returns by clabel, so you can store them and iterate over them, using .set_rotation(0) to orient them horizontally.
Change the last few lines of your script to:
labels1 = plt.clabel(CS, inline=True, use_clabeltext=True, fmt=formatter, fontsize=12)
labels2 = plt.clabel(CS, inline=1, fontsize=12)
for l in labels1+labels2:
l.set_rotation(0)