how i can repeat loop for random walk? - python

I want to calculate average for random walk for 1000 times to get good average so my code for this random walk is
import math
import random
from matplotlib import pyplot
position = 0
walk = [position]
steps = 10
for i in xrange(steps):
step = 1 if random.randint(0, 1) else -1
position += step
walk.append(position)
print((walk))
pyplot.hist(walk)
pyplot.show()
so, what is best way to make python repeat it many times and calculated the average for these random walks.
Thanks

It will be easier to do if you break it down into smaller functions, for example making the main part of your code a function
def makewalk(steps):
position = 0
walk = [position]
for i in xrange(steps):
step = 1 if random.randint(0, 1) else -1
position += step
walk.append(position)
return walk # instead of simply printing it
Also, you could use inbuilt functions to reduce it to a few lines
import numpy
def makewalk(N):
steps = numpy.random.randint(0, 2, N) * 2 - 1
# an array of length N with random integers between 0 (inclusive) and 2 (exclusive)
# multiplying it by two and subtracting 1 the numbers 0 and 1 become -1 and 1 respectively
walk = numpy.cumsum(steps) # what it says, a cumulative sum
return walk
Now just loop over it 1000 times
from matplotlib import pyplot
steps = 10000
numwalks = 1000
walks = [makewalk(steps) for i in xrange(numwalks)]
There are your walks, do whatever you like with them, and since the walks are numpy arrays you can easily compute the elementwise sum without loops
averagewalk = numpy.sum(walks, 0)*1.0/numwalks # sums along the 0th axis and returns an array of length steps

Related

Give lower values a higher weight in a `randint()` function

