Python numpy addition error - python

I'm getting a very odd error using a basic shortcut method in python. It seems, unless I'm being very stupid, I get different values for A = A + B, and A += B. Here is my code:
def variance(phi,sigma,numberOfIterations):
variance = sigma
for k in range(1,numberOfIterations):
phik = np.linalg.matrix_power(phi,k)
variance = variance + phik*sigma*phik.T
return variance
This basically just calculates the covariance of a vector autoregression. So for:
phi = np.matrix('0.7 0.2 -0.1; 0.001 0.8 0.1; 0.001 0.002 0.9')
sigma = np.matrix('0.07 0.01 0.001; 0.01 0.05 0.004; 0.001 0.004 0.01')
I get:
variance(phi,sigma,10) =
[[ 0.1825225 0.07054728 0.00430524]
[ 0.07054728 0.14837229 0.02659357]
[ 0.00430524 0.02659357 0.04657858]]
This is correct I believe (agrees with Matlab). Now if I change the line above to
variance += phik*sigma*(phik.T)
I get:
variance(phi,sigma,10) =
[[ 0.34537165 0.20258329 0.04365378]
[ 0.20258329 0.33471052 0.1529369 ]
[ 0.04365378 0.1529369 0.19684553]]
Whats going on?
Many thanks
Dan

The culprit is:
variance = sigma
If you change that to:
variance = sigma.copy()
You'll see the correct result.
This is because += actually performs a (more efficient) in-place addition… And since both variance and sigma reference the same array, both will be updated. For example:
>>> sigma = np.array([1])
>>> variance = sigma
>>> variance += 3
>>> sigma
array([4])

Related

Generate random numbers for a non-standard beta distribution

I need to generate random numbers which follows a beta distribution with the following parameters. X between [0.2,2], mean = 0.60 and std = 0.15.
I already had this code but when I summarized the mean and the std of the generated vector they do not agree with the input parameters. I am afraid that something is not working properly, can someone help?
This is the code:
ac,bc=0.2,2.0
mc,sc=0.6,0.15
mc = (mc-ac)/(bc-ac)
sc = (sc**2/(bc-ac)**2)**0.5
alpha_C_Crit = mc*(mc*(1-mc)/sc**2-1)
beta_C_Crit = (1-mc)*(mc*(1-mc)/sc**2-1)
C_crit= beta.rvs(alpha_C_Crit,beta_C_Crit,loc=0.2,scale=2.0,size=100000)
The summary of the generated vector C_crit.mean()=0.6445875200893583 (not close to 0.60) and C_crit.std()=0.16723411402825206 not that close to 0.15.
I'm not spotting your error (although you seem to be squaring and then square-rooting unnecessarily). I went ahead and did my own implementation independently using numpy, and the following produces results with the target mean and standard deviation:
import numpy
my_min = 0.2
my_max = 2.0
mu = 0.6
sigma = 0.15
# Rescale distribution to range of std beta: (0,1)
scale_factor = my_max - my_min
std_mu = (mu - my_min) / scale_factor
std_sigma = sigma / scale_factor
# Convert mu/sigma to alpha/beta as used by numpy.random.beta()
nu = std_mu * (1.0 - std_mu) / (std_sigma * std_sigma) - 1.0
alpha_param = std_mu * nu
beta_param = (1.0 - std_mu) * nu
# Generate std betas, scale and translate back to original parameterization
ary = scale_factor * numpy.random.beta(alpha_param, beta_param, 100000) + my_min
Analyzing the results using JMP statistical software produced the following histogram and summary statistics:
This doesn't push close to the upper bound for the range, since values approaching 2.0 would be over nine standard deviations from the mean.

How to get rid of the ZeroDivisionError: float division by zero

