Draw multiple curves on one picture via matplotlib - python

this is my first time using this wonderful website. and I've already trapped in this question for a long time. I hope there is someone's help.
Question Details: I can't Draw multiple curves on one picture via matplotlib. I've tried using scatter rather than plot, but it still didn't work. until now, running this program can only show one curve which is green made from last plot statement. and I debug for many times, but i gotta nothing improved.my desired expectation is to have all six curves shown in one picture with corresponding colors, appreciate your help.Here is my code:
from scipy.special import comb
import matplotlib.pyplot as plt
# setting paraments
N = 100000
x = range(501)
a = range(5,11)
# define a function to traverse all x and find M
def get_P_A(a_):
M_results = []
for x_ in x:
a1 = 1
sum_P = 0.0
while (a1 <= a_):
P_A = comb(x_, a1) * comb(N - x_, 5 - a1) / comb(N, 5)
M = 1 * (1 - P_A) * N / a_ + (a_ + 1) * P_A * N / a_
sum_P += M
a1 += 1
M_results.append(sum_P)
return M_results
# traverse all a and store the result in M_total_results
M_total_results = [get_P_A(a_) for a_ in a]
# visualize calculation results
plt.plot(x,M_total_results[0],color='red',linewidth=3,linestyle='--') #When M=5, use the red line to show the result
plt.plot(x,M_total_results[1],color='blue',linewidth=3,linestyle='--') #When M=6, use the blue line to show the result
plt.plot(x,M_total_results[2],color='yellow',linewidth=3,linestyle='--') #When M=7, use the yellow line to show the result
 
plt.plot(x,M_total_results[3],color='cyan',linewidth=3,linestyle='--') #When M=8, use the cyan line to show the result
 
plt.plot(x,M_total_results[4],color='black',linewidth=3,linestyle='--') #When M=9, use the black line to show the result
plt.plot(x,M_total_results[5],color='green',linewidth=3,linestyle='--') #When M=10, use the green line to show the result
# add corresponding labels to the chart
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title("Distribution of the number of mixed samples under different positive samples",size=24)
plt.xlabel("number of positive samples/个",size=14)
plt.ylabel("M",size=14)
plt.tick_params(axis='both',size=14)
plt.grid(alpha=0.8,linestyle=':')
plt.show()

Your plotting code is fine. If you print the rows of M_total_results, you will find the curves are overlapping.
You can also check it by adjusting the alpha of the bottom curves.

Related

python need to convert a "linspace" into something more "logarithmic"

Forgive me, I'm always been very bad at math, now trying to learn some python (and some math aswell) by coding.
I have this:
import numpy as np
import matplotlib.pyplot as plt
whole = 1 # percentage: 1 = 100%, 0.1 = 10% ecc
nparts = 10 # how many "steps" we want to use
origin = 100 # starting value we
ranged = origin * whole # defining the percentage of our starting value
values = np.linspace(origin - ranged/2, origin + ranged/2, nparts * 2)
r = []
g = []
for v in values:
if v > origin:
r.append(v)
#reds = f"{v} is {100*(v - origin)/origin}% of {origin}"
#print(reds)
else:
g.append(v)
#greens = f"{v} is {100*(v - origin)/origin}% of {origin}"
#print(greens)
print("reds")
print(r)
print("greens")
print(g)
These last print(g) and print(r) output the numerical results.
If I plot this, you can clearly see what this does.
axes = plt.gca()
#origin BLUE
plt.axhline(y=origin, color='b', linestyle='-')
#reds
for i in r:
plt.axhline(y=i, color='r', linestyle='-')
#greens
for i in g:
plt.axhline(y=i, color='g', linestyle='-')
plot
So as you can see given an origin (blue line) and giving a +/- percentage whole it creates n lines ( nparts ) both reds if they are > origin and green if < origin spreading them linearly values = np.linspace(origin - ranged/2, origin + ranged/2, nparts * 2)
on this whole percentage from the origin value
now my question is: how can I spread those lines on a logarithmic way (don't get me wrong, I'm so bad at math I don't even know if what I'm looking for is logarithmic-related)
I would like to achieve something like this (I did photoshop the plotted image).
I would really love to keep that whole and maybe being able to add a new variable to "control" this logarithmic spreading
basically I need to find a way to find another function to replace values = np.linspace(origin - ranged/2, origin + ranged/2, nparts * 2) to achieve the desired result attached below.
I tried both np.geomspace and np.logspace without any success, maybe I'm just bad or maybe I need to find another way to do this.
Desired result:
desired result
Can you help me solve this out?
Thanks a lot.
You can pass any linspace to np.log. This will give the logarithm of each point. To get the result within certain bounds, you can use a linear transformation: divide by the largest value and multiply with the desired range, perhaps add a baseline value.
For example:
values = np.log(np.linspace(1, 10, nparts))
r = 150 - 50 * (values / values[-1])
g = 50 + 50 * (values / values[-1])
This will result in the following plot:
If you need to include the origin and whole variables, here's one way to do that:
values = np.log(np.linspace(1, 10, nparts))
half_range = origin * whole / 2
g = origin + half_range * (1 - values / values[-1])
r = origin * 2 - g

