Creating vector with one unknown using a function - python

I have a problem that I cannot get my head around although there must be a simple way to do so. Basically, I have this function:
Pr(d) = Pr(d_0) -10*n*lodg(d/d_0)
where we can ignore (for now) the Pr(d) term. Now, I want to pass the follwing dataframe:
d
0 200
1 600
2 800
3 1000
with d_0 constant. I sould actually pass it as an array using df_matrix = df.to_numpy().
What I want is to create a function
import pandas as pd
import numpy as np
from sympy import symbols, solve
from scipy.optimize import fsolve
import math
def recieved_power(pr_d0,d_0, x):
pr_d0 -10*n*math.log(d/d_0)
that will return a vector with the variable n (unknown). It should return:
-3.0102999566398116*n
-7.781512503836435*n
-9.030899869919434*n
-10.0*n
Is that possible. I cannot just multiply by n afterwards because there might be new factors in a later stage of the work.
Thanks for any insight.

I suggest you change
def recieved_power(pr_d0,d_0, x):
pr_d0 -10*n*math.log(d/d_0)
into
def recieved_power(pr_d0,d,d_0):
return lambda n: [ pr_d0 -10*n*math.log(single_d/d_0) for single_d in d]
For example, the following code snippet takes in the parameters pr_d0, d and d0 and returns a function which takes in n and outputs a list of numbers, each of which representing a single pr_d0 -10nmath.log(d/d_0) value
import math
d=[200,600,800,1000]
def recieved_power(pr_d0,d,d_0):
return lambda n: [ pr_d0 -10*n*math.log(single_d/d_0) for single_d in d]
func_list=recieved_power(0,d,d_0=1)
print(func_list(3))

Related

replicating an excel solver function in python to get required output

assuming I got cashflows from year 1 through year 4
cf = [30,45,52,67]
and discount rates (zero coupon)
rt = [.02,.03,.04,.05]
calculating PV is straight fwd in python
import numpy as np
import pandas as pd
cf = [30,45,52,67]
rt = [.02,.03,.04,.05]
sum([x[0]/(1+x[1])**(i+1) for i,x in enumerate(zip(cf,rt))])
gives me the output
173.1775
Now, if I want my NPV to be 180 (hypothetically), I will simply run a solve in excel and that will adjust my "rt" (by adding a spread across the board)
How do I replicate the same in python? I have seen/ used SciPy optimize for other purpose, but unsure how do I use it here (or if there is any other solution)
You can solve your problem using newton (an implementation of the Newton-Raphson method) from scipy.optimize.
newton needs a starting point and a function of a single parameter that evaluates to zero when you reach your target (this is not truly true, newton can accept also functions of more than one variable, but…) so we write a function that accepts your arguments and returns the function needed by newton, and at last we call newtonwith the initial value of zero
In [25]: from scipy.optimize import newton
...: cf = [30,45,52,67]
...: rt = [.02,.03,.04,.05]
...:
...: def make_fun(cf, ret, val):
...: def fun(d):
...: return val-sum([x[0]/(1+x[1]+d)**(i+1)for i,x in enumerate(zip(cf,rt))])
...: return fun
...:
...: newton(make_fun(cf, rt, 180), 0)
Out[25]: -0.014576385759418057
Edit: of course you can choose a more descriptive name for make_fun …

How to use exp() in integration function?

