Just a little error in my fuction general_poly - python

Hello everybody I almost correct with my code and here it is:
def general_poly (L):
""" L, a list of numbers (n0, n1, n2, ... nk)
Returns a function, which when applied to a value x, returns the value
n0 * x^k + n1 * x^(k-1) + ... nk * x^0 """
def inner(x):
L.reverse()
return sum(e*x**L.index(e) for e in L)
return inner
but gives an error when I test 4 should give 189 but give me 18
Thanks

Fixing inner
There are a couple things in your method that should be cleaned up. First of all, when you call L.reverse(), you are actually reversing the list that is referenced by the variable passed into general_poly. You should use reversed which is an iterable, copy of the original list, reversed.
def general_poly(L):
""" ... """
def inner(x):
enum_iter = enumerate(reversed(L))
return sum(e * pow(x, i) for i, e in enum_iter)
return inner
Also, you'll see that I've wrapped the reversed(L) call in enumerate which returns tuples containing the index and the element in the given iterable value. This means you don't need to use the L.index() method in ever loop iteration, saving you lookup time. Also, personally I prefer using pow over the ** syntax because I think it reads a little more clearly, but that is just my preference.
Testing general_poly
Let's use an example to test this. If I generate a poly function with [1, 2, 3] it should return a function representing the equation x^2 + 2x + 3.
func = general_poly([1, 2, 3])
print(func(3)) # 18
print(func(4)) # 27
print(func(5)) # 38

Related

"TypeError: <lambda>() takes 1 positional argument but 2 were given" using reduce()

I want to return sum of square of numbers passed in list.
from functools import reduce
def square_sum(numbers):
return reduce(lambda x: x ** 2, numbers)
print(square_sum([1, 2, 2]))
However i am getting the error: TypeError: <lambda>() takes 1 positional argument but 2 were given.
I couldn't understand reason behind it.
Here's how you might define sum if it didn't exist:
from functools import reduce
def sum(it):
return reduce(lambda acc, val: acc + val, it)
Or:
from functools import reduce
import operator
def sum(it):
return reduce(operator.add, it)
functools.reduce reduces the values produced by an iterator to a single value by repeatedly combining consecutive values using the function you provide. So the function needs to be able to combine two values and therefore must take two arguments.
So you could define sum_of_squares using reduce, like this, although there are a lot of corner cases to cope with:
from functools import reduce
def sum_of_squares(it):
it = iter(it)
try:
first = next(it)
except StopIteration:
return 0
return reduce(lambda acc, val: acc + val * val,
it,
first * first)
Personally, I think the following is clearer:
def sum_of_squares(it):
return sum(map(lambda x: x ** 2, it))
The function parameter to reduce() should take two arguments: an old one and a new one. To sum, you'd just need to add them together:
lambda r, x: x**2 + r
However, that doesn't actually do what you want because the first element never gets squared (so it doesn't get the right answer if the first element is >1). You might be thinking reduce() is like sum(map()):
def square_sum(numbers):
return sum(map(lambda x: x**2, numbers))
But it's more readable to replace the map with a generator expression:
def square_sum(numbers):
return sum(x**2 for x in numbers)
print(square_sum([1, 2, 2])) # -> 9

Python- np.random.choice