I keep running into the error float division by zero and can't understand why I am getting it. However when I run the code originally given to me (written and run in matlab) no errors occur.
The Code
import numpy as np
import matplotlib.pyplot as plt
from astropy import constants as const
#Part 1: Exploring Rotation Curves
M = 10**42 #Approximate mass of the Milky Way (kg)
G = const.G #Universal gravitational constant (m^3 kg^-1 s^-2)
r = np.linspace(0, 3e20) #Radii (m)
rkpc = r*(3.24e-20) #Radii (kpc)
plt.figure(1)
plt.title('Rotation Curves for Three Mass Distributions')
v1 = np.sqrt(G * M / r) # Orbital velocity in system with central mass (m/s)
M_prop = np.linspace(0, M) # Array of masses increasing proportionally with radius
v2 = np.sqrt(G * M_prop / r)
M_dens = (M * (r / (max(r)))**3)
v3 = np.sqrt((G * M_dens) / r)
plt.plot(rkpc, v1/1000, 'b', label = 'Constant M_{r}')
plt.plot(rkpc, v2/1000, 'k', label = 'M_{r} \propto r')
plt.plot(rkpc, v3/1000, 'r', label = 'M_{r} \propto r^{3}')
I know the error is occurring due to the two following lines
M_dens = (M * (r / (max(r)))**3)
v3 = np.sqrt((G * M_dens) / r)
I assume it is happening due to the max(r) but would someone be able to shed more light on why this is happening? Potentially a fix?
Sorry if this doesn't work, I'm a bit rough with math commands like these.
In this line:
r = np.linspace(0, 3e20)
r will start as 0. Later in this line:
v3 = np.sqrt((G * M_dens) / r)
you divide by r, which is 0.
Anything divided by 0 is undefined, so Python doesn't like it and raises the error.
I'm not sure how matlab handles divide by zero but it's possible to change the numpy behaviour using np.errstate.
a = np.arange(-5, 5.)
b = np.arange(-2, 8.)
with np.errstate(divide='ignore'):
res0 = a / b
res1 = b / a
print(res0, '\n', res1)
# [ 2.5 4. -inf -2. -0.5 0. 0.25 0.4 0.5 0.57142857]
# [ 0.4 0.25 -0. -0.5 -2. inf 4. 2.5 2. 1.75]
Alternatively create a function which can set the inf, -inf results to a useful default value.
def do_div( a,b, def_val=np.inf):
with np.errstate(divide='ignore'):
res = a / b
res[ ~np.isfinite(res) ] = def_val
return res
print( do_div( a, b, 100 ))
# [ 2.5 4. 100. -2. -0.5 0. 0.25 0.4 0.5 0.57142857]
print( do_div( b, a, 100 ))
# [ 0.4 0.25 -0. -0.5 -2. 100. 4. 2.5 2. 1.75]
Setting the errstate for divide to 'ignore' suppresses the warning. Numpy returns plus or minus infinity for a divide by zero. The do_div function sets any infinity values to a default. In my work that's most often zero. I've used 100 here so it's easy to see. Matlab probably does something similar returning infinity or an alternative default value and not issuing an error or a warning.

Loglikelihood of normal distribution