Here is a snippet of my code.
from scipy.integrate import quad
from numpy import exp, log, inf
def f(x):
return log(log(x))/(x*log(x**2))
val, err = quad(f, exp(), exp(2))
val
I know the code is structured correctly but I cannot format the exp() correctly. What am I doing wrong? The function should output 0.069324. Thanks ahead of time for the help!!!
Here is the answer from WolfRamAlpha:
numpy's exp is a function, not a number. You want
exp(1) = e
exp(2) = e**2
or maybe
import numpy as np
np.e
np.e**2
as your integration limits.
That said, I get
from numpy import exp, log
def f(x):
return log(log(x))/(x*log(x**2))
val, err = quad(f, exp(1), exp(2))
val
returning 0.12011325347955035
This is definitely the value of this integral. You can change variables to verify
val,err = quad(lambda x: log(x)/(2*x),1,2)
which gives the same result
Just replace exp() by exp(1) and you are good to go. By the way, once you have figured out the correct function, you can also use one liner lambda functions. Your code is perfectly fine. I thought of just sharing another possible way to implement the same thing.
f = lambda x: np.log(np.log(x))/(x*np.log(x**2))
val, err = quad(f, exp(1), 2*exp(1))

Adding items to an array in a loop

I try to add a row to a numpy.array within a loop and it's not working although I don't get any error. My general aim is to compare two files and create a third file summarizing the comparison.
ipython
import numpy as np
my arrays
aList1=np.array([['A','we'],['A','we'],['B','we'],['C','de']])
aList2=np.array([['A'],['B'],['D']])
aResult=np.array(['row1','occurence'])
my function
def coverageA(array,file1,name1,colum1,file2,name2,colum2):
x=file1[1:,colum1]
y=file2[1:,colum2]
for f in x:
if f in y:
array=np.vstack((array,np.array([f,'shared'])))
else:
array=np.vstack((array,np.array([f,name1])))
for f in y:
if f not in x:
array=np.vstack((array,np.array([f,name2])))
return
and use it this way
coverageA(aResult,alist1,'list1', 0,aList2,'list',0)
but aResult didn't change
print(aResult)
output:(['row1','occurence'])
wanted
([['row1','occurence'],['A', 'shared'],['B', 'shared'],['C','list1'],['D','list2']])
repaired:
import numpy as np
#my arrays
aList1=np.array([['A','we'],['A','we'],['B','we'],['C','de']])
aList2=np.array([['A'],['B'],['D']])
aResult=np.array(['row1','occurence'])
#my function
def coverageA(array,file1,name1,colum1,file2,name2,colum2):
x=file1[1:,colum1]
y=file2[1:,colum2]
for f in x:
if f in y:
array=np.vstack((array,np.array([f,'shared'])))
else:
array=np.vstack((array,np.array([f,name1])))
for f in y:
if f not in x:
array=np.vstack((array,np.array([f,name2])))
print(array)
return array
#and use it this way
aResult=coverageA(aResult,aList1,'list1', 0,aList2,'list2',0)
#but aResult didn't change
print(aResult)
#output:(['row1','occurence'])
#wanted
#([['row1','occurence'],['A', 'shared'],['B', 'shared'],['C','list1'],['D','list2']])
The explanation is, that in python arguments are passed by assignment, which is explained nicely here. In the line array=np.vstack((array,np.array([f,'shared']))) a new numpy array is created at a new possition im memory (array points to this), but aResult still points to its old position. You can check the memory adresses with print(id(array)).

ValueError: need more than 3 values to unpack when using optimize.minimize

I'm pretty new to python and I got stuck on this:
I'd like to use scipy.optimize.minimize to maximize a function and I'm having some problem with the extra arguments of the function I defined.
I looked for a solution in tons of answered questions but I can't find anything that solves my problem.
I saw in Structure of inputs to scipy minimize function how to pass extra arguments that one wants to be constant in the minimization of the function and my code seems fine to me from this point of view.
This is my code:
import numpy as np
from scipy.stats import pearsonr
import scipy.optimize as optimize
def min_pears_function(a,exp):
(b,c,d,e)=a
return (1-(pearsonr(b + exp[0] * c + exp[1] * d + exp[2],e)[0]))
a = (log_x,log_y,log_t,log_z) # where log_x, log_y, log_t and log_z are numpy arrays with same length
guess_PF=[0.6,2.0,0.2]
res = optimize.minimize(min_pears_function, guess_PF, args=(a,), options={'xtol': 1e-8, 'disp': True})
When running the code I get the following error:
ValueError: need more than 3 values to unpack
But I can't see what needed argument I'm missing. The function seems to work fine, so I guess the problem is in optimize.minimize call?
Your error occurs here:
def min_pears_function(a,exp):
# XXX: This is your error line
(b,c,d,e)=a
return (1-(pearsonr(b + exp[0] * c + exp[1] * d + exp[2],e)[0]))
This is because:
the initial value you pass to optimize.minimize is guessPF which has just three values ([0.6,2.0,0.2]).
this initial value is passed to min_pears_function as the variable a.
Did you mean for it to be passed as exp? Is it exp you wish to solve for? In that case, redefine the signature as:
def min_pears_function(exp, a):
...