I am using the numpy.random.choice module to generate an 'array' of choices based on an array of functions:
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base=[f, g]
funcs=np.random.choice(base,size=2)
This code will produce an 'array' of 2 items referencing a function from the base array.
The reason for this post is, I have printed the outcome of funcs and recieved:
[<function f at 0x00000225AC94F0D0> <function f at 0x00000225AC94F0D0>]
Clearly this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it, this is where the problem comes in. I want to change the choice of function, so that it is no longer random and instead depends on some conditions, so it might be:
for i in range(2):
if testvar=='true':
choice[i] = 0
if testvar== 'false':
choice[i] = 1
This would return an array of indicies to be put in later function
The problem is, the further operations of the code (I think) require this previous form of function reference: [ ] as an input, instead of a simple array of 0,1 Indicies and I don't know how I can get an array of form [ ] by using if statements.
I could be completely wrong about the rest of the code requiring this input, but I don't know how I can amend it, so am hence posting it here. The full code is as follows: (it is a slight variation of code provided by #Attack68 on Evolving functions in python) It aims to store a function that is multiplied by a random function on each iteration and integrates accordingly. (I have put a comment on the code above the function that is causing the problem)
import numpy as np
import scipy.integrate as int
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = np.random.choice(base, size=2)
print(funcs)
#The below function is where I believe the [<function...>] input to be required
def apply(x, funcs):
y = 1
for func in funcs:
y *= func(x)
return y
print('function value at 1.5 ', apply(1.5, funcs))
answer = int.quad(apply, 1, 2, args=(funcs,))
print('integration over [1,2]: ', answer)
Here is my attempt of implementing a non-random event:
import numpy as np
import scipy.integrate as int
import random
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = list()
for i in range(2):
testvar=random.randint(0,100) #In my actual code, this would not be random but dependent on some other situation I have not accounted for here
if testvar>50:
func_idx = 0 # choose a np.random operation: 0=f, 1=g
else:
func_idx= 1
funcs.append(func_idx)
#funcs = np.random.choice(base, size=10)
print(funcs)
def apply(x, funcs):
y = 1
for func in funcs:
y *= func(x)
return y
print('function value at 1.5 ', apply(1.5, funcs))
answer = int.quad(apply, 1, 2, args=(funcs,))
print('integration over [1,2]: ', answer)
This returns the following error:
TypeError: 'int' object is not callable
If: You are trying to refactor your original code that operates on a list of randomly chosen functions to a version that operates with random indices which correspond to items in a list of functions. Refactor apply.
def apply(x,indices,base=base):
y = 1
for i in indices:
f = base[i]
y *= f(x)
return y
...this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it...
Functions are objects, the list contains a reference to the objects themselves. They can be used by either assigning them to a name then calling them or indexing the list and calling the object:
>>> def f():
... return 'f'
>>> def g():
... return 'g'
>>> a = [f,g]
>>> q = a[0]
>>> q()
'f'
>>> a[1]()
'g'
>>> for thing in a:
print(thing())
f
g
Or you can pass them around:
>>> def h(thing):
... return thing()
>>> h(a[1])
'g'
>>>
If you still want to use your function apply as-is, you need to keep your input a list of functions. Instead of providing a list of indices, you can use those indices to create your list of functions.
Instead of apply(1.5, funcs), try:
apply(1.5, [base(n) for n in funcs])

Higher Order of Function (Map)