Matplotlib displaying axes but not datapoints

I'm trying to plot a velocity profile, with the following code. The axes are plotted, however no data points are plotted.
import pandas as pd
from matplotlib import pyplot as plt
n = 0.4
k = 53
d = 0.000264
r = 0.000132
p = 15000
u = (n/n+1)*(p*1/2*k)**(1/n)*(d**((n+1)/n) - r**((n+1)/n))
plt.plot(u)
Graph produced:
First off, note that (p*1/2*k) is a very confusing way to write a multiplication. In (about all) programming languages, the multiplications and divisions are done left to right, so, (p*1/2*k) equals (p*k/2) while perhaps you meant (p/(2*k)).
When plotting a 2D graph, you have to think what you want in the x direction, and what in the y direction. As you only give an x, there is nothing to plot. Also, plot default want to draw lines and for a line at least 2 xy pairs are needed. To only draw a point, plot accepts a third parameter, for example 'ro' to represent a red dot. Supposing u is meant to be the y direction and you don't have an x, you could give it a zero:
plt.plot(0, u, 'ro')
Now, probably you want to draw a curve of u for different values of some x. As in your equation there is no x nor a t, it is hard for me to know what you would like to see on the horizontal direction.
Let's suppose you want to show u as a function of d and that d goes from 0.0 to 0.0005. Typically, with numpy you create a sequence of values for d, lets say split into 200 small intervals: d = np.linspace(0.0, 0.0005, 200). Then, there is the magick of numpy, that when you write u = f(d), numpy makes an array for u with as many entries as d.
Example:
import numpy as np
from matplotlib import pyplot as plt
n = 0.4
k = 53
d = np.linspace(0.0, 0.0005, 200) # 0.000264
r = 0.000132
p = 15000
u = (n / n + 1) * (p * 1 / 2 * k) ** (1 / n) * (d ** ((n + 1) / n) - r ** ((n + 1) / n))
plt.plot(d, u)
plt.show()

bifurcation diagram with python