Using randint() how do I give lower values a higher weight (higher chance to be picked)?
I have the following code:
def grab_int():
current = randint(0,10)
# I want the weight of the numbers to go down the higher they get (so I will get more 0s than 1s, more 1s than 2, etc)
OBS!
I would like to do this in a more elegant fashion than found in some other answers, (such as: Python Weighted Random). Is there a way to do this by perhaps importing some kind of weight module?
Specification:
I would like to have a randint() where the chance of it returning 0 is 30% and then it linearly deteriorates down to 10 being a 1% chance.
The following method satisfies your requirements. It uses the rejection sampling approach: Generate an integer uniformly at random, and accept it with probability proportional to its weight. If the number isn't accepted, we reject it and try again (see also this answer of mine).
import random
def weighted_random(mn, mx, mnweight, mxweight):
while True:
# Get the highest weight.
highestweight=max(mnweight,mxweight)
# Generate a uniform random integer in the interval [mn, mx].
r=random.randint(mn,mx)
# Calculate the weight for this integer. This ensures the min's
# weight is mnweight and the max's weight is mxweight
weight=mnweight+(mxweight-mnweight)*((i-mn)/(mx-mn))
# Generate a random value between 0 and the highest weight
v=random.random()*highestweight
# Is it less than this weight?
if v<weight:
# Yes, so return it
return r
# No, so try again
(Admittedly due to floating-point division as well as random.random(), which outputs floating-point numbers, the implementation is not exactly "elegant", but the example below is, once we've written it. The implementation could be improved by the use of Fractions in the fractions module.)
This method can also be implemented using the existing random.choices method in Python as follows. First we calculate the required weights for random.choices, then we pass those weights. However, this approach is not exactly efficient if the range between the minimum and maximum is very high.
import random
# Calculate weights for `random.choices`
def make_weights(mn, mx, mnweight, mxweight):
r=(mx-mn)
return [mnweight+(mxweight-mnweight)*(i/r) for i in range(mn, mx+1)]
def weighted_random(mn, mx, mnweight, mxweight):
weights=make_weights(mn, mx, mnweight, mxweight)
return random.choices(range(mn, mx+1), weights=weights)[0]
With the NumPy library this can even be implemented as follows:
import random
import numpy
def weighted_random(mn, mx, mnweight, mxweight):
return random.choices(range(mn, mx+1), \
weights=numpy.linspace(mnweight,mxweight,(mx-mn)+1))[0]
An example of the weighted_random function follows:
# Generate 100 random integers in the interval [0, 10],
# where 0 is assigned the weight 30 and 10 is assigned the
# weight 10 and numbers in between are assigned
# decreasing weights.
print([weighted_random(0,10,30,10) for i in range(100)])
The easiest way I found was to just manually assign weights:
def grab_int():
global percent
global percentLeft
global upby
# I want the weight of the numbers to go down the higher they get (so I will get more 0s than 1s, more 1s than 2, etc)
current = randint(0,100)
if current < 30:
upby = randint(1,2)
#0
elif current < 40:
upby = 1
#1
elif current < 45:
upby = 2
#2
elif current < 50:
upby = 3
#3
upby = 4
#4
elif current < 60:
upby = 5
#5
elif current < 65:
upby = 6
#6
elif current < 70:
upby = 7
#7
elif current < 75:
upby = 8
#8
elif current < 90:
upby = 9
#9
elif current < 95:
upby = 10
#10
else: # I'm dumb so I accidentally only added up to 95%, This just gives 0 a 5% higher chance without having to rewrite all the other values
upby = 0
#0

How to minimize code when there are lot of lists?

I'm making a code to simulate a Brownian motion.
from random import random
import matplotlib.pyplot as plt
import numpy as np
N=100
p=0.5
l=1
x1=[]
x2=[]
x1.append(0)
x2.append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = x1[i-l] + step
x1.append(X1)
for i in range(1, N):
step = -l if random() < p else l
X2 = x2[i-l] + step
x2.append(X2)
x1mean=np.array(x1)
x2mean=np.array(x2)
mean=[]
for j in range (0,N):
mean.append((x1mean[j]+x2mean[j])/2.0)
plt.plot(mean)
plt.plot(x1)
plt.plot(x2)
plt.show()
This code makes the displacement for 2 diferent particles, but in order to calculate the mean displacement properly, I would need to have a great number of particles, likes 100. As you can see, I'm looking for a way to condensate the code because I cannot repetat the same code 100 times.
Is there a way to create a loop that makes all this code in function of 1 variable, i.e. the number of particles?
Thanks.
I can't provide you a working python code, because until now I did not write a single line of python code. But I can give you an idea how to solve your problem.
Assumptions:
N : Number of Moves
P : Number of Particles
Step 1:
Create a method generating your array/list and returning it. So you can re-use it and avoid copying your code.
def createParticleMotion(N, p, l):
x1=[]
x1.append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = x1[i-l] + step
x1.append(X1)
return x1
Step 2:
Create a list of lists, lets call it particleMotions. The list it selves has P list of your N moves. Fill the list within a for loop for you number of particles P by calling the method from the first step and append the list paticleMotions by the returned list/array.
May be the answer for Python: list of lists will help you creating this.
Step 3:
After you created and filled particleMotions use this list within a double for loop and calculate the mean and store it in a list of means.
mean=[]
for n in range (0,N):
sum=0
for p in range (0,P):
sum = sum + particleMotions[p][n]
mean.append(sum/P)
And now you can use a next for loop to plot your result.
for particle in range (0,P):
plt.plot(particleMotions[particle])
So again don't blame me for syntax errors. I am no phyton developer. I just want to give you a way to solve your problem.
This?
from random import random
import matplotlib.pyplot as plt
import numpy as np
N=100
p=0.5
l=1
mydict = {}
for n in range(100):
mydict[n] = []
mydict[n].append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = mydict[n][i-l] + step
mydict[n].append(X1)
for k,v in mydict.iteritems():
plt.plot(v)
# mean
plt.plot([np.mean(i) for i in mydict.values()])
plt.show()

What are the errors inside this random walking code? [duplicate]

This question already has an answer here:
Random walk's weird outcome in python 3?
(1 answer)
Closed 6 years ago.
I am having unexpected outputs with the following code:
import random
N = 30 # number of steps
n = random.random() # generate a random number
x = 0
y = 0
z = 0
count = 0
while count <= N:
if n < 1/3:
x = x + 1 # move east
n = random.random() # generate a new random number
if n >= 1/3 and n < 2/3:
y = y + 1 # move north
n = random.random() # generate a new random number
if n >= 2/3:
z = z + 1 # move up
n = random.random() # generate a new random number
print("(%d,%d,%d)" % (x,y,z))
count = count + 1
When I run the code, the problem is:
Code output displays 31 coordinates, 1 more than the number of steps (N) variable.
Each iteration for 1 step should take only 1 step but it sometimes take multiple steps.
When I tested the code, the problem is ensured. To test the code, I assigned N = 1, and saw the following output:
(-1,0,1) This should be the initial step, but it took multiple steps (both x-1 and z+1), how could this happen?
(-2,0,1) Number of step variable (N) = 1 but this is the second output, why was it displayed?
Thanks for helping
N is 30, so count goes from 0 to 30. Since 30 <= 30 you will run the loop for count=0, 1, ..., 29 AND 30 which is 31 times
When you take a step, you don't ensure that another step is NOT taken. If random happens, you could enter the second or third if after already being in a previous one in the same loop iteration
You are dividing two ints which will only result in another int. So basically your code is do the following:
if n < 0:
x = x + 1 # move east
n = random.random() # generate a new random number
if n >= 0 and n < 1:
y = y + 1 # move north
n = random.random() # generate a new random number
if n >= 1:
z = z + 1 # move up
n = random.random()
fix by changing each if line to include a floating point number
if n < 1.0/3

Efficient Way to Recursively Multiply

I'm creating N_MC paths of simulated stock prices S with n points in each path, excluding the initial point. The algorithm to do so is recursive on the previous value of the stock price, for a given path. Here's what I have now:
import numpy as np
import time
N_MC = 1000
n = 10000
S = np.zeros((N_MC, n+1))
S0 = 1.0
S[:, 0] = S0
start_time_normals = time.clock()
Z = np.exp(np.random.normal(size=(N_MC, n)))
print "generate normals time = ", time.clock() - start_time_normals
start_time_prices = time.clock()
for i in xrange(N_MC):
for j in xrange(1, n+1):
S[i, j] = S[i, j-1]*Z[i, j-1]
print "pices time = ", time.clock() - start_time_prices
The times were:
generate normals time = 1.07
pices time = 9.98
Is there a much more efficient way to generate the arrays S, perhaps using Numpy's routines? It would be nice if the normal random variables Z could be generated more quickly, too, but I'm not as hopeful.
It's not necessary to loop over 'paths', because they're independent of each other. So, you can remove the outer loop for i in xrange(N_MC) and just operate on entire columns of S and Z.
For accelerating the recursive computation, let's just consider a single 'path'. Say z is vector containing the random values at each timestep (all known ahead of time). s is a vector that should contain the output at each timestep. s0 is the initial output at time zero. j is time.
Your code defines the ouput recursively:
s[j] = s[j-1]*z[j-1]
Let's expand this:
s[1] = s[0]*z[0]
s[2] = s[1]*z[1]
= s[0]*z[0]*z[1]
s[3] = s[2]*z[2]
= s[0]*z[0]*z[1]*z[2]
s[4] = s[3]*z[3]
= s[0]*z[0]*z[1]*z[2]*z[3]
Each output s[j] is given by s[0] times the product of the random values from 0 to j-1. You can calculate cumulative products like this using numpy.cumprod(), which should be much more efficient than looping:
s = np.concatenate(([s0], s0 * np.cumprod(z[0:-1])))
You can use the axis parameter for operating along one dimension of a matrix (e.g. for doing this in parallel across 'paths').

Monte Carlo Method in Python

I've been attempting to use Python to create a script that lets me generate large numbers of points for use in the Monte Carlo method to calculate an estimate to Pi. The script I have so far is this:
import math
import random
random.seed()
n = 10000
for i in range(n):
x = random.random()
y = random.random()
z = (x,y)
if x**2+y**2 <= 1:
print z
else:
del z
So far, I am able to generate all of the points I need, but what I would like to get is the number of points that are produced when running the script for use in a later calculation. I'm not looking for incredibly precise results, just a good enough estimate. Any suggestions would be greatly appreciated.
If you're doing any kind of heavy duty numerical calculation, considering learning numpy. Your problem is essentially a one-linear with a numpy setup:
import numpy as np
N = 10000
pts = np.random.random((N,2))
# Select the points according to your condition
idx = (pts**2).sum(axis=1) < 1.0
print pts[idx], idx.sum()
Giving:
[[ 0.61255615 0.44319463]
[ 0.48214768 0.69960483]
[ 0.04735956 0.18509277]
...,
[ 0.37543094 0.2858077 ]
[ 0.43304577 0.45903071]
[ 0.30838206 0.45977162]], 7854
The last number is count of the number of events that counted, i.e. the count of the points whose radius is less than one.
Not sure if this is what you're looking for, but you can run enumerate on range and get the position in your iteration:
In [1]: for index, i in enumerate(xrange(10, 15)):
...: print index + 1, i
...:
...:
1 10
2 11
3 12
4 13
5 14
In this case, index + 1 would represent the current point being created (index itself would be the total number of points created at the beginning of a given iteration). Also, if you are using Python 2.x, xrange is generally better for these sorts of iterations as it does not load the entire list into memory but rather accesses it on an as-needed basis.
Just add hits variable before the loop, initialize it to 0 and inside your if statement increment hits by one.
Finally you can calculate PI value using hits and n.
import math
import random
random.seed()
n = 10000
hits = 0 # initialize hits with 0
for i in range(n):
x = random.random()
y = random.random()
z = (x,y)
if x**2+y**2 <= 1:
hits += 1
else:
del z
# use hits and n to compute PI

Categories