Related
I wrote some code that creates randomised patches from graphs in matplotlib. Basically how it works is that you create a graph from nodes taken from a circle using the parametric equation for a circle and then you randomly displace the nodes along the vector of (0,0) to the node point on the circumference of the circle. That way you can be certain to avoid lines from crossing each other once the circle is drawn. In the end you just append the first (x,y) coordinate to the list of coordinates to close the circle.
What I want to do next is to find a way to fill that circular graph with a solid colour so that I can create a "stamp" that can be used to make randomised patches on a canvas that hopefully will not create crossing edges. I want to use this to make procedural risk maps in svg format, because a lot of those are uploaded with terrible edges using raster image formats using jpeg.
I am pretty sure that my information of the nodes should be sufficient to make that happen but I have no idea how to implement that. Can anyone help?
import numpy as np
import matplotlib.pyplot as plt
def node_circle(r=0.5,res=100):
# Create arrays (x and y coordinates) for the nodes on the circumference of a circle. Use parametric equation.
# x = r cos(t) y = r sin(t)
t = np.linspace(0,2*np.pi,res)
x = r*np.cos(t)
y = r*np.sin(t)
return t,x,y
def sgn(x,x_shift=-0.5,y_shift=1):
# A shifted sign function to use as a switching function
# in order to avoid shifts lower than -0.5 which is
# the radius of the circle.
return -0.5*(np.abs(x -x_shift)/(x -x_shift)) +y_shift
def displacer(x,y,low=-0.5,high=0.5,maxrad=0.5):
# Displaces the node points of the circle
shift = 0
shift_increment = 0
for i in range(len(x)):
shift_increment = np.random.uniform(low,high)
shift += shift_increment*sgn(maxrad)
x[i] += x[i]*shift
y[i] += y[i]*shift
x = np.append(x,x[0])
y = np.append(y,y[0])
return x,y
def plot():
# Actually visualises everything
fig, ax = plt.subplots(figsize=(4,4))
# np.random.seed(1)
ax.axis('off')
t,x,y = node_circle(res=100)
a = 0
x,y = displacer(x,y,low=-0.15,high=0.15)
ax.plot(x,y,'r-')
# ax.scatter(x,y,)
plt.show()
plot()
got it: the answer is to use matplotlib.Patches.Polygon
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
def node_circle(r=0.5,res=100):
# Create arrays (x and y coordinates) for the nodes on the circumference of a circle. Use parametric equation.
# x = r cos(t) y = r sin(t)
t = np.linspace(0,2*np.pi,res)
x = r*np.cos(t)
y = r*np.sin(t)
return x,y
def sgn(x,x_shift=-0.5,y_shift=1):
# A shifted sign function to use as a switching function
# in order to avoid shifts lower than -0.5 which is
# the radius of the circle.
return -0.5*(np.abs(x -x_shift)/(x -x_shift)) +y_shift
def displacer(x,y,low=-0.5,high=0.5,maxrad=0.5):
# Displaces the node points of the circle
shift = 0
shift_increment = 0
for i in range(len(x)):
shift_increment = np.random.uniform(low,high)
shift += shift_increment*sgn(maxrad)
x[i] += x[i]*shift
y[i] += y[i]*shift
x = np.append(x,x[0])
y = np.append(y,y[0])
return x,y
def patch_distributor(M,N,res,grid='square'):
# Distribute Patches based on a specified pattern/grid.
if grid == 'square':
data = np.zeros(shape=(M,N,2,res+1))
for i in range(M):
for j in range(N):
x,y = displacer(*node_circle(res=res),low=-0.2,high=0.2)
data[i,j,0,:] = x
data[i,j,1,:] = y
return data
def plot(res):
# Actually visualises everything
fig, ax = plt.subplots(figsize=(4,4))
# np.random.seed(1)
ax.axis('off')
# x,y = node_circle(res=res)
# x,y = displacer(x,y,low=-0.15,high=0.15)
# xy = np.zeros((len(x),2))
# xy[:,0] = x
# xy[:,1] = y
patch_data = patch_distributor(10,10,res)
for i in range(patch_data.shape[0]):
for j in range(patch_data.shape[1]):
x,y = patch_data[i,j]
x += i*0.5
y += j*0.5
xy = np.zeros((len(x),2))
xy[:,0] = x
xy[:,1] = y
patch = Polygon(xy,fc='w',ec='k',lw=2,zorder=np.random.randint(2),antialiased=False)
ax.add_patch(patch)
ax.autoscale_view()
# ax.plot(x,y,'r-')
# ax.scatter(x,y,)
plt.savefig('lol.png')
plot(res=40)
# Displace circle along the line of (0,0) -> (cos(t),sin(t))
# Make the previous step influence the next to avoid jaggedness
# limit displacement level to an acceptable amount
# Random displaced cubic grid as placing points for stamps.
I'm trying to generate a regular n number of points within the volume of a sphere. I found this similar answer (https://scicomp.stackexchange.com/questions/29959/uniform-dots-distribution-in-a-sphere) on generating a uniform regular n number of points on the surface of a sphere, with the following code:
import numpy as np
n = 5000
r = 1
z = []
y = []
x = []
alpha = 4.0*np.pi*r*r/n
d = np.sqrt(alpha)
m_nu = int(np.round(np.pi/d))
d_nu = np.pi/m_nu
d_phi = alpha/d_nu
count = 0
for m in range (0,m_nu):
nu = np.pi*(m+0.5)/m_nu
m_phi = int(np.round(2*np.pi*np.sin(nu)/d_phi))
for n in range (0,m_phi):
phi = 2*np.pi*n/m_phi
xp = r*np.sin(nu)*np.cos(phi)
yp = r*np.sin(nu)*np.sin(phi)
zp = r*np.cos(nu)
x.append(xp)
y.append(yp)
z.append(zp)
count = count +1
which works as intended:
How can I modify this to generate a regular set of n points in the volume of a sphere?
Another method to do this, yielding uniformity in volume:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
dim_len = 30
spacing = 2 / dim_len
point_cloud = np.mgrid[-1:1:spacing, -1:1:spacing, -1:1:spacing].reshape(3, -1).T
point_radius = np.linalg.norm(point_cloud, axis=1)
sphere_radius = 0.5
in_points = point_radius < sphere_radius
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(point_cloud[in_points, 0], point_cloud[in_points, 1], point_cloud[in_points, 2], )
plt.show()
Output (matplotlib mixes up the view but it is a uniformly sampled sphere (in volume))
Uniform sampling, then checking if points are in the sphere or not by their radius.
Uniform sampling reference [see this answer's edit history for naiive sampling].
This method has the drawback of generating redundant points which are then discarded.
It has the upside of vectorization, which probably makes up for the drawback. I didn't check.
With fancy indexing, one could generate the same points as this method without generating redundant points, but I doubt it can be easily (or at all) vectorized.
Sample uniformly along X. For every value of X, you draw two Y from X²+Y²=1. Sample uniformly between these two Y. Then for every (X, Y) pair, you draw two Z from X²+Y²+Z²=1. Sample uniformly between these two Z.
I am trying to get a plot of a Mandelbrot set and having trouble plotting the expected plot.
As I understand, the Mandelbrot set is made up of values c, which would converge if are iterated through the following equation z = z**2 + c. I used the initial value of z = 0.
Initially, I was getting a straight line. I look for solutions online to see where I went wrong. Using the following link in particular, I attempted to improve my code:
https://scipy-lectures.org/intro/numpy/auto_examples/plot_mandelbrot.html
Here is my improved code. I don't really understand the reason of using np.newaxis and why I am plotting the final z values that converge. Am I misunderstanding the definition of the Mandelbrot set?
# initial values
loop = 50 # number of interations
div = 600 # divisions
# all possible values of c
c = np.linspace(-2,2,div)[:,np.newaxis] + 1j*np.linspace(-2,2,div)[np.newaxis,:]
z = 0
for n in range(0,loop):
z = z**2 + c
plt.rcParams['figure.figsize'] = [12, 7.5]
z = z[abs(z) < 2] # removing z values that diverge
plt.scatter(z.real, z.imag, color = "black" ) # plotting points
plt.xlabel("Real")
plt.ylabel("i (imaginary)")
plt.xlim(-2,2)
plt.ylim(-1.5,1.5)
plt.savefig("plot.png")
plt.show()
and got the following image, which looks closer to the Mandelbrot set than anything I got so far. But it looks more of a starfish with scattered dots around it.
Image
For reference, here is my initial code before improvement:
# initial values
loop = 50
div = 50
clist = np.linspace(-2,2,div) + 1j*np.linspace(-1.5,1.5,div) # range of c values
all_results = []
for c in clist: # for each value of c
z = 0 # starting point
for a in range(0,loop):
negative = 0 # unstable
z = z**2 + c
if np.abs(z) > 2:
negative +=1
if negative > 2:
break
if negative == 0:
all_results.append([c,"blue"]) #converging
else:
all_results.append([c,"black"]) # not converging
Alternatively, with another small change to the code in the question, one can use the values of z to colorize the plot. One can store the value of n where the absolute value of the series becomes larger than 2 (meaning it diverges), and color the points outside the Mandelbrot set with it:
import pylab as plt
import numpy as np
# initial values
loop = 50 # number of interations
div = 600 # divisions
# all possible values of c
c = np.linspace(-2,2,div)[:,np.newaxis] + 1j*np.linspace(-2,2,div)[np.newaxis,:]
# array of ones of same dimensions as c
ones = np.ones(np.shape(c), np.int)
# Array that will hold colors for plot, initial value set here will be
# the color of the points in the mandelbrot set, i.e. where the series
# converges.
# For the code below to work, this initial value must at least be 'loop'.
# Here it is loop + 5
color = ones * loop + 5
z = 0
for n in range(0,loop):
z = z**2 + c
diverged = np.abs(z)>2
# Store value of n at which series was detected to diverge.
# The later the series is detected to diverge, the higher
# the 'color' value.
color[diverged] = np.minimum(color[diverged], ones[diverged]*n)
plt.rcParams['figure.figsize'] = [12, 7.5]
# contour plot with real and imaginary parts of c as axes
# and colored according to 'color'
plt.contourf(c.real, c.imag, color)
plt.xlabel("Real($c$)")
plt.ylabel("Imag($c$)")
plt.xlim(-2,2)
plt.ylim(-1.5,1.5)
plt.savefig("plot.png")
plt.show()
The plot doesn't look correct, because in the code in the question z (i.e. the iterated variable) is plotted. Iterating z = z*z + c, the Mandelbrot set is given by those real, imaginary part pairs of c, for which the series doesn't diverge. Hence the small change to the code as shown below gives the correct Mandelbrot plot:
import pylab as plt
import numpy as np
# initial values
loop = 50 # number of interations
div = 600 # divisions
# all possible values of c
c = np.linspace(-2,2,div)[:,np.newaxis] + 1j*np.linspace(-2,2,div)[np.newaxis,:]
z = 0
for n in range(0,loop):
z = z**2 + c
plt.rcParams['figure.figsize'] = [12, 7.5]
p = c[abs(z) < 2] # removing c values for which z has diverged
plt.scatter(p.real, p.imag, color = "black" ) # plotting points
plt.xlabel("Real")
plt.ylabel("i (imaginary)")
plt.xlim(-2,2)
plt.ylim(-1.5,1.5)
plt.savefig("plot.png")
plt.show()
I have two sets 1__scatter_xyz.dat and 2__scatter_xyz.dat of scattered points.
These points are defined by 3 coordinates: x, y, z
1__scatter_xyz.dat : https://paste.ubuntu.com/25069931/
2__scatter_xyz.dat : https://paste.ubuntu.com/25069938/
These two sets of scattered points intersect in a region:
gnuplot> splot "1__scatter_xyz.dat" using 3:1:2 with points lt 1 title "1", "2__scatter_xyz.dat" using 3:1:2 with points lt 1 lc 2 title "2"
gnuplot> set xlabel 'x'
gnuplot> set ylabel 'y'
gnuplot> set zlabel 'z'
The crossing between the surface of set 1 with the surface of set 2 will define a line / curve, that plotted in a 2D y-xdiagram, will give us the phase boundary between these two sets.
I would like to plot in a 2D y-xdiagram this line / curve that arises from the crossing of both surfaces.
The way I thought on how to attack this problem :
We can define a new function, w = z_{1} - z_{2}.
The crossing between these two surfaces will be the points where w = (z_{1} - z_{2}) = 0.
I could then define two regions:
a) A region where w = 0
b) A region where w \neq 0
If I plot these two values of w in a 2D y-xdiagram :
I could then define that this line / curve is the phase boundary between these two sets:
a) The region where w = 0 is where both sets coexist together
b) The region where w \neq 0 is where both sets do not coexist together
Why I cannot progress with this solution:
If we just remove the blank lines on the .dat files and sort x- wise:
sed '/^\s*$/d' 1__scatter_xyz.dat | grep -v "^#" | sort -k1 -n > 1__scatter_xyz_sort_x_wise.dat
sed '/^\s*$/d' 2__scatter_xyz.dat | grep -v "^#" | sort -k1 -n > 2__scatter_xyz_sort_x_wise.dat
If you look at both x_wise.dat files, there is overlapping data:
set 1 goes from a y of -4.41 to 10.85, and set 2 goes from 8.06 to 17.64. The array of y is different on both sets. However, the array of x is the same: from 10 to 2000 with a step of 20.1.
Thus, set 1 and set 2 have the same array of x_{j}: from 10 to 2000 in a step of 20.1.
However, both sets do not have the same array of ys: there is an array y_{i}^{1} for set 1 and an array y_{i}^{2} for set 2.
In other words,
Thus, imagine that I find a point where both surfaces have the same value of z.
This point will be defined by x_{j}, y_{i}^{1} and y_{i}^{2} instead of two unique coordinates.
More efficient ideas are more than welcome.
Using scipy's griddata for this:
import numpy as np
import sys
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
# Load data:
x_1, y_1, z_1 = = np.loadtxt(./1__scatter_xyz.dat, skiprows = 1).T
x_2, y_2, z_2 = = np.loadtxt(./2__scatter_xyz.dat, skiprows = 1).T
# According to the example posted in the above scipy's griddata link,
# variables "points" and "values" are defined, so we can similarly use:
points_1 = (x_1, y_1)
points_2 = (x_2, y_2)
values_1 = (z_1)
values_2 = (z_2)
We would now have to define the grid.
As explained deeply on the post, the array of y is sampled differently on both sets.
I we carefully study the data, there is a region of overlapping between both sets on the y space:
So, continuing with this scipy's griddata example, we can set:
T_initial = 10.0
T_end = 2000.0
number_of_Ts = 100
P_initial = 8.0622
P_end = 10.8535
number_of_Ps = 100
# And then define the mesh as:
grid_T, grid_P = np.meshgrid(np.linspace(T_initial, T_end, number_of_Ts), np.linspace(P_initial, P_end, number_of_Ps))
At this point I do not know how to continue, because we can actually just define two sets of grids ?
grid_Gibbs_solid_1 = griddata(points_solid_1, values_solid_1, (grid_T, grid_P), method='cubic')
grid_Gibbs_solid_2 = griddata(points_solid_2, values_solid_2, (grid_T, grid_P), method='cubic')
Which would be the approach to follow ?
Let f(x,y) and g(x,y) denote the functions corresponding to your two surfaces. What you are looking for is to plot the contour corresponding to the equation f(x,y) == g(x,y), or, equivalently f(x,y) - g(x,y) == 0.
Matplotlib offers the function contour for this purpose. As a simple example, consider the two surfaces given by the functions
import numpy as np
def f(x, y):
return np.exp(-(x**2 + y**2))
def g(x, y):
return (3*x**2 + y**2)/16
The following snippet plots the function f-g, the (3D) contour corresponding to f-g==0 as well as its (2D) projection on the z-plane
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure(figsize = (8,8))
ax = fig.gca(projection='3d')
X = np.linspace(-2, 2, 30)
Y = np.linspace(-2, 2, 30)
X, Y = np.meshgrid(X, Y)
Z = g(X,Y)
ax.plot_surface(X, Y, f(X,Y)-g(X,Y), rstride=1, cstride=1, cmap = cm.viridis, antialiased=False, alpha = 0.5)
ax.contour(X, Y, f(X,Y) - g(X,Y), zdir='z', offset=-2, levels = [0])
ax.contour(X, Y, f(X,Y) - g(X,Y), levels = [0])
ax.set_zlim(zmin = -2)
In your case, you have data samples instead of functions. You may easily obtain (approximate) functions of the surfaces from your data by interpolation (see scipy.interpolate).
I have a problem when using hist(). I created an array with 396 entries which contains every possible outcome of
X + 0.5 Y + Z,
where 0 <= X,Z <=5 and 0 <= Y <= 10. All I want to create is a frequency histogram whose bar heights coincide with the probability that a certain value is taken under the assumption of x,y,z being independently, uniformly distributed. I thought I'd give hist() a try:
import pandas as pd
import matplotlib.pyplot as plt
def activity(x,y,z):
return 1 * x + 0.5 * y + 1 * z
rangex = np.arange(6)
rangey = np.arange(11)
rangez = np.arange(6)
rhs = 10
activities = [activity(x,y,z) for x in rangex for y in rangey for z in rangez]
activities = pd.Series(activities)
fig, axes = plt.subplots(1,1)
n, bins, patches = axes.hist(activities, bins=np.linspace(-0.25, 15.25, num=32), \
normed=True)
Here is the weird thing: n sums up to 2!!!
With the choice of bins, I made sure that each item falls into exactly 1 bin, and I know that there are exactly 31 bins necessary, because my value range is from 0 through 15 in steps of 0.5.
I am sorry I couldn't simplify this example. A random trial with 100 values yielded a correct frequency. Instead, this is what the histogram looks like:
From the picture, it is obvious that the frequencies do not sum up to 1. There are, e.g., in the center 11 bars of height 0.1 or above. The frequencies of the red plot, however, do sum up to 1.
My question: Why do I get a false normalization?
See below the code to manually calculate a correct histogram:
barposs, barheight = zip(*activities.value_counts(normalize=True).iteritems())
plt.bar(np.array(barposs) - 0.25, np.array(barheight), width=0.5, color='red')
I appreciate any useful comment on that.