I'm a beginner and I don't speak english very well so sorry about that.
I'd like to draw the bifurcation diagram of the sequence :
x(n+1)=ux(n)(1-x(n)) with x(0)=0.7 and u between 0.7 and 4.
I am supposed to get something like this :
So, for each value of u, I'd like to calculate the accumulation points of this sequence. That's why I'd like to code something that could display every points (u;x1001),(u;x1002)...(u;x1050) for each value of u.
I did this :
import matplotlib.pyplot as plt
import numpy as np
P=np.linspace(0.7,4,10000)
m=0.7
Y=[m]
l=np.linspace(1000,1050,51)
for u in P:
X=[u]
for n in range(1001):
m=(u*m)*(1-m)
break
for l in range(1051):
m=(u*m)*(1-m)
Y.append(m)
plt.plot(X,Y)
plt.show()
And, I get a blank graphic.
This is the first thing I try to code and I don't know anything yet in Python so I need help please.
There are a few issues in your code. Although the problem you have is a code review problem, generating bifurcation diagrams is a problem of general interest (it might need a relocation on scicomp but I don't know how to request that formally).
import matplotlib.pyplot as plt
import numpy as np
P=np.linspace(0.7,4,10000)
m=0.7
# Initialize your data containers identically
X = []
Y = []
# l is never used, I removed it.
for u in P:
# Add one value to X instead of resetting it.
X.append(u)
# Start with a random value of m instead of remaining stuck
# on a particular branch of the diagram
m = np.random.random()
for n in range(1001):
m=(u*m)*(1-m)
# The break is harmful here as it prevents completion of
# the loop and collection of data in Y
for l in range(1051):
m=(u*m)*(1-m)
# Collection of data in Y must be done once per value of u
Y.append(m)
# Remove the line between successive data points, this renders
# the plot illegible. Use a small marker instead.
plt.plot(X, Y, ls='', marker=',')
plt.show()
Also, X is useless here as it contains a copy of P.
To save bifurcation diagram in png format, you can try this simple code.
# Bifurcation diagram of the logistic map
import math
from PIL import Image
imgx = 1000
imgy = 500
image = Image.new("RGB", (imgx, imgy))
xa = 2.9
xb = 4.0
maxit = 1000
for i in range(imgx):
r = xa + (xb - xa) * float(i) / (imgx - 1)
x = 0.5
for j in range(maxit):
x = r * x * (1 - x)
if j > maxit / 2:
image.putpixel((i, int(x * imgy)), (255, 255, 255))
image.save("Bifurcation.png", "PNG")

Removing coloured axis markers from Python plot

edit: to enhance code example/upload improved picture
I am using pylab to plot a graph in Python (example code shown below). The plot appears correctly, however, I can not find a way of removing the coloured axis join lines (shown on the graph below), which make the graph fairly unsightly.
I have searched the forums and not found a similar question, so any help would be appreciated.
Thank you
Code extract used for plot:
Code based on example given here: http://code.activestate.com/recipes/578256-script-that-compares-various-interest-rate-term-st/
from pylab import plot, title, xlabel, ylabel, show
r0 = 0.5 # current UK funding rate
b = 2.0 # 1 % long term interest rate
a = 0.1#speed of reversion
beta = 0.2#SD
n = 1 # number of simulation trials
T = 15. # time of projection
m = 15. # subintervals
dt = T/m # difference in time each subinterval
r = np.zeros(shape=(n, m), dtype=float) # matrix to hold short rate paths
#loop used to simulate interest rates and plot points
for i in np.arange(1,m):
r[j,i] = r[j,i-1] + a*(b-r[j,i-1])*dt + beta*sqrt(dt)*standard_normal();
plot(np.arange(0, T, dt), r[j],linestyle='--')
show()
If I understand correctly, you are just plotting all the lines for j index.
What you want is probably just r[0,:] for the first simulation. If so, after the next i j for-look, do this
figure() # create a new figure canvas
plot(np.arange(0, T, dt), r[0,:], ,linestyle='--')
Does this solve the problem?
(edit)
Then, probably the problem is that what you need is in the intermediate results. I took simply the max of intermediate result and plotted it as thicker line.
from pylab import *
r0 = 0.5 # current UK funding rate
b = 2.0 # 1 % long term interest rate
a = 0.1#speed of reversion
beta = 0.2#SD
n = 1 # number of simulation trials
T = 15. # time of projection
m = 15. # subintervals
dt = T/m # difference in time each subinterval
r = np.zeros(shape=(n, m), dtype=float) # matrix to hold short rate paths
temp = [] # to save intermediate results
j = 0
clf()
x = np.arange(0, T, dt)
#loop used to simulate interest rates and plot points
for i in np.arange(1,m):
r[j,i] = r[j,i-1] + a*(b-r[j,i-1])*dt + beta*sqrt(dt)*standard_normal()
temp.append(r[j,:])
plot(x, r[j,:],linestyle='--')
results = np.array(temp)
plot( x, results.max(axis=0), linewidth=2 )
(edit2)
actually, just the final result is the same thing as max. so
plot(x, results[-1,:])
is enough...

Method to uniformly randomly populate a disk with points in python

I have an application that requires a disk populated with 'n' points in a quasi-random fashion. I want the points to be somewhat random, but still have a more or less regular density over the disk.
My current method is to place a point, check if it's inside the disk, and then check if it is also far enough away from all other points already kept. My code is below:
import os
import random
import math
# ------------------------------------------------ #
# geometric constants
center_x = -1188.2
center_y = -576.9
center_z = -3638.3
disk_distance = 2.0*5465.6
disk_diam = 5465.6
# ------------------------------------------------ #
pts_per_disk = 256
closeness_criteria = 200.0
min_closeness_criteria = disk_diam/closeness_criteria
disk_center = [(center_x-disk_distance),center_y,center_z]
pts_in_disk = []
while len(pts_in_disk) < (pts_per_disk):
potential_pt_x = disk_center[0]
potential_pt_dy = random.uniform(-disk_diam/2.0, disk_diam/2.0)
potential_pt_y = disk_center[1]+potential_pt_dy
potential_pt_dz = random.uniform(-disk_diam/2.0, disk_diam/2.0)
potential_pt_z = disk_center[2]+potential_pt_dz
potential_pt_rad = math.sqrt((potential_pt_dy)**2+(potential_pt_dz)**2)
if potential_pt_rad < (disk_diam/2.0):
far_enough_away = True
for pt in pts_in_disk:
if math.sqrt((potential_pt_x - pt[0])**2+(potential_pt_y - pt[1])**2+(potential_pt_z - pt[2])**2) > min_closeness_criteria:
pass
else:
far_enough_away = False
break
if far_enough_away:
pts_in_disk.append([potential_pt_x,potential_pt_y,potential_pt_z])
outfile_name = "pt_locs_x_lo_"+str(pts_per_disk)+"_pts.txt"
outfile = open(outfile_name,'w')
for pt in pts_in_disk:
outfile.write(" ".join([("%.5f" % (pt[0]/1000.0)),("%.5f" % (pt[1]/1000.0)),("%.5f" % (pt[2]/1000.0))])+'\n')
outfile.close()
In order to get the most even point density, what I do is basically iteratively run this script using another script, with the 'closeness' criteria reduced for each successive iteration. At some point, the script can not finish, and I just use the points of the last successful iteration.
So my question is rather broad: is there a better way to do this? My method is ok for now, but my gut says that there is a better way to generate such a field of points.
An illustration of the output is graphed below, one with a high closeness criteria, and another with a 'lowest found' closeness criteria (what I want).
A simple solution based on Disk Point Picking from MathWorld:
import numpy as np
import matplotlib.pyplot as plt
n = 1000
r = np.random.uniform(low=0, high=1, size=n) # radius
theta = np.random.uniform(low=0, high=2*np.pi, size=n) # angle
x = np.sqrt(r) * np.cos(theta)
y = np.sqrt(r) * np.sin(theta)
# for plotting circle line:
a = np.linspace(0, 2*np.pi, 500)
cx,cy = np.cos(a), np.sin(a)
fg, ax = plt.subplots(1, 1)
ax.plot(cx, cy,'-', alpha=.5) # draw unit circle line
ax.plot(x, y, '.') # plot random points
ax.axis('equal')
ax.grid(True)
fg.canvas.draw()
plt.show()
It gives.
Alternatively, you also could create a regular grid and distort it randomly:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
n = 20
tt = np.linspace(-1, 1, n)
xx, yy = np.meshgrid(tt, tt) # create unit square grid
s_x, s_y = xx.ravel(), yy.ravel()
ii = np.argwhere(s_x**2 + s_y**2 <= 1).ravel() # mask off unwanted points
x, y = s_x[ii], s_y[ii]
triang = tri.Triangulation(x, y) # create triangluar grid
# distort the grid
g = .5 # distortion factor
rx = x + np.random.uniform(low=-g/n, high=g/n, size=x.shape)
ry = y + np.random.uniform(low=-g/n, high=g/n, size=y.shape)
rtri = tri.Triangulation(rx, ry, triang.triangles) # distorted grid
# for circle:
a = np.linspace(0, 2*np.pi, 500)
cx,cy = np.cos(a), np.sin(a)
fg, ax = plt.subplots(1, 1)
ax.plot(cx, cy,'k-', alpha=.2) # circle line
ax.triplot(triang, "g-", alpha=.4)
ax.triplot(rtri, 'b-', alpha=.5)
ax.axis('equal')
ax.grid(True)
fg.canvas.draw()
plt.show()
It gives
The triangles are just there for visualization. The obvious disadvantage is that depending on your choice of grid, either in the middle or on the borders (as shown here), there will be more or less large "holes" due to the grid discretization.
If you have a defined area like a disc (circle) that you wish to generate random points within you are better off using an equation for a circle and limiting on the radius:
x^2 + y^2 = r^2 (0 < r < R)
or parametrized to two variables
cos(a) = x/r
sin(a) = y/r
sin^2(a) + cos^2(a) = 1
To generate something like the pseudo-random distribution with low density you should take the following approach:
For randomly distributed ranges of r and a choose n points.
This allows you to generate your distribution to roughly meet your density criteria.
To understand why this works imagine your circle first divided into small rings of length dr, now imagine your circle divided into pie slices of angle da. Your randomness now has equal probability over the whole boxed area arou d the circle. If you divide the areas of allowed randomness throughout your circle you will get a more even distribution around the overall circle and small random variation for the individual areas giving you the psudo-random look and feel you are after.
Now your job is just to generate n points for each given area. You will want to have n be dependant on r as the area of each division changes as you move out of the circle. You can proportion this to the exact change in area each space brings:
for the n-th to n+1-th ring:
d(Area,n,n-1) = Area(n) - Area(n-1)
The area of any given ring is:
Area = pi*(dr*n)^2 - pi*(dr*(n-1))
So the difference becomes:
d(Area,n,n-1) = [pi*(dr*n)^2 - pi*(dr*(n-1))^2] - [pi*(dr*(n-1))^2 - pi*(dr*(n-2))^2]
d(Area,n,n-1) = pi*[(dr*n)^2 - 2*(dr*(n-1))^2 + (dr*(n-2))^2]
You could expound this to gain some insight on how much n should increase but it may be faster to just guess at some percentage increase (30%) or something.
The example I have provided is a small subset and decreasing da and dr will dramatically improve your results.
Here is some rough code for generating such points:
import random
import math
R = 10.
n_rings = 10.
n_angles = 10.
dr = 10./n_rings
da = 2*math.pi/n_angles
base_points_per_division = 3
increase_per_level = 1.1
points = []
ring = 0
while ring < n_rings:
angle = 0
while angle < n_angles:
for i in xrange(int(base_points_per_division)):
ra = angle*da + da*math.random()
rr = r*dr + dr*random.random()
x = rr*math.cos(ra)
y = rr*math.sin(ra)
points.append((x,y))
angle += 1
base_points_per_division = base_points_per_division*increase_per_level
ring += 1
I tested it with the parameters:
n_rings = 20
n_angles = 20
base_points = .9
increase_per_level = 1.1
And got the following results:
It looks more dense than your provided image, but I imagine further tweaking of those variables could be beneficial.
You can add an additional part to scale the density properly by calculating the number of points per ring.
points_per_ring = densitymath.pi(dr**2)*(2*n+1)
points_per_division = points_per_ring/n_angles
This will provide a an even better scaled distribution.
density = .03
points = []
ring = 0
while ring < n_rings:
angle = 0
base_points_per_division = density*math.pi*(dr**2)*(2*ring+1)/n_angles
while angle < n_angles:
for i in xrange(int(base_points_per_division)):
ra = angle*da + min(da,da*random.random())
rr = ring*dr + dr*random.random()
x = rr*math.cos(ra)
y = rr*math.sin(ra)
points.append((x,y))
angle += 1
ring += 1
Giving better results using the following parameters
R = 1.
n_rings = 10.
n_angles = 10.
density = 10/(dr*da) # ~ ten points per unit area
With a graph...
and for fun you can graph the divisions to see how well it is matching your distriubtion and adjust.
Depending on how random the points need to be, it may be simple enough to just make a grid of points within the disk, and then displace each point by some small but random amount.
It may be that you want more randomness, but if you just want to fill your disc with an even-looking distribution of points that aren't on an obvious grid, you could try a spiral with a random phase.
import math
import random
import pylab
n = 300
alpha = math.pi * (3 - math.sqrt(5)) # the "golden angle"
phase = random.random() * 2 * math.pi
points = []
for k in xrange(n):
theta = k * alpha + phase
r = math.sqrt(float(k)/n)
points.append((r * math.cos(theta), r * math.sin(theta)))
pylab.scatter(*zip(*points))
pylab.show()
Probability theory ensures that the rejection method is an appropriate method
to generate uniformly distributed points within the disk, D(0,r), centered at origin and of radius r. Namely, one generates points within the square [-r,r] x [-r,r], until a point falls within the disk:
do{
generate P in [-r,r]x[-r,r];
}while(P[0]**2+P[1]**2>r);
return P;
unif_rnd_disk is a generator function implementing this rejection method:
import matplotlib.pyplot as plt
import numpy as np
import itertools
def unif_rnd_disk(r=1.0):
pt=np.zeros(2)
while True:
yield pt
while True:
pt=-r+2*r*np.random.random(2)
if (pt[0]**2+pt[1]**2<=r):
break
G=unif_rnd_disk()# generator of points in disk D(0,r=1)
X,Y=zip(*[pt for pt in itertools.islice(G, 1, 1000)])
plt.scatter(X, Y, color='r', s=3)
plt.axis('equal')
If we want to generate points in a disk centered at C(a,b), we have to apply a translation to the points in the disk D(0,r):
C=[2.0, -3.5]
plt.scatter(C[0]+np.array(X), C[1]+np.array(Y), color='r', s=3)
plt.axis('equal')

Categories

Resources