I'm trying to find maximum likelihood estimate of mu and sigma from normal distribution using minimize function form scipy. However minimazation returns expected value of mean but estimate of sigma is far from real sigma.
I define function llnorm that returns negative log-likelihood of normal distribution, then create random sample from normal distribution with mean 150 and standard deviation 10, then using optimize I am trying to find MLE.
import numpy as np
import math
import scipy.optimize as optimize
def llnorm(par, data):
n = len(data)
mu, sigma = par
ll = -np.sum(-n/2 * math.log(2*math.pi*(sigma**2)) - ((data-mu)**2)/(2 * (sigma**2)))
return ll
data = 10 * np.random.randn(100) + 150
result = optimize.minimize(llnorm, [150,10], args = (data))
Even though mean of data is close to 150 and std is close to 10, optimazation returns much smaller value of estimated sigma (close to 0).
Your math is slightly off:
ll = n*math.log(2*math.pi*(sigma**2))/2 + np.sum(((data-mu)**2)/(2 * (sigma**2)))
or
ll = np.sum(math.log(2*math.pi*(sigma**2))/2 + ((data-mu)**2)/(2 * (sigma**2)))
First I cancel the -'s (not a problem), but above all either you keep the constant term in the sum and don't multiply it by n, or you take it out and multiply it by n,... but not both at the same time.
np.random.randn creates random Gaussian distribution with variance 1 (docs here). Since you aim to have distribution with std of 10, you need to multiply with 10 * 10 instead
import numpy as np
import math
import scipy.optimize as optimize
def llnorm(par, data):
n = len(data)
mu, sigma = par
ll = -np.sum(-n/2 * math.log(2*math.pi*(sigma**2)) - ((data-mu)**2)/(2 * (sigma**2)))
return ll
data = 10 * 10 * np.random.randn(100) + 150
result = optimize.minimize(llnorm, [150,10], args = (data))
print(result)
This gives me:
fun: 36328.17002555693
hess_inv: array([[ 0.96235834, -0.32116447],
[-0.32116447, 0.10879383]])
jac: array([0., 0.])
message: 'Optimization terminated successfully.'
nfev: 44
nit: 8
njev: 11
status: 0
success: True
x: array([166.27014352, 9.15113937])
EDIT: it seems like the output of ~9 is purely coincidental. Something else needs to be investigated

Accumulative sums and Standard Deviation with loops for in Python

I has tried solve this but I cannot.
I'm trying determinate standard deviation in finance, I mean:
Pr = Prob are equal [0.3, 0.4, 0.3]
r = Return are equal [0.10 ,0.05, 0.30]
So, first I calculate my average
E(r) = 0.10*0.3 + 0.4*0.05 + 0.3*0.3 = 0.14
Second, calculate my variance:
Var = 0.3*(0.1-0.14)^2 + 0.4*(0.05-0.14)^2 + 0.3*(0.3 - 0.14)^2 = 0.0114
Third, my Standard Deviation is
Var^(1/2) = 0.10677078 rounded to 0.10677
In Python, I has tried solve using basic arhitmetic but I cannot do.
My code is:
import math
def dev_stan(prob, ret):
Pro = 0
Des_Stan = 0
Var = 0
for i in range(len(ret)):
Pro += prob[i]*ret[i]
Var += (ret[i] - Pro)**2*prob[i]
Des_Stan = (math.sqrt(Var))
return Des_Stan, Var, Pro, ret, prob
x = [0.30,0.4,0.30]
y = [0.10,0.05,0.30]
print(dev_stan(x,y))
This code result in : 0.0956556 but this is not the answer.
Your problem is trying to calculate the mean and variance and standard deviation as some sort of running total, all calculated simultaneously. You can't do that with those specific formulas that you are using here. As you showed by hand, you did the mean calculation first, and only after you got the full mean did you calculate the variance, and then only after getting the variance did you calculate the standard deviation. You can't just apply that variance formula to a piece of the mean and hope things work out right.
import math
def dev_stan(prob, ret):
Pro = 0
Des_Stan = 0
Var = 0
for i in range(len(ret)):
Pro += prob[i]*ret[i]
for i in range(len(ret)):
Var += (ret[i] - Pro)**2*prob[i]
Des_Stan = (math.sqrt(Var))
return Des_Stan, Var, Pro, ret, prob
should work. Notice that the final Des_Stan has to be outside the for loop. If you want to compute a running estimate of the mean, variance and standard deviation, you will have to utilize different formulas.
For mathematical calculations in python Numpy is what you want.
import numpy as np
def dev_stan(x, y):
mean = x.dot(y)
var = np.sum(x * (y - mean) ** 2)
std = np.sqrt(var)
return mean, var, std, x, y
x = np.array([0.30,0.4,0.30])
y = np.array([0.10,0.05,0.30])
print(dev_stan(x,y))