Round in numpy to Nearest Step

I would like to know how I can round a number in numpy to an upper or lower threshold which is function of predefined step size. Hopefully stated in a clearer way, if I have the number 123 and a step size equal to 50, I need to round 123 to the closest of either 150 or 100, in this case 100. I came out with function below which does the work but I wonder if there is a better, more succint, way to do this.
Thanks in advance,
Paolo
def getRoundedThresholdv1(a, MinClip):
import numpy as np
import math
digits = int(math.log10(MinClip))+1
b = np.round(a, -digits)
if b > a: # rounded-up
c = b - MinClip
UpLow = np.array((b,c))
else: # rounded-down
c = b + MinClip
UpLow = np.array((c,b))
AbsDelta = np.abs(a - UpLow)
return UpLow[AbsDelta.argmin()]
getRoundedThresholdv1(143, 50)
The solution by pb360 is much better, using the second argument of builtin round in python3.
I think you don't need numpy:
def getRoundedThresholdv1(a, MinClip):
return round(float(a) / MinClip) * MinClip
here a is a single number, if you want to vectorize this function you only need to replace round with np.round and float(a) with np.array(a, dtype=float)
Summary: This is a correct way to do it, the top answer has cases that do not work:
def round_step_size(quantity: Union[float, Decimal], step_size: Union[float, Decimal]) -> float:
"""Rounds a given quantity to a specific step size
:param quantity: required
:param step_size: required
:return: decimal
"""
precision: int = int(round(-math.log(step_size, 10), 0))
return float(round(quantity, precision))
My reputation is too low to post a comment on the top answer from Ruggero Turra and point out the issue. However it has cases which did not work for example:
def getRoundedThresholdv1(a, MinClip):
return round(float(a) / MinClip) * MinClip
getRoundedThresholdv1(quantity=13.200000000000001, step_size=0.0001)
Returns 13.200000000000001 right back whether using numpy or the standard library round. I didn't even find this by stress testing the function. It just came up when using it in production code and spat an error.
Note full credit for this answer comes out of an open source github repo which is not mine found here
Note that round() in Ruggero Turra his answer rounds to the nearest even integer. Meaning:
a= 0.5
round(a)
Out: 0
Which may not be what you expect.
In case you want 'classical' rounding, you can use this function, which supports both scalars and Numpy arrays:
import Numpy as np
def getRoundedThresholdv1(a, MinClip):
scaled = a/MinClip
return np.where(scaled % 1 >= 0.5, np.ceil(scaled), np.floor(scaled))*MinClip
Alternatively, you could use Numpy's method digitize. It requires you to define the array of your steps. digitize will kind of ceil your value to the next step. So in order to round in a 'classical' way we need an intermediate step.
You can use this:
import Numpy as np
def getRoundedThresholdv1(a, MinClipBins):
intermediate = (MinClipBins[1:] + MinClipBins[:-1])/2
return MinClipBins[np.discritize(a, intermediate)]
You can then call it like:
bins = np.array([0, 50, 100, 150])
test1 = getRoundedThresholdv1(74, bins)
test2 = getRoundedThresholdv1(125, bins)
Which gives:
test1 = 50
test2 = 150

Categories

Resources