I am slightly confused by map in python. The function for mapaccepts 2 parameters: `map(function, variables).
Why is the code below able to take in multiply and add as variables but the second code isn't able to? In a usual case, multiply should be passed in in as a function, check out range.
def multiply(x):
return x * x
def add(x):
return x + x
funcs = [multiply, add]
for i in range (1, 5):
value = list(map(lambda x: x(i), funcs))
print(value)
This is the second code:
def multiply(x):
return x * x
def add(x):
return x + x
funcs = (add, multiply)
multi_func = (multiply)
for i in range (1, 5):
value = list(map(lambda x: x(i), multi_func))
print(value)
Is it possible to make use of 1 function and still use for in range?
Using range:
map(multiply, range(1, 5))
map applies its first argument, which is a function, to each element of the iterable which is the second argument. The function is applied lazily. That means it's done only when you iterate over the map object, e.g., when you create a list of of it.
Let's take a look at your first code. funcs = [multiply, add] creates a list, which is iterable, of two elements. Both elements are functions. This is normal in Python, because functions are just regular objects, and can be passed around, assigned, have attributes, etc. The loop
for i in range (1, 5):
value = list(map(lambda x: x(i), funcs))
print(value)
Repeats form 1 to 4. At each iteration it maps lambda x: x(i) to the functions in funcs. When i = 1, the map ends up doing multiply(1), add(1). When i = 2, it's multiply(2), add(2), and so on.
The second code doesn't work because of a typo. (x) is just x, but (x,) is a one-element tuple whose first element is x. map requires the second argument to be iterable, so passing in a function won't do. If you want to map to a single function, you need to supply an iterable with one element: multi_func = (multiply,).
Once corrected, the second version will print multiply(1) when i = 1, multiply(2) when i = 2, etc.
Something like list(map(multiply, range(1, 5))) will in fact be an easier way to write the second version. You can also do something similar with the first code, using zip:
zip(map(func, range(1, 5)) for func in funcs)

How to write a function as range?

I need to use a function as range but an error appears saying that n was not set:
NameError: name 'n' is not defined
I'm actually learning how to use python and I do not know if the syntax is correct, I just find examples of lists as ranges.
Could someone clear my ideas, give me some suggestions?
[EDIT1] My function z depends on j and f(n).
[EDIT2] I´m usind fibonacci ranges for integrate over a sphere.
The program is something like this:
def f(n):
a, b = 0, 1
for i in range(n):
a, b = b, a+b
return a
def y(n):
return f(n) + some_const
def z(j):
for j in range(0,f(n-1)):
return j*y(n) + j*f(n-1) + j*f(n)
You have
def z(j):
for j in range(0,f(n-1)):
return j*y(n) + j*f(n-1) + j*f(n)
Notice you say this takes something called j while your other functions take n.
Did you mean
def z(n):
for j in range(0,f(n-1)):
return j*y(n) + j*f(n-1) + j*f(n)
When you get an error check the line number it refers to.
Also, consider giving your variables longers names - just single letters get easy to muddle up!
As pointed out by the comment, once this stops giving the error message it might not do what you want.
You first function loops and then returns:
def f(n):
a = something
for i in range(n):
a = a + i
return a
(I presume something is set to, er, something)
Your z function returns as soon as it gets into the loop: perhaps you just want to collect the results and return them?
def z(n):
stuff = []
for j in range(0,f(n-1)):
stuff.append( j*y(n) + j*f(n-1) + j*f(n) )
return stuff
Notice the return is further left - no longer indented inside the for loop.
In fact you could use a list comprehension then:
def z(n):
return [j*y(n) + j*f(n-1) + j*f(n) for j in range(0,f(n-1))]
There are several problems with the snippet that you posted.
It would help if you include the code that calls the functions. It also seems that you should look into local-scope of vars in Python- it does not matter what you call the parameter passed into the function, so you could call the var in the brackets "n" for every function, but it is preferable to give them a meaningful name that indicates what that parameter represents- just useful for others looking at the code, and good practice!
Lastly, using a docstring inside the function makes it very clear what the functions do, and may include a desc. of the params passed (type/class).
def range_sum(n): # instead of f- range_sum seems appropriate
"""
Sums the range of numbers from 0 to n
>>> range_sum(4) # example data
10
"""
# no idea what a is meant to be, unless an accumulator to
# store the total, in which case it must be initialised
accum = 0
for i in range(1, n+1): #iterates from 1 to n
accum = aaccum + i
return a # returns the total
def y(m, const): # use a descriptive func name
"""
Sums the range of numbers from 0 to m and adds const
>>> y(4, 7) # example data
17
"""
return range_sum(m) + const
def z(j, n, m): # pass all the vars you need for the function so they have a value
"""
Something descriptive
>>> z(4, 2, 5) # example data
?
"""
total
for j in range(0,f(n-1)):
total += j*y(m) + j*f(n-1) + j*f(n)
return total
print("First Func, ", range_sum(4))
print("Second Func, ", y(4, 7))
print("Third Func, ", z(4, 2, 5))
Note that the number of arguments passed to each function matches the number expected by the function. It is possible to set defaults, but get the hang of getting this right first.
Not sure what the last function is meant to do, but as mentioned in the comment above, showing some code to illustrate how you call the code can be useful, as in the sample.

Python Change Variable In Place

I was recently learning how to use random.shuffle in Python and I was surprised to see that the function shuffles the variable in place without returning anything. How is this acheived in the function? Looking at the random library's source code yielded no answer.
How can I write my own function that changes the variable in place without reassignment?
I is works because of lists are mutable. You cant reassign any variable because it will be new variable. You cant modify immutable typed variable. You can modify mutable variable.
So:
>>> def addone(x):
... x += 1
>>> a = 2
>>> addone(a)
>>> a
2
>>> def addone(x):
... x.append(1)
...
>>> l=[2]
>>> addone(l)
>>> l
[2, 1]
>>> def addone(x):
... x = x + [1]
...
>>> li=[2]
>>> addone(li)
>>> li
[2]
Well, just look at the implementation of random.shuffle. Find it in a file called random.py, it's a pure python implementation and quite simple - just using a loop of assignments (with tuple unpacking).
def shuffle(self, x, random=None, int=int):
"""x, random=random.random -> shuffle list x in place; return None.
Optional arg random is a 0-argument function returning a random
float in [0.0, 1.0); by default, the standard random.random.
Do not supply the 'int' argument.
"""
if random is None:
random = self.random
for i in reversed(xrange(1, len(x))):
# pick an element in x[:i+1] with which to exchange x[i]
j = int(random() * (i+1))
x[i], x[j] = x[j], x[i]

Categories

Resources