Python: reduce on tuple of tuples - python

I am trying to compute in Python the length of the path from a point A to a point B going through a list of intermediary points. I know how to do it but I do want to use the reduce Built-in function.
Why I tried so far, please note that it is completely wrong, is this:
reduce(lambda x,y: math.sqrt((y[1]-y[0])**2+(x[1]-x[0])**2) , ((1,2),(3,4),(1,8)))
Any idea?
Thanks.

You should map before you reduce.
points = [(1, 2), (3, 4), (1, 8)]
distances = (math.hypot(b[0]-a[0], b[1]-a[1])
for a, b in zip(points, points[1:]))
total_distance = sum(distances)
or, if you must use reduce(), although sum() is better for this purpose:
import operator
total_distance = reduce(operator.add, distances)
If you have a lot of points, you might find NumPy helpful in doing this all at once, quickly:
import numpy
total_distance = numpy.hypot(*numpy.diff(numpy.array(points), axis=0)).sum()
Edit: use math.hypot() and add NumPy method.

It isn't pretty but it can be done :-)
>>> tot = ((1,2),(3,4),(1,8))
>>> reduce(lambda d,((x0,y0),(x1,y1)): d + ((x1-x0)**2+(y1-y0)**2)**0.5, zip(tot[1:], tot[0:]), 0.0)
7.3005630797457695

reduce() is simply the wrong tool for this purpose. It is possible to do it with reduce(), but it is a bit weird:
def distance((x, d), y):
return y, d + math.hypot(y[0] - x[0], y[1] - x[1])
print reduce(distance, [(3,4),(1,8)], ((1, 2), 0.0))[1]
prints
7.30056307975
The last parameter passed to the reduce() call is the starting point and the initial value for the distance.