Define an algorithm which gets a number and a list and returns a scalar based on number's distance to average of the list

Let's suppose that we have got a list which appends an integer in each iteration which is between 15, 32(let's call the integer rand). I want to design an algorithm which assigns a reward around 1 (between 1.25 and 0.75) to each rand. the rule for assigning the reward goes like this.
first we calculate the average of the list. Then if rand is more than average, we expect the reward to be less than 1, and if rand is less than average, the reward gets higher than 1. The more distance between average and rand, the more reward increases/decreases.
for example:
rand = 15, avg = 23 then reward = 1.25
rand = 32, avg = 23 then reward = 0.75
rand = 23, avg = 23 then reward = 1
and so on.
I had developed the code below for this algorithm:
import numpy as np
rollouts = np.array([])
i = 0
def modify_reward(lst, rand):
reward = 1
constant1 = 0.25
constant2 = 1
std = np.std(lst)
global avg
avg = np.mean(lst)
sub = np.subtract(avg, rand)
landa = sub / std if std != 0 else 0
coefficient = -1 + ( 2 / (1 + np.exp(-constant2 * landa)))
md_reward = reward + (reward * constant1 * coefficient)
return md_reward
while i < 100:
rand = np.random.randint(15, 33)
rollouts = np.append(rollouts, rand)
modified_reward = modify_reward(rollouts, rand)
i += 1
print([i,rand, avg, modified_reward])
# test the reward for upper bound and lower bound
rand1, rand2 = 15, 32
reward1, reward2 = modify_reward(rollouts, rand1), modify_reward(rollouts, rand2)
print(['reward for upper bound', rand1, avg, reward1])
print(['reward for lower bound', rand2, avg, reward2])
The algorithm works quite fine, but if you look at examples below, you would notice the problem with algorithm.
rand = 15, avg = 23.94 then reward = 1.17 # which has to be 1.25
rand = 32, avg = 23.94 then reward = 0.84 # which has to be 0.75
rand = 15, avg = 27.38 then reward = 1.15 # which has to be 1.25
rand = 32, avg = 27.38 then reward = 0.93 # which has to be 0.75
As you might have noticed, Algorithm doesn't consider the distance between avg and bounds (15, 32).
The more avg moves towards lower bound or higher bound, the more modified_reward gets unbalanced.
I need modified_reward to be uniformly assigned, no matter avg moves toward upper bound or lower bound.
Can anyone suggest some modification to this algorithm which could consider the distance between avg and bounds of the list.
Putting together these two requirements:
if rand is more than average, we expect the reward to be less than 1, and if rand is less than average, the reward gets higher than 1.
I need modified_reward to be uniformly assigned, no matter avg moves toward upper bound or lower bound.
is slightly tricky, depending on what you mean by 'uniformly'.
If you want 15 to always be rewarded with 1.25, and 32 to always be rewarded with 0.75, you can't have a single linear relationship while also respecting your first requirement.
If you are happy with two linear relationships, you can aim for a situation where modified_reward depends on rand like this:
which I produced with this Wolfram Alpha query. As you can see, this is two linear relationships, with a 'knee' at avg. I expect you'll be able to derive the formulae for each part without too much trouble.
This code implements a linear distribution of weights proportional to the distance from average towards your given limits.
import numpy as np
class Rewarder(object):
lo = 15
hi = 32
weight = 0.25
def __init__(self):
self.lst = np.array([])
def append(self, x):
self.lst = np.append(self.lst, [x])
def average(self):
return np.mean(self.lst)
def distribution(self, a, x, b):
'''
Return a number between 0 and 1 proportional to
the distance of x from a towards b.
Note: Modify this fraction if you want a normal distribution
or quadratic etc.
'''
return (x - a) / (b - a)
def reward(self, x):
avg = self.average()
if x > avg :
w = self.distribution(avg, x, self.hi)
else:
w = - self.distribution(avg, x, self.lo)
return 1 - self.weight * w
rollouts = Rewarder()
rollouts.append(23)
print rollouts.reward(15)
print rollouts.reward(32)
print rollouts.reward(23)
Producing:
1.25
0.75
1.0
The code in your question seems to be using np.std which I presume is an attempt to get a normal distribution. Remember that the normal distribution never actually gets to zero.
If you tell me what shape you want for the distribution we can modify Rewarder.distribution to suit.
Edit:
I can't access the paper you refer to but infer that you want a sigmoid style distribution of rewards giving a 0 at mean and approximately +/-0.25 at min and max. Using the error function as the weighting if we scale by 2 we get approximately 0.995 at min and max.
Override the Rewarder.distribution:
import math
class RewarderERF(Rewarder):
def distribution(self, a, x, b):
"""
Return an Error Function (sigmoid) weigthing of the distance from a.
Note: scaled to reduce error at max to ~0.003
ref: https://en.wikipedia.org/wiki/Sigmoid_function
"""
return math.erf(2.0 * super(RewarderERF, self).distribution(a, x, b))
rollouts = RewarderERF()
rollouts.append(23)
print rollouts.reward(15)
print rollouts.reward(32)
print rollouts.reward(23)
results in:
1.24878131454
0.75121868546
1.0
You can choose which error function suits your application and how much error you can accept at min and max. I'd also expect that you'd integrate all these functions into your class, I've split everything out so we can see the parts.
Regarding the calculating the mean, do you need to keep the list of values and recalculate each time or can you keep a count and running total of the sum? Then you would not need numpy for this calculation.
I don't understand why you are calculating md_reward like this. Please provide logic and reason. But
landa = sub / std if std != 0 else 0
coefficient = -1 + ( 2 / (1 + np.exp(-constant2 * landa)))
md_reward = reward + (reward * constant1 * coefficient)
will not give what you are looking for. Because lets consider below cases
for md_reward to be .75
--> coefficient should be -1
--> landa == -infinite (negative large value, i.e. , rand should be much larger than 32)
for md_reward to be 1
--> coefficient should be 0
--> landa == 0 (std == 0 or sub == 0) # which is possible
for md_reward to be 1.25
--> coefficient should be 1
--> landa == infinite (positive large value, i.e. , rand should be much smaller than 15)
If you want to normalize reward from avg to max and avg to min. check below links.
https://stats.stackexchange.com/questions/70801/how-to-normalize-data-to-0-1-range
https://stats.stackexchange.com/questions/70553/what-does-normalization-mean-and-how-to-verify-that-a-sample-or-a-distribution
Now modify your function with something below.
def modify_reward(lst, rand):
reward = 1
constant1 = 0.25
min_value = 15
max_value = 32
avg = np.mean(lst)
if rand >= avg:
md_reward = reward - constant1*(rand - avg)/(max_value - avg) # normalize rand from avg to max
else:
md_reward = reward + constant1*(1 - (rand - min_value)/(avg - min_value)) # normalize rand from min to avg
return md_reward
I have used below method
Normalized:
(X−min(X))/(max(X)−min(X))
for case rand >= avg
min(X) will be avg and max(X) is max_value
and for case rand < avg
min(X) in min_value and max(X) is avg
Hope this helps.
Try this
def modify_reward(lst, rand):
reward = 1
constant = 0.25 #Think of this as the +/- amount from initial reward
global avg
avg = np.mean(lst)
sub = np.subtract(avg, rand)
dreward = 0
if sub>0:
dreward = sub/(avg-15) #put your lower boundary instead of 15
elif sub<0:
dreward = sub/(32-avg) #put your higher boundary instead of 32
md_reward = reward +(dreward*constant)
return md_reward
This is the linear solution inspired by #AakashM. I don't know if this is what you were looking for, but this fits your description.

Categories