3

I'm trying to combine pairs of numbers passed in via sys.argv. Example:

python myscript.py -35.12323 112.76767 -36.33345 112.76890 -33.68689 111.8980

My goal would be to turn these into sets of two's in tuples. Like such:

((-35.12323,112.76767),(-36.33345,112.76890),(-33.68689,111.8980))

This is what I've tried to so far, but it has a problem when I pass the results to the Shapely Point and Polygon methods.

from shapely.geometry import Polygon
from shapely.geometry import Point
import sys

def args_to_tuple(list):
    arglist = []

    for coord in list:
        arglist.append(float(coord))

    i = 0
    polylist = []

    for xory in arglist:
        tmp = (arglist[i],arglist[i+1])
        polylist.append(tmp)

    return tuple(polylist)


poly = Polygon(args_to_tuple(sys.argv[3:]))

# xy pair for point is first two sys args
point = Point(args_to_tuple(sys.argv[1:3]))

print point.within(poly) # should result in true, but doesn't
print poly.contains(point) # should result in true, but doesn't

It seems like this would be something common in itertools, but I can't seem to find anything in that module that lets you grab items in a pair.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Kenny Powers
  • 1,254
  • 2
  • 15
  • 25
  • Please edit question (specifically title) to reflect, that you do not want to combine list elements into list/tuple of tuples, but want to understand why `.within` and `.contains` return `False` with your code. Maybe you should ask a new question – Konstantin Jun 11 '15 at 01:37
  • Alik, I knew something was up with my arguments to tuple problem though, not the shapely library, which is now working since it's being passed a valid tuple of tuples. Thanks for the help! – Kenny Powers Jun 11 '15 at 01:42
  • Possible duplicate of [Iterating over every two elements in a list](https://stackoverflow.com/questions/5389507/iterating-over-every-two-elements-in-a-list) – Georgy Jun 03 '19 at 13:02

5 Answers5

2
In [10]: args_str = '-35.12323 112.76767 -36.33345 112.76890 -33.68689 111.8980'

In [11]: args = map(float, args_str.split())

In [12]: args
Out[12]: [-35.12323, 112.76767, -36.33345, 112.7689, -33.68689, 111.898]

In [13]: zip(args[::2], args[1::2])
Out[13]: [(-35.12323, 112.76767), (-36.33345, 112.7689), (-33.68689, 111.898)]

zip can be replaced with itertools.izip which produces an iterator instead of a list.

Konstantin
  • 24,271
  • 5
  • 48
  • 65
  • This works, and I appreciate it, but isn't this returning a 'list of tuples' to be more precise? Same for TigerhawkT3 's answer as well. – Kenny Powers Jun 11 '15 at 01:04
  • @KennyPowers yes, it returns a list of tuples, but if you need a tuple containing tuples then you can always do this `tuple(zip(args[::2], args[1::2]))`. `args[::2]` is a list slicing, it constructs a new list consisting of odd elemets. Read more about it [on SO](http://stackoverflow.com/a/509295/471899) – Konstantin Jun 11 '15 at 01:09
  • Thanks for the clarification, I forgot about the skipping piece for list slicing...very useful! Last question though, both answers here take strings in. When arguments are passed into a python script and used like sys.argv[1:] they are a list of strings instead, which means the str.split() method doesn't work for this scenario. – Kenny Powers Jun 11 '15 at 01:12
  • @KennyPowers if you have a list of points, instead of a string then just replace the last argument in `map` call. Anyway the most important part of the answer is about using `zip` and list slicing, not about converting data (either string or a list of string) into a list of floats – Konstantin Jun 11 '15 at 01:16
1
def args_to_tuple(lst):
    it = iter(map(float, lst.split()))
    return [(item, next(it)) for item in it]

>>> a = '-35.12323 112.76767 -36.33345 112.76890 -33.68689 111.8980'
>>> args_to_tuple(a)
[(-35.12323, 112.76767), (-36.33345, 112.7689), (-33.68689, 111.898)]
TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
  • Same thing with this answer, the input would be a list of strings rather than a string separated by spaces. The input is coming from sys.argv[1:]. Is there a pythonic way to do this and output a tuple of tuples? – Kenny Powers Jun 11 '15 at 01:14
  • Then just use `lst` instead of `lst.split()`, and pass `sys.argv[1:]`. – TigerhawkT3 Jun 11 '15 at 01:43
  • it work only if a have even arguments, if odd this solution throw exception – madjardi Apr 06 '16 at 13:46
  • @madjardi - The question specifies that they come in pairs. The numbers are 2D coordinates. – TigerhawkT3 Apr 06 '16 at 21:14
1

I would guess the Point constructor would probably take 1 pair, not a tuple containing 1 pair? To do this, you'd need to change:

point = Point(args_to_tuple(sys.argv[1:3]))

To:

point = Point(args_to_tuple(sys.argv[1:3])[0])

Or maybe:

point = Point(*args_to_tuple(sys.argv[1:3])[0])

Without knowing the shapely API I'm not sure.

As for converting arguments to a tuple of pairs, your method does just fine. However, if you want a pre-packaged no-hassle solution, I'd look at pytoolz's partition_all method for grouping pairs together. Also look at cytoolz, which strives to make those methods comparable to C in performance.

EDIT

I noticed a bug in your method. You are not incrementing i in your loop from your args_to_tuple method! Here is a revised version where you populate polylist:

polylist = [(arglist[i],arglist[i+1]) for i in xrange(0,len(arglist),2)]
parity3
  • 643
  • 9
  • 18
  • That's correct about the Point constructor. I was trying to mash up something that would take sys args and prep them for both the Polygon and Point constructors, which would take a tuple of tuples and a tuple respectively. I was able to get my desired output with a lot of bad code, but could never get a True return from a check if a point was in a poly, even though checking those points manually with shapely returns True. There was something about my conversion that was not acceptable by one of the two constructors. – Kenny Powers Jun 11 '15 at 01:20
  • In short, I'd be passing in sys args in the format of python script.py x y x y x y x y where the first xy pair of floats is the point, everything there after is the polygon that could have an infinite number of points defining it. – Kenny Powers Jun 11 '15 at 01:22
  • My hunch at this time would be to go back to basics and ignore the reading-from-sys.argv part. Just create a Point and Polygon from tuple constants containing float constants and make SURE that the within and contains methods are behaving as expected. – parity3 Jun 11 '15 at 01:25
  • I agree, that's exactly what I did and how I arrived at the conclusion that these calls should be returning True values, not only by looking at a map alone. – Kenny Powers Jun 11 '15 at 01:29
  • I spotted a bug in the original method to convert sys.argv into the tuple of pairs, see EDIT in my answer. – parity3 Jun 11 '15 at 01:33
  • parity3, Thanks! It's always good to see where I went wrong from the get go. The end answer is infinitely cleaner, but it's still great to see what was dysfunctional about the first attempt. – Kenny Powers Jun 11 '15 at 01:41
1
args_list = "-35.12323 112.76767 -36.33345 112.76890 -33.68689 111.8980".split()

You can zip the same iterator object to get pairs that you want:

a = (float(x) for x in args_list)
zip(a, a)
# [(-35.12323, 112.76767), (-36.33345, 112.7689), (-33.68689, 111.898)]

For each tuple that zip returns next is called on the same iterator object twice, pairing up your arguments in the desired fashion.

For python 3 you can just use a map since map returns an iterable map object instead of a generator expression.

%%python3

a = map(float, args_list)
print(tuple(zip(a, a)))
# ((-35.12323, 112.76767), (-36.33345, 112.7689), (-33.68689, 111.898))

You can also wrap the list returned by map using the iter built-in for python 2.

%%python2

a = iter(map(float, args_list)
print zip(a, a)
# [(-35.12323, 112.76767), (-36.33345, 112.7689), (-33.68689, 111.898)]
dting
  • 38,604
  • 10
  • 95
  • 114
  • I really like this. You can also use `map()` if you want, for the same result. Also, you can leave out the argument to `split()` since the default is whitespace (but the OP is working with a `list` slice in the first place, so it's a bit of a moot point). – TigerhawkT3 Jun 11 '15 at 01:51
0

Thanks guys for pointing me in the right direction! Here's my solution for future reference. Not sure why my crappier method wasn't working, but this seems pythonic and simple enough to go with.

from shapely.geometry import Polygon
from shapely.geometry import Point
import sys

#point = Point(38.27269, -120.98145)

# Point
l = map(float, sys.argv[1:3])
point = Point(tuple((l[0],l[1])))


# Polygon
l = map(float, sys.argv[3:])
poly = Polygon(tuple(zip(l[::2], l[1::2])))

# Test
print point.within(poly) # should result in true
print poly.contains(point) # should result in true
Kenny Powers
  • 1,254
  • 2
  • 15
  • 25