The following code calculates the following vectors:
the orientation Vector (red) and two vectors (blues) which result by rotating the red vector 60 degree clock -und counterclockwise.
import matplotlib.pyplot as plt
import numpy as np
def Visualize(orienVector,vector1,vector2):
# Create figure and subplot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Plot data points
#ax.scatter(vector1[0], vector1[1], color='blue')
#ax.scatter(vector2[0], vector2[1], color='orange')
# Set limits for x and y axes
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# ===== Important bits start here =====
# Set properties of spines
ax.spines['top'].set_color('none')
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
# Set axis tick positions
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# Set specific tick locations
ax.set_xticks([-10,-5, 0, 5 , 10])
ax.set_yticks([-10,-5, 0, 5 , 10])
# ===== End Of Important Bits =====
# Draw arrows
ax.arrow(0, 0 , vector1[0][0], vector1[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , vector2[0][0], vector2[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , orienVector[0][0], orienVector[1][0],
head_width=0.03,
head_length=0.1,
lw=2,
fc='red',
ec='red',
length_includes_head=True)
plt.show()
# rotation matrix clockwise
def rotMatrixClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, -s], [s, c]])
return R
# rotation matrix clockwise
def rotMatrixCounterClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, s], [-s, c]])
return R
# center of the poit of interests POIS
POI_X = [10,12,15,17,20,50]
POI_Y = [20,30,25,22,19,35]
# position of the pedestrian
pedPostion = np.array([[3],[4]])
# range of the horisontal angel view
spanningAngle = np.radians(60)
# calculate the cone
for angle in range(0,360,5):
# calculating the component of orientation vector V0, where the length |V0| = 1
x0 = 5*np.cos(np.radians(angle))
y0 = 5*np.sin(np.radians(angle))
v0 = np.array([[x0],[y0]])
v1 = rotMatrixCounterClockWise(spanningAngle).dot(v0)
v2 = rotMatrixClockWise(spanningAngle).dot(v0)
Visualize(v0,v1,v2)
The output of this vector looks like
I'm trying to fill the area between the blue vectors to obtain a cone like the following:
The distance between the head of the cone (0,0) and the arc is always 5
However, I can't get it to work. I'm new to Matlibplot
Maybe not completely what you are looking for, but you could create a wedge between the vectors. It would be more appropriate to fit an ellipse and fill the coordinates in between. Here is an example for the wedge.
import matplotlib.pyplot as plt, numpy as np
from matplotlib import patches
v1 = np.array((1, 2))
v2 = np.array((1, -2))
base = np.array((0, 0))
theta1 = np.rad2deg(np.arctan(v1[1] / v1[0]))
theta2 = np.rad2deg(np.arctan(v2[1] / v2[0]))
fig, ax = plt.subplots()
a, b = theta1, theta2
if b > a:
a, b = b, a
artist = patches.Arc(base,
width = 1,
height = 1,
theta1 = a,
theta2 = b,
color = 'green')
ax.arrow(*base, *v1, color = 'black')
ax.arrow(*base, *v2, color = 'black')
ax.arrow(*base, *(v2 + v1), color = 'red')
wedge = patches.Wedge(base,
r = 1,
width = 1,
theta1 = theta2,
theta2 = theta1,
color = 'green')
ax.add_patch(wedge)
fig.show()
You could figure out analytically the points in the arc (a portion of a circle, so y = +- (1-x^2)**0.5), add them to the polygon defined by the origin and extremities of red and yellow vectors, then use https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill.html.
An example of filling a polygon is here: https://matplotlib.org/stable/gallery/lines_bars_and_markers/fill.html#sphx-glr-gallery-lines-bars-and-markers-fill-py
Related
I'm looking for a way to assign color to line plots in matplotlib in a way that's responsive to the line's angle. This is my current code:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
horz = [[0.5,0.6,0.8],[0.1,0.8,0.9],[0.2,0.5,0.9]]
vert = [[0.1,0.2,0.3],[0.05,0.1,0.15],[0.2,0.3,0.35]]
f = plt.figure(figsize=(6,6))
ax = plt.axes()
for column in range(0,len(horz)):
x = np.array(horz[column])
y = np.array(vert[column])
#LINEAR TRENDLINE
z = np.polyfit(horz[column], vert[column], 1)
p = np.poly1d(z)
ax.plot(horz[column],p(horz[column]),"-")
plt.arrow(x=horz[column][-2],y=p(horz[column])[-2],dx=(horz[column][-1]-horz[column][-2]),dy=(p(horz[column])[-1]-p(horz[column])[-2]), shape='full', lw=.01,
length_includes_head=True, head_width=.012, head_length=0.02, head_starts_at_zero=False, overhang = 0.5)
#FIG SETTINGS
plt.xlim([0, 1])
plt.ylim([0.1,0.5])
ax.set_title('Title',
fontsize = 14)
The idea here would be that if the line is at 0 degrees, it would be at one end of a given gradient, and if it were at 90 degrees, at the other end. Additionally, I'd like the line length to be taken as the intensity of the color. So if the line is short, it'd be closer to white, and if the line is long, it'd be closer to the raw color from the gradient.
Managed to solve it myself. Used pretty simple formulas for calculating the lines' slopes and distances and then used these as input for the color mapping and alpha transparency attribute.
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors as colors
import numpy as np
%matplotlib inline
#Data
horz = [[0.5,0.6,0.8],[0.1,0.3,0.4],[0.2,0.5,0.9],[0.9,0.95,0.95]]
vert = [[0.1,0.2,0.45],[0.05,0.1,0.15],[0.2,0.3,0.35],[0.1,0.3,0.5]]
#Slope calculation
def slopee(x1,y1,x2,y2):
x = (y2 - y1) / (x2 - x1)
return x
#Color set up
cmap = plt.cm.coolwarm_r
#0 means a horizontal line, 1 means a line at 45 degrees, infinite means a vertical line (2 is vertical enough)
cNorm = colors.Normalize(vmin=0, vmax=2)
scalarMap = cm.ScalarMappable(norm=cNorm,cmap=cmap)
#Fig settings
f = plt.figure(figsize=(6,6))
ax = plt.axes()
for column in range(0,len(horz)):
x = np.array(horz[column])
y = np.array(vert[column])
#LINEAR TRENDLINE
# 1 LINEAR
# >=2 POLINOMIAL
z = np.polyfit(horz[column], vert[column], 1)
p = np.poly1d(z)
#Distance calc formula
def calculateDistance(x1,y1,x2,y2):
dist = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
return dist
#Set up max an min distances
maxdist = calculateDistance(0,0,0,0.9)
mindist = calculateDistance(0,0,0,0)
#Calculate line slope
slope = slopee(horz[column][0],p(horz[column])[0],horz[column][-1],p(horz[column])[-1])
#Not interested in any slopes going "down"
if slope >=0:
#Map colors based on slope (0-2)
colorVal = scalarMap.to_rgba(slope)
#Map transparency based on distance
transparency = (calculateDistance(horz[column][0],p(horz[column])[0],horz[column][-1],p(horz[column])[-1])-mindist)/(maxdist-mindist)
#Set up minimun transparency to be 50% instead of 0%
transparency = (0.5*transparency) + 0.5
#The actual arrow plot
plt.arrow(x=horz[column][0],y=p(horz[column])[0],dx=(horz[column][-1]-horz[column][0]),dy=(p(horz[column])[-1]-p(horz[column])[0]), shape='full',length_includes_head=True, head_starts_at_zero=False, lw=.5, head_width=.011, head_length=0.01, overhang = 0.5, color=colorVal,alpha=transparency)
#FIG SETTINGS
plt.xlim([0, 1])
plt.ylim([0,0.5])
ax.set_title('Title',fontsize = 14)
Congrats on solving it yourself. I had put this together before I realized you had posted your answer. Very similar approach:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from math import sqrt
plt.rcParams["figure.figsize"] = (15,15)
# Create a color mapper for degress to color
values = np.linspace(1.0, 90.0, 90)
norm = colors.Normalize(vmin=0.0, vmax=90.0, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap=cm.coolwarm_r)
horz = [[0.5,0.6,0.8],[0.1,0.3,0.4],[0.2,0.5,0.9],[0.9,0.95,0.95]]
vert = [[0.1,0.2,0.45],[0.05,0.1,0.15],[0.2,0.3,0.35],[0.1,0.3,0.5]]
f = plt.figure(figsize=(15,15))
ax = plt.axes()
# Calculate lengths of each line
lengths = [sqrt((x[-1]-x[0])**2 + (y[-1]-y[0])**2) for x,y in zip(horz, vert)]
for x,y,length in zip(horz,vert, lengths):
alpha = length / sum(lengths)
angle = np.rad2deg(np.arctan2(y[-1] - y[0], x[-1] - x[0]))
color = mapper.to_rgba(angle)
plt.arrow(x[0],y[0],x[-1]-x[0], y[-1]-y[0],shape='full', lw=2,
length_includes_head=True, head_width=.012, head_length=0.02, head_starts_at_zero=False, overhang = 0.5, alpha=alpha, color=color)
Given a center and two angles of a rotated ellipse of Arc from matplotlib.patches, I want to plot the two lines starting from the center of the Arc to the ends of the Arc.
Here is a piece of code that does that:
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
fig, ax = plt.subplots(1,1)
r = 2 #Radius
a = 0.2*r #width
b = 0.5*r #height
#center of the ellipse
x = 0.5
y = 0.5
ax.add_patch(Arc((x, y), a, b, angle = 20,
theta1=0, theta2=120,
edgecolor='b', lw=1.1))
#Now look for the ends of the Arc and manually set the limits
ax.plot([x,0.687],[y,0.567], color='r',lw=1.1)
ax.plot([x,0.248],[y,0.711], color='r',lw=1.1)
plt.show()
Which results in
.
Here the red lines were drawn looking carefully at the ends of the arc. However, as Arc does not allow to fill the arc for optimization, I wonder if there is a way to do it automatically for any center and angles.
According to Wikipedia an ellipse in its polar form looks like
Using this you may calculate the end points of the lines.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
fig, ax = plt.subplots(1,1)
r = 2 #Radius
a = 0.2*r #width
b = 0.5*r #height
#center of the ellipse
x = 0.5; y = 0.5
# angle
alpha = 20
ax.add_patch(Arc((x, y), a, b, angle = alpha,
theta1=0, theta2=120,
edgecolor='b', lw=1.1))
def ellipse(x0,y0,a,b,alpha,phi):
r = a*b/np.sqrt((b*np.cos(phi))**2 + (a*np.sin(phi))**2)
return [x0+r*np.cos(phi+alpha), y0+r*np.sin(phi+alpha)]
x1,y1 = ellipse(x, y, a/2., b/2., np.deg2rad(alpha), np.deg2rad(0))
ax.plot([x,x1],[y,y1], color='r',lw=1.1)
x2,y2 = ellipse(x, y, a/2., b/2., np.deg2rad(alpha), np.deg2rad(120))
ax.plot([x,x2],[y,y2], color='r',lw=1.1)
ax.set_aspect("equal")
plt.show()
Suppose I have a circle x**2 + y**2 = 20.
Now I want to plot the circle with n_dots number of dots in the circles perimeter in a scatter plot. So I created the code like below:
n_dots = 200
x1 = np.random.uniform(-20, 20, n_dots//2)
y1_1 = (400 - x1**2)**.5
y1_2 = -(400 - x1**2)**.5
plt.figure(figsize=(8, 8))
plt.scatter(x1, y1_1, c = 'blue')
plt.scatter(x1, y1_2, c = 'blue')
plt.show()
But this shows the dots not uniformly distributed all the places in the circle. The output is :
So how to create a circle with dots in scatter plot where all the dots are uniformly distributed in the perimeter of the circle?
A simple way to plot evenly-spaced points along the perimeter of a circle begins with dividing the whole circle into equally small angles where the angles from circle's center to all points are obtained. Then, the coordinates (x,y) of each point can be computed. Here is the code that does the task:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(8, 8))
n_dots = 120 # set number of dots
angs = np.linspace(0, 2*np.pi, n_dots) # angles to the dots
cx, cy = (50, 20) # center of circle
xs, ys = [], [] # for coordinates of points to plot
ra = 20.0 # radius of circle
for ang in angs:
# compute (x,y) for each point
x = cx + ra*np.cos(ang)
y = cy + ra*np.sin(ang)
xs.append(x) # collect x
ys.append(y) # collect y
plt.scatter(xs, ys, c = 'red', s=5) # plot points
plt.show()
The resulting plot:
Alternately, numpy's broadcasting nature can be used and shortened the code:
import matplotlib.pyplot as plt
import numpy as np
fig=plt.figure(figsize=(8, 8))
n_dots = 120 # set number of dots
angs = np.linspace(0, 2*np.pi, n_dots) # angles to the dots
cx, cy = (50, 20) # center of circle
ra = 20.0 # radius of circle
# with numpy's broadcasting feature...
# no need to do loop computation as in above version
xs = cx + ra*np.cos(angs)
ys = cy + ra*np.sin(angs)
plt.scatter(xs, ys, c = 'red', s=5) # plot points
plt.show()
for a very generalized answer that also works in 2D:
import numpy as np
import matplotlib.pyplot as plt
def u_sphere_pts(dim, N):
"""
uniform distribution points on hypersphere
from uniform distribution in n-D (<-1, +1>) hypercube,
clipped by unit 2 norm to get the points inside the insphere,
normalize selected points to lie on surface of unit radius hypersphere
"""
# uniform points in hypercube
u_pts = np.random.uniform(low=-1.0, high=1.0, size=(dim, N))
# n dimensional 2 norm squared
norm2sq = (u_pts**2).sum(axis=0)
# mask of points where 2 norm squared < 1.0
in_mask = np.less(norm2sq, np.ones(N))
# use mask to select points, norms inside unit hypersphere
in_pts = np.compress(in_mask, u_pts, axis=1)
in_norm2 = np.sqrt(np.compress(in_mask, norm2sq)) # only sqrt selected
# return normalized points, equivalently, projected to hypersphere surface
return in_pts/in_norm2
# show some 2D "sphere" points
N = 1000
dim = 2
fig2, ax2 = plt.subplots()
ax2.scatter(*u_sphere_pts(dim, N))
ax2.set_aspect('equal')
plt.show()
# plot histogram of angles
pts = u_sphere_pts(dim, 1000000)
theta = np.arctan2(pts[0,:], pts[1,:])
num_bins = 360
fig1, ax1 = plt.subplots()
n, bins, patches = plt.hist(theta, num_bins, facecolor='blue', alpha=0.5)
plt.show()
similar/related:
https://stackoverflow.com/questions/45580865/python-generate-an-n-dimensional-hypercube-using-rejection-sampling#comment78122144_45580865
Python Uniform distribution of points on 4 dimensional sphere
http://mathworld.wolfram.com/HyperspherePointPicking.html
Sampling uniformly distributed random points inside a spherical volume
I am trying to remove the edge color in the plot of a cylinder where I have set an alpha and facecolors. However, if I also set the facecolors, I can still see the edge colors. If I remove the alpha = 0.5 statement then the problem is resolved, however I need the alpha to be <1 . Here is an example:
You can still see the blue edgecolors even tough I have set the edgecolor to None.
This is the code where I use plot_surface()
ax.plot_surface(X, Y,Z, edgecolor = "None", facecolors = col1, alpha = 0.5)
Yet the edge colors are still there? However, if I remove the facecolors statement inside plot_surface() then the edge colors are no longer there. Here is the complete code:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.linalg import norm
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import random
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
origin = np.array([0, 0, 0])
#axis and radius
p0 = np.array([0, 0, 0])
p1 = np.array([8, 8, 8])
R = 4
#vector in direction of axis
v = p1 - p0
#find magnitude of vector
mag = norm(v)
#unit vector in direction of axis
v = v / mag
#make some vector not in the same direction as v
not_v = np.array([1, 0, 0])
if (v == not_v).all():
not_v = np.array([0, 1, 0])
#make vector perpendicular to v
n1 = np.cross(v, not_v)
#normalize n1
n1 /= norm(n1)
#make unit vector perpendicular to v and n1
n2 = np.cross(v, n1)
#surface ranges over t from 0 to length of axis and 0 to 2*pi
t = np.linspace(0, mag, 200)
theta = np.linspace(0, 2 * np.pi, 100)
#use meshgrid to make 2d arrays
t, theta = np.meshgrid(t, theta)
#generate coordinates for surface
X, Y, Z = [p0[i] + v[i] * t + R * np.sin(theta) * n1[i] + R * np.cos(theta) * n2[i] for i in [0, 1, 2]]
col1 = plt.cm.Blues(np.linspace(0,1,200)) # linear gradient along the t-axis
col1 = np.repeat(col1[np.newaxis,:, :], 100, axis=0) # expand over the theta- axis
ax.plot_surface(X, Y,Z, edgecolor = None, facecolors = col1, alpha = 0.5)
#plot axis
ax.plot(*zip(p0, p1), color = 'red')
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.axis('off')
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
plt.show()
Setting linewidth=0 in plot_surface() solves this problem:
ax.plot_surface(X, Y, Z, edgecolor=None, facecolors=col1, alpha=0.5, linewidth=0)
p.s.: I didn't find this worth an answer, but per: Question with no answers, but issue solved in the comments (or extended in chat), I added it as a quick answer so the question can be marked as solved
Here is my code to plot some data:
from scipy.interpolate import griddata
from numpy import linspace
import matplotlib.pyplot as plt
meanR = [9.95184937, 9.87947708, 9.87628496, 9.78414422,
9.79365258, 9.96168969, 9.87537519, 9.74536093,
10.16686878, 10.04425475, 10.10444126, 10.2917172 ,
10.16745917, 10.0235203 , 9.89914 , 10.11263505,
9.99756449, 10.17861254, 10.04704248]
koord = [[1,4],[3,4],[1,3],[3,3],[2,3],[1,2],[3,2],[2,2],[1,1],[3,1],[2,1],[1,0],[3,0],[0,3],[4,3],[0,2],[4,2],[0,1],[4,1]]
x,y=[],[]
for i in koord:
x.append(i[0])
y.append(i[1])
z = meanR
xi = linspace(-2,6,300);
yi = linspace(-2,6,300);
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
plt.scatter(x,y,marker='o',c='b',s=15)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
plt.show()
In result we have:
How can I inscribe it in a circle? something like this
Because you don't seem to need any axes you can also use a normal projection, remove the axes and draw a circle. I had some fun and added some bonus ears, a nose and a color bar. I annotated the code, I hope it is clear.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import scipy.interpolate
import numpy
import matplotlib
import matplotlib.pyplot as plt
# close old plots
plt.close("all")
# some parameters
N = 300 # number of points for interpolation
xy_center = [2,2] # center of the plot
radius = 2 # radius
# mostly original code
meanR = [9.95184937, 9.87947708, 9.87628496, 9.78414422,
9.79365258, 9.96168969, 9.87537519, 9.74536093,
10.16686878, 10.04425475, 10.10444126, 10.2917172 ,
10.16745917, 10.0235203 , 9.89914 , 10.11263505,
9.99756449, 10.17861254, 10.04704248]
koord = [[1,4],[3,4],[1,3],[3,3],[2,3],[1,2],[3,2],[2,2],[1,1],[3,1],[2,1],[1,0],[3,0],[0,3],[4,3],[0,2],[4,2],[0,1],[4,1]]
x,y = [],[]
for i in koord:
x.append(i[0])
y.append(i[1])
z = meanR
xi = numpy.linspace(-2, 6, N)
yi = numpy.linspace(-2, 6, N)
zi = scipy.interpolate.griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
# set points > radius to not-a-number. They will not be plotted.
# the dr/2 makes the edges a bit smoother
dr = xi[1] - xi[0]
for i in range(N):
for j in range(N):
r = numpy.sqrt((xi[i] - xy_center[0])**2 + (yi[j] - xy_center[1])**2)
if (r - dr/2) > radius:
zi[j,i] = "nan"
# make figure
fig = plt.figure()
# set aspect = 1 to make it a circle
ax = fig.add_subplot(111, aspect = 1)
# use different number of levels for the fill and the lines
CS = ax.contourf(xi, yi, zi, 60, cmap = plt.cm.jet, zorder = 1)
ax.contour(xi, yi, zi, 15, colors = "grey", zorder = 2)
# make a color bar
cbar = fig.colorbar(CS, ax=ax)
# add the data points
# I guess there are no data points outside the head...
ax.scatter(x, y, marker = 'o', c = 'b', s = 15, zorder = 3)
# draw a circle
# change the linewidth to hide the
circle = matplotlib.patches.Circle(xy = xy_center, radius = radius, edgecolor = "k", facecolor = "none")
ax.add_patch(circle)
# make the axis invisible
for loc, spine in ax.spines.iteritems():
# use ax.spines.items() in Python 3
spine.set_linewidth(0)
# remove the ticks
ax.set_xticks([])
ax.set_yticks([])
# Add some body parts. Hide unwanted parts by setting the zorder low
# add two ears
circle = matplotlib.patches.Ellipse(xy = [0,2], width = 0.5, height = 1.0, angle = 0, edgecolor = "k", facecolor = "w", zorder = 0)
ax.add_patch(circle)
circle = matplotlib.patches.Ellipse(xy = [4,2], width = 0.5, height = 1.0, angle = 0, edgecolor = "k", facecolor = "w", zorder = 0)
ax.add_patch(circle)
# add a nose
xy = [[1.5,3], [2,4.5],[2.5,3]]
polygon = matplotlib.patches.Polygon(xy = xy, facecolor = "w", zorder = 0)
ax.add_patch(polygon)
# set axes limits
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
plt.show()
If you replace the part where you do the plotting with:
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
CS = ax.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
ax.scatter(x,y,marker='o',c='b',s=15)
ax.set_xlim(min(x),max(x))
ax.set_ylim(min(y),max(y))
you get this
To get what you want, you have to rescale the x, y, xi, yi such that the image is centered in zero. You might also need to convert to polar coordinates. Now I don't have time to provide more info, but I hope that this helps you in getting started