reduce does not work that way, you start with an initial value a, which you specify or is taken as first element from your iterable. afterwards, you pass a,next_element to the function (lambda) provided and store the result in a, repeat until all elements are iterated.
You can do what you want with sum and map by first calculating all distances from one point to the next and then summing them:
path = [(1,2),(3,4),(1,8)]
sum(map(lambda x,y: math.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2), path[:-1],path[1:]))
edit: or with the hypot function (thx #ralu):
sum(map(lambda x,y: math.hypot(x[0]-y[0],x[1]-y[1]), path[:-1],path[1:]))

This is just not the sort of code you want to write.
Reduce won't be a good solution.
I suggest a iterative one.
It will be the most readable, pythonic and maintainable solution.
import math
path = [(1,2),(3,4),(1,8)]
def calc_dist(waypoints):
dist = 0.0
for i in range(len(waypoints) - 1):
a = waypoints[i]
b = waypoints[i+1]
dist += math.hypot(a[0]-b[0], b[1]-a[1])
return dist
print calc_dist( path )

Here is a redux meta-iterator that can be combined with the built-in reduce to get the result you want. This implementation avoids all buffering of the input sequence.
def redux(f):
def execute(iterable):
iterable = iter(iterable)
try:
state = iterable.next()
except StopIteration:
raise ValueError, 'empty sequences not supported'
while True:
newstate = iterable.next()
yield f(state, newstate)
state = newstate
return execute
f = redux(lambda x, y: math.sqrt((y[0] - x[0])**2 + (y[1] - x[1])**2))
print reduce(operator.add, f(((1,2),(3,4),(1,8))))
The above prints 7.30056307975.
The redux function can be generalized to support more than two arguments at a time in a sliding window, by using inspect.getargspec to count the number of arguments required by its function argument.

I'm aware that what I'm about to suggest is not ideal, but I think this is as close as I can get for my contribution. This is a fun problem to solve, even if it isn't the most traditional application of reduce.
The key issue seems to be keeping track of the distance from point to point without overwriting the points themselves- adding another 'dimension' to each point gives you a field with which you can track the running distance.
iterable = ((1,2,0), (3,4,0), (1,8,0))
# originally ((1,2), (3,4), (1,8))
from math import sqrt
def func(tup1, tup2):
'''function to pass to reduce'''
# extract coordinates
x0 = tup1[0]
x1 = tup2[0]
y0 = tup1[1]
y1 = tup2[1]
dist = tup1[2] # retrieve running total for distance
dx = x1 - x0 # find change in x
dy = y1 - y0 # find change in y
# add new distance to running total
dist += sqrt(dx**2 + dy**2)
# return 2nd point with the updated distance
return tup2[:-1] + (dist,) # e.g. (3, 4, 2.828)
Now reduce:
reduce(func, iterable)[-1]
# returns 7.3005630797457695
This way, the intermediate tuple of tuples (i.e., after one 'reduction') becomes:
((3, 4, 2.8284271247461903), (1,8,0))

Just for fun, here is an alternate solution with a slightly different approach than the reduce(sum, map(hypot, zip(...))) approach.
tot = ((1,2),(3,4),(1,8))
reduce(lambda (d,(x,y)),b: (d+math.hypot(x-b[0],y-b[1]), b), tot, (0, tot[0]))[0]
Note that the reduce actually returns the tuple (distance, last point), hence the [0] at the end. I think this would be more efficient than zip solutions but haven't actually checked.

Related

Displaying solutions within a certain range when using sympy solve command

I can be considered pretty much new to python and coding in general so forgive me for my ignorance.
I'm trying to solve a system of trigonometric functions in python, and I'm doing so using the solve command from sympy. However, this method returns only a finite number of solutions, two in this particular case.
I've read through the documentation and it seems that to get an expression for all the solutions solveset is to be used instead. However, I do not want all the solutions to be displayed, but rather only a finite amount which is contained within a certain range.
Here's the example:
from sympy import *
x, y = symbols('x, y')
eq1 = Eq(y - sin(x), 0)
eq2 = Eq(y - cos(x), 0)
sol = solve([eq1, eq2], [x, y])
print(sol)
which only returns the first two solutions in the positive x range.
How could I do to, for example, display all the solutions within the x range [-2pi, 2pi]?
I'd want them in explicit form rather than written in term of some multiplier since I then need to convert them into numerical form.
Thank you in advance.
SymPy can really take you down rabbit holes. I agree with kampmani's solution, only if you can easily solve for y on your own. However, in more general cases and in higher dimensions, his solution does not hold.
For example, the following will be slightly more tricky:
eq1 = Eq(z - x*y, 0)
eq2 = Eq(z - cos(x) - sin(y), 0)
eq3 = Eq(z + x*y, 0)
So here I am; killing a fly with a bazooka. The problem is that one was able to simplify the set of equations into a single equation with a single variable. But what if you can't do that (for example, if it was a larger system)?
In this case, one needs to use nonlinsolve to solve the system of equations. But this does not provide numeric solutions directly and does not have a domain argument.
So the following code unpacks the solutions. It goes through each tuple in the set of solutions and finds the numeric solutions for each component in the tuple. Then in order to get the full list, you need a Cartesian Product of each of those components. Repeat this for each tuple in the set of solutions.
The following should work for almost any system of equations in any dimension greater than 1. It produces numeric solutions in the cube whose boundaries are the domains variable.
from sympy import *
import itertools # used for cartesian product
x, y, z = symbols('x y z', real=True)
domains = [Interval(-10, 10), Interval(-10, 10), Interval(-10, 10)] # The domain for each variable
eq1 = z - x*y
eq2 = z - cos(x) - sin(y)
eq3 = z + x*y
solutions = nonlinsolve([eq1, eq2, eq3], [x, y, z]) # the recommended function for this situation
print("---------Solution set----------")
print(solutions) # make sure the solution set is reasonable. If not, assertion error will occur
_n = Symbol("n", integer=True) # the solution set often seems to contain these symbols
numeric_solutions = []
assert isinstance(solutions, Set) # everything that I had tried resulted in a FiniteSet output
for solution in solutions.args: # loop through the different kinds of solutions
assert isinstance(solution, Tuple) # each solution should be a Tuple if in 2D or higher
list_of_numeric_values = [] # the list of lists of a single numerical value
for i, element in enumerate(solution):
if isinstance(element, Set):
numeric_values = list(element.intersect(domains[i]))
else: # assume it is an Expr
assert isinstance(element, Expr)
if _n.name in [s.name for s in element.free_symbols]: # if n is in the expression
# change our own _n to the solutions _n since they have different hidden
# properties and they cannot be substituted without having the same _n
_n = [s for s in element.free_symbols if s.name == _n.name][0]
numeric_values = [element.subs(_n, n)
for n in range(-10, 10) # just choose a bunch of sample values
if element.subs(_n, n) in domains[i]]
elif len(element.free_symbols) == 0: # we just have a single, numeric number
numeric_values = [element] if element in domains[i] else []
else: # otherwise we just have an Expr that depends on x or y
# we assume this numerical value is in the domain
numeric_values = [element]
# note that we may have duplicates, so we remove them with `set()`
list_of_numeric_values.append(set(numeric_values))
# find the resulting cartesian product of all our numeric_values
numeric_solutions += itertools.product(*list_of_numeric_values)
# remove duplicates again to be safe with `set()` but then retain ordering with `list()`
numeric_solutions = list(set(numeric_solutions))
print("--------`Expr` values----------")
for i in numeric_solutions:
print(list(i)) # turn it into a `list` since the output below is also a `list`.
print("--------`float` values---------")
for i in numeric_solutions:
print([N(j) for j in i]) # could have been converted into a `tuple` instead
In particular, it produces the following output for the new problem:
---------Solution set----------
FiniteSet((0, ImageSet(Lambda(_n, 2*_n*pi + 3*pi/2), Integers), 0))
--------`Expr` values----------
[0, -5*pi/2, 0]
[0, -pi/2, 0]
[0, 3*pi/2, 0]
--------`float` values---------
[0, -7.85398163397448, 0]
[0, -1.57079632679490, 0]
[0, 4.71238898038469, 0]
It was a lot of effort and probably not worth it but oh well.
By using solveset you can restrict the solutions with domain argument. For evaluating numerical results use .evalf() or another similar method.
from sympy import Interval, symbols, solveset, sin, cos, pi
x = symbols('x')
sol = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
print(sol)
print(sol.evalf())
Output
FiniteSet(-7*pi/4, -3*pi/4, pi/4, 5*pi/4)
FiniteSet(-5.49778714378214, -2.35619449019234, 0.785398163397448, 3.92699081698724)
I hope this helps!
Thanks to the brilliant suggestion from #kampmani it is possible to achieve the desired result.
For start, the FiniteSet elements are not indexed and cannot be used, so the FiniteSet has to be converted into a list:
solx_array = []
#
#
#
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
The next step is to find the y coordinate of the intersection point given its x coordinate. The final code should look somewhat similar to this:
from sympy import Interval, symbols, solveset, sin, cos, pi
sol_array = []
x = symbols('x')
solx = solveset(cos(x) - sin(x), x, domain=Interval(-2*pi, 2*pi))
solx_array=list(solx)
for i in range(len(solx_array)):
soly = cos(solx_array[i])
sol_array.append(str(solx_array[i] + soly))
for i in range(len(sol_array)):
print(sol_array[i])
Still don't know how to convert the results into numerical form though, any idea is very appreciated.

How can I use built-in function to find the closest value in list to a target?

E.g., given
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
points = [Point(x=1.0, y=1.0), Point(x=2.0, y=2.0), Point(x=5.0, y=5.0)]
target = Point(x=4.5, y=5.0)
closest_point = find_closest(target, points)
I want to return Point(x=5.0, y=5.0). Ideally I'd like to use a built in function that takes (list, target, comp) where comp takes (a, b) -> float and the goal is to find such a from list that minimizes (a, target), e.g.:
closest_point = find_closest(points, target, dist) # where dist is (a.x-b.x)**2 + (a.y-b.y)**2
The reason I'm interested in this is because I found myself writing 3 duplicated functions where the only difference is that dist functions (and they use different fields to compute it).
The min function can take a key argument, which is a function to use as your comparator. In this case you can write a lambda to compute the distance from each point to target.
>>> min(points, key=lambda pt : sqrt((target.x - pt.x)**2 + (target.y - pt.y)**2))
Point(x=5.0, y=5.0)
You can try following example code:
def find_closest(target, points):
dist = []
for i in points:
dist.append((i.x - target.x)**2 + (i.y - target.y)**2)
min_index = dist.index(min(dist))
return points[min_index]
closest_point = find_closest(target, points)
It gives output: Point(x=5.0, y=5.0)

How can I create a list of R=10000 booleans called inside in Python?

import random, math
random.seed(1)
def in_circle(x, origin = [0]*2):
"""
This function determines if a two-dimensional point
falls within the unit circle.
"""
if len(x) != 2:
return "x is not two-dimensional!"
elif distance(x, origin) < 1:
return True
else:
return False
print(in_circle((1,1)))
Next, I want to determine whether each point in x falls within the unit circle centered at (0,0) using the function "in_circle". How do I do it?
My level of programming - Beginner
I don't know how your distance function looks like but assuming your x variable passed to the function has 10000 points, this is a way you can compute your boolean array. While calling the function in_circle, you can pass your array/list of all 10000 points and replace [(1,1), (1,2), (2,2)]) in in_circle([(1,1), (1,2), (2,2)]) by your array/list containing points. Let me know if it doesn't work.
In this solution, you will call the function in_circle once by passing all the 10000 points together and then the for loop will compute the distances inside the function. In my opinion, it is better than putting the for loop outside and calling the function 10000 times for each point one by one.
import random, math
random.seed(1)
def in_circle(x, origin = [0]*2):
bool_array = []
for point in x: # assuming x contains 10000 points
if len(x) != 2:
print ("x is not two-dimensional!")
continue
elif distance(x, origin) < 1:
bool_array.append(True)
else:
bool_array.append(False)
return bool_array
bool_array = in_circle([(1,1), (1,2), (2,2)])
as I understand, you have the list of tuples like this
l = [(x1, y1),
(x2, y2),
...
(x10000, y10000)]
what you need in that case is to iterate over it
but first, create a list that will contain booleans
bools = []
for xy in l: # xy is tuple
bools.append(in_circle(xy))
that was begginer level, but there's even fancier way of doing it:
bools = [in_circle(xy) for xy in l]
Hi Just put your 1000 points or any amount of coordinates in a list li below and run your function over them using a loop and you will get your booleans in a list X. Like:
x=[] #declare an empty list where boolean value needs to be stored
li=[(1,1),(0,0),(-1,1)]
for i in range(0,len(li)):
x1=in_circle(li[i])
x.append(x1)
If lever of programming beginner, you will find useful to also look at these concepts, they ll be handy in your programmer future:
map, lambda and generators.
You could define simple functions using lambda functions.
As:
in_circle = lambda (x, y): True if Math.sqrt( Math.pow(x,2) + Math.pow(y,2) ) < 1 else False
# assuming circle has center 0, 0 and radius 1
2.
and then map the function to a list of points:
map( in_circle, your_list)
Note that in lambda the syntax (x, y) is because you are passing a tuple as one argument, and your list is formed like:
your_list = [(0,1),(0, 0.5), (0.3, 0.4) ...]
3.
Instead of list, you can also use a generator, a structure very handy in iterations if you don't need to use again your list.
syntax is similar (note the brakets ! ( VS [ )
your_point = [ (random.random(), random.random()) for i in range(n) ]
# list comprehension
your_point = ( (random.random(), random.random()) for i in range(n) )
# generator
4.
So you could generate a list of N booleans like:
n = 10000
your_point = ( (random.random(), random.random()) for i in range(n) )
bool_list = map( in_circle, your_list)
For your curiosity in difference between lamdba and regular functions, see also:
what is the difference for python between lambda and regular function?
For your interest in generators VS lists comprehension:
Generator Expressions vs. List Comprehension

How to find last "K" indexes of vector satisfying condition (Python) ? (Analogue of Matlab's "find" )

Consider some vector:
import numpy as np
v = np.arange(10)
Assume we need to find last 2 indexes satisfying some condition.
For example in Matlab it would be written e.g.
find(v <5 , 2,'last')
answer = [ 3 , 4 ] (Note: Matlab indexing from 1)
Question: What would be the clearest way to do that in Python ?
"Nice" solution should STOP search when it finds 2 desired results, it should NOT search over all elements of vector.
So np.where does not seems to be "nice" in that sense.
We can easyly write that using "for", but is there any alternative way ?
I am afraid using "for" since it might be slow (at least it is very much so in Matlab).
This attempt doesn't use numpy, and it is probably not very idiomatic.
Nevertheless, if I understand it correctly, zip, filter and reversed are all lazy iterators that take only the elements that they really need. Therefore, you could try this:
x = list(range(10))
from itertools import islice
res = reversed(list(map(
lambda xi: xi[1],
islice(
filter(
lambda xi: xi[0] < 5,
zip(reversed(x), reversed(range(len(x))))
),
2
)
)))
print(list(res))
Output:
[3, 4]
What it does (from inside to outside):
create index range
reverse both array and indices
zip the reversed array with indices
filter the two (value, index)-pairs that you need, extract them by islice
Throw away the values, retain only indices with map
reverse again
Even though it looks somewhat monstrous, it should all be lazy, and stop after it finds the first two elements that you are looking for. I haven't compared it with a simple loop, maybe just using a loop would be both simpler and faster.
Any solution you'd find will iterate over the list even if the loop is 'hidden' inside a function.
The solution to your problem depends on the assumptions you can make e.g. is the list sorted?
for the general case I'd iterate over the loop starting at the end:
def find(condition, k, v):
indices = []
for i, var in enumerate(reversed(v)):
if condition(var):
indices.append(len(v) - i - 1)
if len(indices) >= k:
break
return indices
The condition should then be passed as a function, so you can use a lambda:
v = range(10)
find(lambda x: x < 5, 3, v)
will output
[4, 3, 2]
I'm not aware of a "good" numpy solution to short-circuiting.
The most principled way to go would be using something like Cython which to brutally oversimplify it adds fast loops to Python. Once you have set that up it would be easy.
If you do not want to do that you'd have to employ some gymnastics like:
import numpy as np
def find_last_k(vector, condition, k, minchunk=32):
if k > minchunk:
minchunk = k
l, r = vector.size - minchunk, vector.size
found = []
n_found = 0
while r > 0:
if l <= 0:
l = 0
found.append(l + np.where(condition(vector[l:r]))[0])
n_found += len(found[-1])
if n_found >= k:
break
l, r = 3 * l - 2 * r, l
return np.concatenate(found[::-1])[-k:]
This tries balancing loop overhead and numpy "inflexibility" by searching in chunks, which we grow exponentially until enough hits are found.
Not exactly pretty, though.
This is what I've found that seems to do this job for the example described (using argwhere which returns all indices that meet the criteria and then we find the last two of these as a numpy array):
ind = np.argwhere(v<5)
ind[-2:]
This searches through the entire array so is not optimal but is easy to code.

"'generator' object is not subscriptable" error

Why am I getting this error, from line 5 of my code, when attempting to solve Project Euler Problem 11?
for x in matrix:
p = 0
for y in x:
if p < 17:
currentProduct = int(y) * int(x[p + 1]) * int(x[p + 2]) * int(x[p + 3])
if currentProduct > highestProduct:
print(currentProduct)
highestProduct = currentProduct
else:
break
p += 1
'generator' object is not subscriptable
Your x value is is a generator object, which is an Iterator: it generates values in order, as they are requested by a for loop or by calling next(x).
You are trying to access it as though it were a list or other Sequence type, which let you access arbitrary elements by index as x[p + 1].
If you want to look up values from your generator's output by index, you may want to convert it to a list:
x = list(x)
This solves your problem, and is suitable in most cases. However, this requires generating and saving all of the values at once, so it can fail if you're dealing with an extremely long or infinite list of values, or the values are extremely large.
If you just needed a single value from the generator, you could instead use itertools.islice(x, p) to discard the first p values, then next(...) to take the one you need. This eliminate the need to hold multiple items in memory or compute values beyond the one you're looking for.
import itertools
result = next(itertools.islice(x, p))
As an extension to Jeremy's answer some thoughts about the design of your code:
Looking at your algorithm, it appears that you do not actually need truly random access to the values produced by the generator: At any point in time you only need to keep four consecutive values (three, with an extra bit of optimization). This is a bit obscured in your code because you mix indexing and iteration: If indexing would work (which it doesn't), your y could be written as x[p + 0].
For such algorithms, you can apply kind of a "sliding window" technique, demonstrated below in a stripped-down version of your code:
import itertools, functools, operator
vs = [int(v) for v in itertools.islice(x, 3)]
for v in x:
vs.append(int(v))
currentProduct = functools.reduce(operator.mul, vs, 1)
print(currentProduct)
vs = vs[1:]

Categories