Related
Alright. So I've been through some SO answers such as Find an element in a list of tuples in python and they don't seem that specific to my case. And I am getting no idea on how to use them in my issue.
Let us say I have a list of a tuple of tuples; i.e. the list stores several data points each referring to a Cartesian point. Each outer tuple represents the entire data of the point. There is an inner tuple in this tuple which is the point exactly. That is, let us take the point (1,2) and have 5 denoting some meaning to this point. The outer tuple will be ((1,2),5)
Well, it is easy to figure out how to generate this. However, I want to search for an outer tuple based on the value of the inner tuple. That is I wanna do:
for y in range(0, 10):
for x in range(0, 10):
if (x, y) in ###:
print("Found")
or something of this sense. How can this be done?
Based on the suggestion posted as a comment by #timgen, here is some pseudo-sample data.
The list is gonna be
selectPointSet = [((9, 2), 1), ((4, 7), 2), ((7, 3), 0), ((5, 0), 0), ((8, 1), 2)]
So I may wanna iterate through the whole domain of points which ranges from (0,0) to (9,9) and do something if the point is one among those in selectPointSet; i.e. if it is (9, 2), (4, 7), (7, 3), (5, 0) or (8, 1)
Using the data structures that you currently are, you can do it like this:
listTuple = [((1,1),5),((2,3),5)] #dummy list of tuples
for y in range(0, 10):
for x in range(0, 10):
for i in listTuple:#loop through list of tuples
if (x, y) in listTuple[listTuple.index(i)]:#test to see if (x,y) is in the tuple at this index
print(str((x,y)) , "Found")
You can make use of a dictionary.
temp = [((1,2),3),((2,3),4),((6,7),4)]
newDict = {}
# a dictionary with inner tuple as key
for t in temp:
newDict[t[0]] = t[1]
for y in range(0, 10):
for x in range(0, 10):
if newDict.__contains__((x,y)):
print("Found")
I hope this is what you are asking for.
Make a set from the two-element tuples for O(1) lookup.
>>> data = [((1,2),3),((2,3),4),((6,7),4)]
>>> tups = {x[0] for x in data}
Now you can query tups with any tuple you like.
>>> (6, 7) in tups
True
>>> (3, 2) in tups
False
Searching for values from 0 to 9:
>>> from itertools import product
>>> for x, y in product(range(10), range(10)):
... if (x, y) in tups:
... print('found ({}, {})'.format(x, y))
...
found (1, 2)
found (2, 3)
found (6, 7)
If you need to retain information about the third number (and the two-element inner tuples in data are unique) then you can also construct a dictionary instead of a set.
>>> d = dict(data)
>>> d
{(1, 2): 3, (2, 3): 4, (6, 7): 4}
>>> (2, 3) in d
True
>>> d[(2, 3)]
4
Is there a nice Pythonic way to loop over a list, retuning a pair of elements? The last element should be paired with the first.
So for instance, if I have the list [1, 2, 3], I would like to get the following pairs:
1 - 2
2 - 3
3 - 1
A Pythonic way to access a list pairwise is: zip(L, L[1:]). To connect the last item to the first one:
>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]
I would use a deque with zip to achieve this.
>>> from collections import deque
>>>
>>> l = [1,2,3]
>>> d = deque(l)
>>> d.rotate(-1)
>>> zip(l, d)
[(1, 2), (2, 3), (3, 1)]
I'd use a slight modification to the pairwise recipe from the itertools documentation:
def pairwise_circle(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ... (s<last>,s0)"
a, b = itertools.tee(iterable)
first_value = next(b, None)
return itertools.zip_longest(a, b,fillvalue=first_value)
This will simply keep a reference to the first value and when the second iterator is exhausted, zip_longest will fill the last place with the first value.
(Also note that it works with iterators like generators as well as iterables like lists/tuples.)
Note that #Barry's solution is very similar to this but a bit easier to understand in my opinion and easier to extend beyond one element.
I would pair itertools.cycle with zip:
import itertools
def circular_pairwise(l):
second = itertools.cycle(l)
next(second)
return zip(l, second)
cycle returns an iterable that yields the values of its argument in order, looping from the last value to the first.
We skip the first value, so it starts at position 1 (rather than 0).
Next, we zip it with the original, unmutated list. zip is good, because it stops when any of its argument iterables are exhausted.
Doing it this way avoids the creation of any intermediate lists: cycle holds a reference to the original, but doesn't copy it. zip operates in the same way.
It's important to note that this will break if the input is an iterator, such as a file, (or a map or zip in python-3), as advancing in one place (through next(second)) will automatically advance the iterator in all the others. This is easily solved using itertools.tee, which produces two independently operating iterators over the original iterable:
def circular_pairwise(it):
first, snd = itertools.tee(it)
second = itertools.cycle(snd)
next(second)
return zip(first, second)
tee can use large amounts of additional storage, for example, if one of the returned iterators is used up before the other is touched, but as we only ever have one step difference, the additional storage is minimal.
There are more efficient ways (that don't built temporary lists), but I think this is the most concise:
> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]
Pairwise circular Python 'for' loop
If you like the accepted answer,
zip(L, L[1:] + L[:1])
you can go much more memory light with semantically the same code using itertools:
from itertools import islice, chain #, izip as zip # uncomment if Python 2
And this barely materializes anything in memory beyond the original list (assuming the list is relatively large):
zip(l, chain(islice(l, 1, None), islice(l, None, 1)))
To use, just consume (for example, with a list):
>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]
This can be made extensible to any width:
def cyclical_window(l, width=2):
return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])
and usage:
>>> l = [1, 2, 3, 4, 5]
>>> cyclical_window(l)
<itertools.izip object at 0x112E7D28>
>>> list(cyclical_window(l))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
>>> list(cyclical_window(l, 4))
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]
Unlimited generation with itertools.tee with cycle
You can also use tee to avoid making a redundant cycle object:
from itertools import cycle, tee
ic1, ic2 = tee(cycle(l))
next(ic2) # must still queue up the next item
and now:
>>> [(next(ic1), next(ic2)) for _ in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
This is incredibly efficient, an expected usage of iter with next, and elegant usage of cycle, tee, and zip.
Don't pass cycle directly to list unless you have saved your work and have time for your computer to creep to a halt as you max out its memory - if you're lucky, after a while your OS will kill the process before it crashes your computer.
Pure Python Builtin Functions
Finally, no standard lib imports, but this only works for up to the length of original list (IndexError otherwise.)
>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]
You can continue this with modulo:
>>> len_l = len(l)
>>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
I would use a list comprehension, and take advantage of the fact that l[-1] is the last element.
>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]
You don't need a temporary list that way.
Amazing how many different ways there are to solve this problem.
Here's one more. You can use the pairwise recipe but instead of zipping with b, chain it with the first element that you already popped off. Don't need to cycle when we just need a single extra value:
from itertools import chain, izip, tee
def pairwise_circle(iterable):
a, b = tee(iterable)
first = next(b, None)
return izip(a, chain(b, (first,)))
I like a solution that does not modify the original list and does not copy the list to temporary storage:
def circular(a_list):
for index in range(len(a_list) - 1):
yield a_list[index], a_list[index + 1]
yield a_list[-1], a_list[0]
for x in circular([1, 2, 3]):
print x
Output:
(1, 2)
(2, 3)
(3, 1)
I can imagine this being used on some very large in-memory data.
This one will work even if the list l has consumed most of the system's memory. (If something guarantees this case to be impossible, then zip as posted by chepner is fine)
l.append( l[0] )
for i in range( len(l)-1):
pair = l[i],l[i+1]
# stuff involving pair
del l[-1]
or more generalizably (works for any offset n i.e. l[ (i+n)%len(l) ] )
for i in range( len(l)):
pair = l[i], l[ (i+1)%len(l) ]
# stuff
provided you are on a system with decently fast modulo division (i.e. not some pea-brained embedded system).
There seems to be a often-held belief that indexing a list with an integer subscript is un-pythonic and best avoided. Why?
This is my solution, and it looks Pythonic enough to me:
l = [1,2,3]
for n,v in enumerate(l):
try:
print(v,l[n+1])
except IndexError:
print(v,l[0])
prints:
1 2
2 3
3 1
The generator function version:
def f(iterable):
for n,v in enumerate(iterable):
try:
yield(v,iterable[n+1])
except IndexError:
yield(v,iterable[0])
>>> list(f([1,2,3]))
[(1, 2), (2, 3), (3, 1)]
How about this?
li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]
from itertools import izip, chain, islice
itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))
(As above with #j-f-sebastian's "zip" answer, but using itertools.)
NB: EDITED given helpful nudge from #200_success. previously was:
itr = izip(l, chain(l[1:], l[:1]))
If you don't want to consume too much memory, you can try my solution:
[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]
It's a little slower, but consume less memory.
Starting in Python 3.10, the new pairwise function provides a way to create sliding pairs of consecutive elements:
from itertools import pairwise
# l = [1, 2, 3]
list(pairwise(l + l[:1]))
# [(1, 2), (2, 3), (3, 1)]
or simply pairwise(l + l[:1]) if you don't need the result as a list.
Note that we pairwise on the list appended with its head (l + l[:1]) so that rolling pairs are circular (i.e. so that we also include the (3, 1) pair):
list(pairwise(l)) # [(1, 2), (2, 3)]
l + l[:1] # [1, 2, 3, 1]
Just another try
>>> L = [1,2,3]
>>> zip(L,L[1:]) + [(L[-1],L[0])]
[(1, 2), (2, 3), (3, 1)]
L = [1, 2, 3]
a = zip(L, L[1:]+L[:1])
for i in a:
b = list(i)
print b
this seems like combinations would do the job.
from itertools import combinations
x=combinations([1,2,3],2)
this would yield a generator. this can then be iterated over as such
for i in x:
print i
the results would look something like
(1, 2)
(1, 3)
(2, 3)
I've got a list
a = [(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)]
If I say that:
for x in a:
if x[0]==1:
print x
I get the expected result : (1,2) (1,4) (1,8) (1,10) (1,6)
However I want to remove all the occurrences of all the tuples in the format (1,x),So
for x in a:
if x[0]==1:
a.remove(x)
I thought that all the occurences should be removed.However when i say
Print a
I get [(1,4),(2,6),(3,6),(1,6)]
Not all the tuples were removed. How do I do it.??
Thanks
I'd use list comprehension:
def removeTuplesWithOne(lst):
return [x for x in lst if x[0] != 1]
a = removeTuplesWithOne([(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)])
For me it's more pythonic than built-in filter function.
P.S. This function does not change your original list, it creates new one. If your original list is huge, i'd probably use generator expression like so:
def removeTuplesWithOne(lst):
return (x for x in lst if x[0] != 1)
This isn't the same approach as yours but should work
a = filter(lambda x: x[0] != 1, a)
You can use list comprehension like this, to filter out the items which have 1 as the first element.
>>> original = [(1, 2), (1, 4), (2, 6), (1, 8), (3, 6), (1, 10), (1, 6)]
>>> [item for item in original if item[0] != 1]
[(2, 6), (3, 6)]
This creates a new list, rather than modifying the existing one. 99% of the time, this will be fine, but if you need to modify the original list, you can do that by assigning back:
original[:] = [item for item in original if item[0] != 1]
Here we use slice assignment, which works by replacing every item from the start to the end of the original list (the [:]) with the items from the list comprehension. If you just used normal assignment, you would just change what the name original pointed to, not actually modify the list itself.
You can do it with a generator expression if you're dealing with huge amounts of data:
a = [(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)]
# create a generator
a = ((x,y) for x, y in a if x == 1)
# simply convert it to a list if you need to...
>>> print list(a)
[(1, 2), (1, 4), (1, 8), (1, 10), (1, 6)]
As part of learning Python I have set myself some challenges to see the various ways of doing things. My current challenge is to create a list of pairs using list comprehension. Part one is to make a list of pairs where (x,y) must not be the same(x not equal y) and order matters((x,y) not equal (y,x)).
return [(x,y) for x in listOfItems for y in listOfItems if not x==y]
Using my existing code is it possible to modify it so if (x,y) already exists in the list as (y,x) exclude it from the results? I know I could compare items after words, but I want to see how much control you can have with list comprehension.
I am using Python 2.7.
You should use a generator function here:
def func(listOfItems):
seen = set() #use set to keep track of already seen items, sets provide O(1) lookup
for x in listOfItems:
for y in listOfItems:
if x!=y and (y,x) not in seen:
seen.add((x,y))
yield x,y
>>> lis = [1,2,3,1,2]
>>> list(func(lis))
[(1, 2), (1, 3), (1, 2), (2, 3), (1, 2), (1, 3), (1, 2), (2, 3)]
def func(seq):
seen_pairs = set()
all_pairs = ((x,y) for x in seq for y in seq if x != y)
for x, y in all_pairs:
if ((x,y) not in seen_pairs) and ((y,x) not in seen_pairs):
yield (x,y)
seen_pairs.add((x,y))
Alternatively, you can also use generator expression (here: all_pairs) which is like list comprehension, but lazy evaluated. They are very helpful, especially when iterating over combinations, products etc.
Using product and ifilter as well as the unique_everseen recipe from itertools
>>> x = [1, 2, 3, 1, 2]
>>> x = product(x, x)
>>> x = unique_everseen(x)
>>> x = ifilter(lambda z: z[0] != z[1], x)
>>> for y in x:
... print y
...
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)
On Python, range(3) will return [0,1,2]. Is there an equivalent for multidimensional ranges?
range((3,2)) # [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)]
So, for example, looping though the tiles of a rectangular area on a tile-based game could be written as:
for x,y in range((3,2)):
Note I'm not asking for an implementation. I would like to know if this is a recognized pattern and if there is a built-in function on Python or it's standard/common libraries.
In numpy, it's numpy.ndindex. Also have a look at numpy.ndenumerate.
E.g.
import numpy as np
for x, y in np.ndindex((3,2)):
print(x, y)
This yields:
0 0
0 1
1 0
1 1
2 0
2 1
You could use itertools.product():
>>> import itertools
>>> for (i,j,k) in itertools.product(xrange(3),xrange(3),xrange(3)):
... print i,j,k
The multiple repeated xrange() statements could be expressed like so, if you want to scale this up to a ten-dimensional loop or something similarly ridiculous:
>>> for combination in itertools.product( xrange(3), repeat=10 ):
... print combination
Which loops over ten variables, varying from (0,0,0,0,0,0,0,0,0,0) to (2,2,2,2,2,2,2,2,2,2).
In general itertools is an insanely awesome module. In the same way regexps are vastly more expressive than "plain" string methods, itertools is a very elegant way of expressing complex loops. You owe it to yourself to read the itertools module documentation. It will make your life more fun.
There actually is a simple syntax for this. You just need to have two fors:
>>> [(x,y) for x in range(3) for y in range(2)]
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
That is the cartesian product of two lists therefore:
import itertools
for element in itertools.product(range(3),range(2)):
print element
gives this output:
(0, 0)
(0, 1)
(1, 0)
(1, 1)
(2, 0)
(2, 1)
You can use product from itertools module.
itertools.product(range(3), range(2))
I would take a look at numpy.meshgrid:
http://docs.scipy.org/doc/numpy-1.6.0/reference/generated/numpy.meshgrid.html
which will give you the X and Y grid values at each position in a mesh/grid. Then you could do something like:
import numpy as np
X,Y = np.meshgrid(xrange(3),xrange(2))
zip(X.ravel(),Y.ravel())
#[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)]
or
zip(X.ravel(order='F'),Y.ravel(order='F'))
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
Numpy's ndindex() works for the example you gave, but it doesn't serve all use cases. Unlike Python's built-in range(), which permits both an arbitrary start, stop, and step, numpy's np.ndindex() only accepts a stop. (The start is presumed to be (0,0,...), and the step is (1,1,...).)
Here's an implementation that acts more like the built-in range() function. That is, it permits arbitrary start/stop/step arguments, but it works on tuples instead of mere integers.
import sys
from itertools import product, starmap
# Python 2/3 compatibility
if sys.version_info.major < 3:
from itertools import izip
else:
izip = zip
xrange = range
def ndrange(start, stop=None, step=None):
if stop is None:
stop = start
start = (0,)*len(stop)
if step is None:
step = (1,)*len(stop)
assert len(start) == len(stop) == len(step)
for index in product(*starmap(xrange, izip(start, stop, step))):
yield index
Example:
In [7]: for index in ndrange((1,2,3), (10,20,30), step=(5,10,15)):
...: print(index)
...:
(1, 2, 3)
(1, 2, 18)
(1, 12, 3)
(1, 12, 18)
(6, 2, 3)
(6, 2, 18)
(6, 12, 3)
(6, 12, 18)