Related
How would one plot these circle structures:
https://blogs.scientificamerican.com/guest-blog/making-mathematical-art/
in pyplot? I tried this:
x = np.arange(1,11)
def f(x):
return np.cos((10*np.pi*x)/14000)*(1-(1/2)*(np.square(np.cos((16*np.pi*x)/16000))))
def z(x):
return np.sin((10*np.pi*x)/14000)*(1-(1/2)*(np.square(np.cos((16*np.pi*x)/16000))))
def w(x):
return 1/200 + 1/10*np.power((np.sin(52*np.pi*x)/14000),4)
plt.ylim(-10, 10)
plt.title("Matplotlib demo")
plt.xlabel("x axis caption")
plt.ylabel("y axis caption")
x1=np.linspace(0,14000)
results=f(x1)
results2 = z(x1)
results3 = w(x1)
plt.plot(results)
plt.plot(results2)
plt.plot(results3)
plt.show()
But only get this:
Take the first example in the reported link:
So you have to do a for loop with k from 1 to N = 14000, in each iteration you draw a circle of radius R and center in (X, Y) defined in the above equations:
N = 14000
for k in range(1, N + 1):
X = cos(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
Y = sin(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
R = 1/200 + 1/10*(sin(52*pi*k/N))**4
At this point you have the coordinates of the circle's center and its radius, but not the circle iteself yet, so you you have to compute it. First of all you have to define an angle theta from 0 to 2*pi, then compute the cirle's points with:
N = 14000
theta = np.linspace(0, 2*pi, 361)
for k in range(1, N + 1):
X = cos(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
Y = sin(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
R = 1/200 + 1/10*(sin(52*pi*k/N))**4
x = R*np.cos(theta) + X
y = R*np.sin(theta) + Y
Finally, you can draw the circles in each iteration.
Complete code
import numpy as np
import matplotlib.pyplot as plt
from math import sin, cos, pi
N = 14000
theta = np.linspace(0, 2*pi, 361)
fig, ax = plt.subplots(figsize = (10, 10))
for k in range(1, N + 1):
X = cos(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
Y = sin(10*pi*k/N)*(1 - 1/2*(cos(16*pi*k/N))**2)
R = 1/200 + 1/10*(sin(52*pi*k/N))**4
x = R*np.cos(theta) + X
y = R*np.sin(theta) + Y
ax.plot(x, y, color = 'blue', linewidth = 0.1)
plt.show()
My half circle doesn't really look like how I expected.
Am I doing this right or am I missing something pretty big here ?
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
Use np.linspace to create an array for the x values. Use many points to create the circle
Use np.sqrt to solve for an array, instead of looping through each value.
import numpy as np
import matplotlib.pyplot as plt
# function for semicircle
def semicircle(r, h, k):
x0 = h - r # determine x start
x1 = h + r # determine x finish
x = np.linspace(x0, x1, 10000) # many points to solve for y
# use numpy for array solving of the semicircle equation
y = k + np.sqrt(r**2 - (x - h)**2)
return x, y
x, y = semicircle(6, 0, 0) # function call
plt.scatter(x, y, s=3, c='turquoise') # plot
plt.gca().set_aspect('equal', adjustable='box') # set the plot aspect to be equal
Answering my own question.
Plotting the output of the code:
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
[-5, 3.3166247903554]
[-4, 4.47213595499958]
[-3, 5.196152422706632]
[-2, 5.656854249492381]
[-1, 5.916079783099616]
[0, 6.0]
[1, 5.916079783099616]
[2, 5.656854249492381]
[3, 5.196152422706632]
[4, 4.47213595499958]
Looking at the output of the coordinates, it doesn't appear to be a circle.
But if we take our equation and our coordinates and graph them on this website we see that they are indeed a circle. It's an optical illusion that they aren't. Partially because the graph is not evenly displayed and also because plotting points in a range function with steps of 1 (line 13) doesn't plot points at equal arc length distances away from each other.
I would be very gratefull if anyone could help me find a faster solution to my problem.
Heres the scenario:- I have a polygon of float points which I want to map to a grid. The grid cells can be different width and height not uniformed like my image shows. ie rectangular.
I have tried using Image draw but it only uses ints. Converting floats to ints means I have to scale the floats up and remove decimal to keep some precision but image draw will not work with the larger polygon of points.
Is there a more eloquent and fast way of achieving a numpy array of ones (blue) for the filled area of the polygon and zeros (red) for the rest. I have read a little on mesh grid but cant see how it can be of use for this scenario.
Many thanks
The results from the code are
cols = 4
rows = 4
points = [[1535116L, 1725047L], [1535116L, 2125046L], [-464884L, 2125046L], [-464884L, 125046L]]
bbCut = getPythonBoundBox(points)
cutWidth = bbCut[1][0]-bbCut[0][0]
scale = float(cutWidth) / float(rows)
###Center data to origin
for p in range(len(points)):
points[p][0] -= (bbCut[1][0] - bbCut[0][0])/2
points[p][1] -= (bbCut[1][1] - bbCut[0][1])/2
points[p][0] /= scale
points[p][1] /= scale
##move points to Zero
bbCut = getPythonBoundBox(points)
for p in range(len(points)):
points[p][0] -=bbCut[0][0]
points[p][1] -=bbCut[0][1]
pointToTuple= []
for p in range(len(points)):
pointToTuple.append((points[p][0], points[p][1]))
imgWidth = float(rows)
imgHeight = float(cols)
img = Image.new('L', (int(imgWidth), int(imgHeight)), 0)
draw = ImageDraw.Draw(img)
draw.polygon(pointToTuple, fill=1)
array = np.reshape(list(img.getdata()), (cols, rows))
############This is the result from the array############
##If you compare this array to the coloured scaled image ive have drawn
##its missing a 1 on the second value in the first row
##and another 1 on the second row 3rd value
##I'm assuming there is some parsing happening here with float to int?
array([1, 0, 0, 0])
array([1, 1, 0, 0])
array([1, 1, 1, 1])
array([1, 1, 1, 1])
#########################################################
def getPythonBoundBox(points):
bigNumber = 10e10
xmin = bigNumber
xmax = -bigNumber
ymin = bigNumber
ymax = -bigNumber
g = []
a = len(points)
for i in xrange(a):
if points[i][0] < xmin: xmin = points[i][0]
if points[i][0] > xmax: xmax = points[i][0]
if points[i][1] < ymin: ymin = points[i][1]
if points[i][1] > ymax: ymax = points[i][1]
p1 = [xmin,ymin]
g.append(p1)
p2 = [xmax,ymax]
g.append(p2)
return (g)
matplotlib.path.Path has a method contains_points. Hence simply instantiate a path with your polygon points and then check your grid coordinates if they fall within that path. Your grid can have any resolution you want. This is controlled by nx and ny (or alternatively dx and dy) in the code below.
Code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# create a matplotlib path
points = [[1535116L, 1725047L],
[1535116L, 2125046L],
[-464884L, 2125046L],
[-464884L, 125046L],
[1535116L, 1725047L]]
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
path = Path(points, codes)
# check the path
fig, (ax1, ax2, ax3) = plt.subplots(1,3)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')
# create a grid
nx, ny = 1000, 1000
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]
# find points within path
img = path.contains_points(pixel_coordinates).reshape(nx,ny)
# plot
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')
# repeat, but this time specify pixel widths explicitly
dx, dy = 2000, 2000
x = np.arange(xmin, xmax, dx)
y = np.arange(ymin, ymax, dy)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]
img = path.contains_points(pixel_coordinates).reshape(len(x), len(y))
ax3.imshow(img, cmap='gray_r', interpolation='none', origin='lower')
UPDATE:
Ok, so this now tests the if any of the corners of each tile are within the path. For some reason, I still get another answer than the picture suggests. How sure are you, that the points that you provide are exact?
Code + image:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# create a matplotlib path
points = [[1535116L, 1725047L],
[1535116L, 2125046L],
[-464884L, 2125046L],
[-464884L, 125046L],
[1535116L, 1725047L]]
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
path = Path(points, codes)
fig, (ax1, ax2) = plt.subplots(1,2)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')
nx, ny = 4, 4
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_centers = np.c_[xgrid.ravel(), ygrid.ravel()]
def pixel_center_to_corners(x, y, dx, dy, precision=0.):
"""
Returns array indexed by (pixel, corner, (x,y))
"""
# make dx and dy ever so slightly smaller,
# such that the points fall **inside** the path (not **on** the path)
dx -= precision
dy -= precision
return np.array([(x - dx/2., y - dy/2.), # lower left
(x + dx/2., y - dy/2.), # lower right
(x + dx/2., y + dy/2.), # upper right
(x - dx/2., y + dy/2.), # upper left
]).transpose([2,0,1])
# get pixel corners
dx = (xmax - xmin) / float(nx)
dy = (ymax - ymin) / float(ny)
pixel_corners = pixel_center_to_corners(pixel_centers[:,0], pixel_centers[:,1], dx, dy)
# test corners of each pixel;
# set img to True, iff any corners within path;
img = np.zeros((len(pixel_corners)))
for ii, pixel in enumerate(pixel_corners):
is_inside_path = path.contains_points(pixel)
img[ii] = np.any(is_inside_path)
img = img.reshape(len(x), len(y))
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')
so my question is how to make a polar plot r = f(theta) for a function f by calculating r for a range of values of theta and then converting r and theta to Cartesian coordinates using equations x = r cos(theta) , y = r sin(theta).
BUT I need to plot the spiral r = (theta)^2 for 0 <= theta <= 10*pi
this is what I have so far....not getting a spiral here.
#! /usr/bin/env python
import matplotlib.pyplot as plt
from math import cos, sin, pi
from numpy import linspace
for theta in linspace(0,10*pi):
r = ((theta)**2)
x = r*cos(theta)
y = r*sin(theta)
plt.plot(x,y)
plt.savefig("spiral.png")
plt.show()
You need to create a list of values, not just a single point. In your case, you keep calculating x and y, but never save them anywhere. So all you are plotting is the pair (x,y) after the last iteration.
x = []
y = []
for theta in linspace(0,10*pi):
r = ((theta)**2)
x.append(r*cos(theta))
y.append(r*sin(theta))
plt.plot(x,y)
plt.show()
Output
import numpy as np
import matplotlib.pyplot as plt
theta = np.radians(np.linspace(0,360*5,1000))
r = theta**2
x_2 = r*np.cos(theta)
y_2 = r*np.sin(theta)
plt.figure(figsize=[10,10])
plt.plot(x_2,y_2)
plt.show()
I have the following problem:
a have N points on a sphere specified by a array x, with x.shape=(N,3). This array contains their cartesian coordinates. Furthermore, at each point, I have a specified temperature. This quantity is saved in an array T, with T.shape=(N,).
Is there any straight forward way to map this temperature distribution into the plane using different colors?
If it simplifies the task, the position can also be given in polar coordinates (\theta,\phi).
To plot your data, you can use Basemap. The only problem is, that both contour and contourf routines needs gridded data. Here is example with naive (and slow) IDW-like interpolation on sphere. Any comments are welcome.
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
def cart2sph(x, y, z):
dxy = np.sqrt(x**2 + y**2)
r = np.sqrt(dxy**2 + z**2)
theta = np.arctan2(y, x)
phi = np.arctan2(z, dxy)
theta, phi = np.rad2deg([theta, phi])
return theta % 360, phi, r
def sph2cart(theta, phi, r=1):
theta, phi = np.deg2rad([theta, phi])
z = r * np.sin(phi)
rcosphi = r * np.cos(phi)
x = rcosphi * np.cos(theta)
y = rcosphi * np.sin(theta)
return x, y, z
# random data
pts = 1 - 2 * np.random.rand(500, 3)
l = np.sqrt(np.sum(pts**2, axis=1))
pts = pts / l[:, np.newaxis]
T = 150 * np.random.rand(500)
# naive IDW-like interpolation on regular grid
theta, phi, r = cart2sph(*pts.T)
nrows, ncols = (90,180)
lon, lat = np.meshgrid(np.linspace(0,360,ncols), np.linspace(-90,90,nrows))
xg,yg,zg = sph2cart(lon,lat)
Ti = np.zeros_like(lon)
for r in range(nrows):
for c in range(ncols):
v = np.array([xg[r,c], yg[r,c], zg[r,c]])
angs = np.arccos(np.dot(pts, v))
idx = np.where(angs == 0)[0]
if idx.any():
Ti[r,c] = T[idx[0]]
else:
idw = 1 / angs**2 / sum(1 / angs**2)
Ti[r,c] = np.sum(T * idw)
# set up map projection
map = Basemap(projection='ortho', lat_0=45, lon_0=15)
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
# compute native map projection coordinates of lat/lon grid.
x, y = map(lon, lat)
# contour data over the map.
cs = map.contourf(x, y, Ti, 15)
plt.title('Contours of T')
plt.show()
One way to do this is to set facecolors by mapping your heat data through the colormap.
Here's an example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 80)
v = np.linspace(0, np.pi, 80)
# create the sphere surface
x=10 * np.outer(np.cos(u), np.sin(v))
y=10 * np.outer(np.sin(u), np.sin(v))
z=10 * np.outer(np.ones(np.size(u)), np.cos(v))
# simulate heat pattern (striped)
myheatmap = np.abs(np.sin(y))
ax.plot_surface(x, y, z, cstride=1, rstride=1, facecolors=cm.hot(myheatmap))
plt.show()
Here, my "heatmap" is just stripes along the y-axis, which I made using the function np.abs(np.sin(y)), but anything that goes form 0 to 1 will work (and, of course, it needs to match the shapes on x, etc.