Related
I have two lists
a = [1,2,3]
b = [9,10]
I want to combine (zip) these two lists into one list c such that
c = [(1,9), (2,10), (3, )]
Is there any function in standard library in Python to do this?
Normally, you use itertools.zip_longest for this:
>>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
...
(1, 9)
(2, 10)
(3, None)
But zip_longest pads the shorter iterable with Nones (or whatever value you pass as the fillvalue= parameter). If that's not what you want then you can use a comprehension to filter out the Nones:
>>> for i in (tuple(p for p in pair if p is not None)
... for pair in itertools.zip_longest(a, b)):
... print(i)
...
(1, 9)
(2, 10)
(3,)
but note that if either of the iterables has None values, this will filter them out too. If you don't want that, define your own object for fillvalue= and filter that instead of None:
sentinel = object()
def zip_longest_no_fill(a, b):
for i in itertools.zip_longest(a, b, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
list(zip_longest_no_fill(a, b)) # [(1, 9), (2, 10), (3,)]
Another way is map:
a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)
Although that will too contain (3, None) instead of (3,). To do that, here's a fun line:
c = (tuple(y for y in x if y is not None) for x in map(None, a, b))
It's not too hard to just write the explicit Python to do the desired operation:
def izip_short(a, b):
ia = iter(a)
ib = iter(b)
for x in ia:
try:
y = next(ib)
yield (x, y)
except StopIteration:
yield (x,)
break
for x in ia:
yield (x,)
for y in ib:
yield (None, y)
a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))
I wasn't sure how you would want to handle the b sequence being longer than the a sequence, so I just stuff in a None for the first value in the tuple in that case.
Get an explicit iterator for each sequence. Run the a iterator as a for loop, while manually using next(ib) to get the next value from the b sequence. If we get a StopIteration on the b sequence, we break the loop and then for x in ia: gets the rest of the a sequence; after that for y in ib: will do nothing because that iterator is already exhausted. Alternatively, if the first for x in ia: loop exhausts the a iterator, the second for x in ia: does nothing but there could be values left in the b sequence and the for y in ib: loop collects them.
Single line:
c = zip(a, b) + [(x,) for x in a[len(b):]] + [(x,) for x in b[len(a):]]
If you want to reuse this:
def mergeUsNicely(a, b):
def tupleMe(val):
return (val,)
return zip(a, b) + map(tupleMe, a[len(b):]) + map(tupleMe, b[len(a):])
This answer is an extension of the top answer that allows for arbitrary inputs instead of only two.
import itertools
sentinel = object()
def zip_longest_no_fill(*args):
for i in itertools.zip_longest(*args, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
What am I missing here ? If List size is greater than 5, I need last element, else first element.
fn = lambda *d: d[-1] if len(d) > 5 else d[0]
print map(fn,[1,2,3,4,5,6,7,8,9,10])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Your use of map is incorrect. Your example is the equivalent of:
print [fn(1), fn(2), fn(3), ...]
You have a second problem with your use of *d in your lambda. This is special syntax for calling a method with multiple arguments. d is now a tuple of all your arguments, so if you call:
>>> fn = lambda *d: d
>>> fn([1, 2, 3])
([1, 2, 3],)
This is what you wanted, probably:
fn = lambda d: d[-1] if len(d) > 5 else d[0]
print fn([1,2,3,4,5,6,7,8,9,10])
10
print fn([1,2,3])
1
This is your code:
In [124]: n = lambda *d: d[-1] if len(d) > 5 else d[0]
In [125]: map(n,[1,2,3,4,5,6,7,8,9,10])
Out[125]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
But this is what it is actually doing:
In [126]: n = lambda *d: d
In [127]: map(n,[1,2,3,4,5,6,7,8,9,10])
Out[127]: [(1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,), (10,)]
For your purpose, this should be sufficient:
In [132]: d = [1,2,3,4,5,6,7,8,9,10]
In [133]: d[-1] if len(d) > 5 else d[0]
Out[133]: 10
In [1]: print map.doc
map(function, sequence[, sequence, ...]) -> list
Return a list of the results of applying the function to the items of
the argument sequence(s). If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of
the items of the sequence (or a list of tuples if more than one sequence).
this would do what you want:
fn = lambda *d: d[-1] if len(d) > 5 else d[0]
map(fn, [[1,2,3,4,5,6,7,8,9,10]])
You can't use map here. this lambda link is helpful to you
As you can see, fn() and gn() do exactly the same and can be used in the
same ways. Note that the lambda definition does not include a "return"
statement -- it always contains an expression which is returned. Also
note that you can put a lambda definition anywhere a function is
expected, and you don't have to assign it to a variable at all.
You try this:
L = [1,2,3,4,5,6,7,8,9,10]
fn = lambda d: d[-1] if len(d) > 5 else d[0]
print fn(L)
10
it also give you same output.
def gn(d): return d[-1] if len(d) > 5 else d[0]
print gn(L)
10
What is the most elegant and concise way (without creating my own class with operator overloading) to perform tuple arithmetic in Python 2.7?
Lets say I have two tuples:
a = (10, 10)
b = (4, 4)
My intended result is
c = a - b = (6, 6)
I currently use:
c = (a[0] - b[0], a[1] - b[1])
I also tried:
c = tuple([(i - j) for i in a for j in b])
but the result was (6, 6, 6, 6). I believe the above works as a nested for loops resulting in 4 iterations and 4 values in the result.
If you're looking for fast, you can use numpy:
>>> import numpy
>>> numpy.subtract((10, 10), (4, 4))
array([6, 6])
and if you want to keep it in a tuple:
>>> tuple(numpy.subtract((10, 10), (4, 4)))
(6, 6)
One option would be,
>>> from operator import sub
>>> c = tuple(map(sub, a, b))
>>> c
(6, 6)
And itertools.imap can serve as a replacement for map.
Of course you can also use other functions from operator to add, mul, div, etc.
But I would seriously consider moving into another data structure since I don't think this type of problem is fit for tuples
Use zip and a generator expression:
c = tuple(x-y for x, y in zip(a, b))
Demo:
>>> a = (10, 10)
>>> b = (4, 4)
>>> c = tuple(x-y for x, y in zip(a, b))
>>> c
(6, 6)
Use itertools.izip for a memory efficient solution.
help on zip:
>>> print zip.__doc__
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
JFYI, execution time in my laptop in 100000 times iteration
np.subtract(a, b) : 0.18578505516052246
tuple(x - y for x, y in zip(a, b)) :
0.09348797798156738
tuple(map(lambda x, y: x - y, a, b)) : 0.07900381088256836
from operator import sub tuple(map(sub, a, b)) : 0.044342041015625
operator looks more elegant for me.
This can also be done just as nicely without an import at all, although lambda is often undesirable:
tuple(map(lambda x, y: x - y, a, b))
If you are looking to get the distance between two points on say a 2d coordinate plane you should use the absolute value of the subtraction of the pairs.
tuple(map(lambda x ,y: abs(x - y), a, b))
Since in your question there only examples of 2-number-tuples, for such coordinate-like tuples, you may be good with simple built-in "complex" class:
>>> a=complex(7,5)
>>> b=complex(1,2)
>>> a-b
>>> c=a-b
>>> c
(6+3j)
>>> c.real
6.0
>>> c.imag
3.0
As an addition to Kohei Kawasaki's answer, for speed, the original solution was actually the fastest (For a length two tuple at least).
>>> timeit.timeit('tuple(map(add, a, b))',number=1000000,setup='from operator import add; a=(10,11); b=(1,2)')
0.6502681339999867
>>> timeit.timeit('(a[0] - b[0], a[1] - b[1])',number=1000000,setup='from operator import add; a=(10,11); b=(1,2)')
0.19015854899998885
>>>
my element-wise tuple arithmetic helper
supported operations: +, -, /, *, d
operation = 'd' calculates distance between two points on a 2D coordinate plane
def tuplengine(tuple1, tuple2, operation):
"""
quick and dirty, element-wise, tuple arithmetic helper,
created on Sun May 28 07:06:16 2017
...
tuple1, tuple2: [named]tuples, both same length
operation: '+', '-', '/', '*', 'd'
operation 'd' returns distance between two points on a 2D coordinate plane (absolute value of the subtraction of pairs)
"""
assert len(tuple1) == len(tuple2), "tuple sizes doesn't match, tuple1: {}, tuple2: {}".format(len(tuple1), len(tuple2))
assert isinstance(tuple1, tuple) or tuple in type(tuple1).__bases__, "tuple1: not a [named]tuple"
assert isinstance(tuple2, tuple) or tuple in type(tuple2).__bases__, "tuple2: not a [named]tuple"
assert operation in list("+-/*d"), "operation has to be one of ['+','-','/','*','d']"
return eval("tuple( a{}b for a, b in zip( tuple1, tuple2 ))".format(operation)) \
if not operation == "d" \
else eval("tuple( abs(a-b) for a, b in zip( tuple1, tuple2 ))")
I have two lists:
a = [(1,2,3),(4,5,6)]
b = [7,8]
I want to merge it into:
c = [(1,2,3,7),(4,5,6,8)]
I used zip(a,b) but the result does not seem correct. Can anyone help?
zip() will just pair up the tuples and the integers. You also need to concatenate the tuple and the new item:
c = [aa + (bb,)
for aa, bb in zip(a, b)]
>>> a = [(1,2,3),(4,5,6)]
>>> b = [7,8]
>>> c = zip(*a)+[b] #c looks like [(1,4),(2,5),(3,6),(7,8)]
>>> print zip(*c) #zip it back together
[(1, 2, 3, 7), (4, 5, 6, 8)]
>>>
Try
map ( lambda x: x[0]+(x[1],), zip(a,b))
And yet another version:
from itertools import izip
[x+(y,) for x,y in izip(a,b)]
Should be efficient and it expresses what you are really doing in a readable way.
And yet another:
map(lambda t, e: t + (e,), a, b)
No need to zip and unpack; map can take both lists at once.
print((a[0]+(b[0],),a[1]+(b[1],)))
This seems clear to me:
[x + (b[i],) for i,x in enumerate(a)]
I have two lists
a = [1,2,3]
b = [9,10]
I want to combine (zip) these two lists into one list c such that
c = [(1,9), (2,10), (3, )]
Is there any function in standard library in Python to do this?
Normally, you use itertools.zip_longest for this:
>>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
...
(1, 9)
(2, 10)
(3, None)
But zip_longest pads the shorter iterable with Nones (or whatever value you pass as the fillvalue= parameter). If that's not what you want then you can use a comprehension to filter out the Nones:
>>> for i in (tuple(p for p in pair if p is not None)
... for pair in itertools.zip_longest(a, b)):
... print(i)
...
(1, 9)
(2, 10)
(3,)
but note that if either of the iterables has None values, this will filter them out too. If you don't want that, define your own object for fillvalue= and filter that instead of None:
sentinel = object()
def zip_longest_no_fill(a, b):
for i in itertools.zip_longest(a, b, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
list(zip_longest_no_fill(a, b)) # [(1, 9), (2, 10), (3,)]
Another way is map:
a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)
Although that will too contain (3, None) instead of (3,). To do that, here's a fun line:
c = (tuple(y for y in x if y is not None) for x in map(None, a, b))
It's not too hard to just write the explicit Python to do the desired operation:
def izip_short(a, b):
ia = iter(a)
ib = iter(b)
for x in ia:
try:
y = next(ib)
yield (x, y)
except StopIteration:
yield (x,)
break
for x in ia:
yield (x,)
for y in ib:
yield (None, y)
a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))
I wasn't sure how you would want to handle the b sequence being longer than the a sequence, so I just stuff in a None for the first value in the tuple in that case.
Get an explicit iterator for each sequence. Run the a iterator as a for loop, while manually using next(ib) to get the next value from the b sequence. If we get a StopIteration on the b sequence, we break the loop and then for x in ia: gets the rest of the a sequence; after that for y in ib: will do nothing because that iterator is already exhausted. Alternatively, if the first for x in ia: loop exhausts the a iterator, the second for x in ia: does nothing but there could be values left in the b sequence and the for y in ib: loop collects them.
Single line:
c = zip(a, b) + [(x,) for x in a[len(b):]] + [(x,) for x in b[len(a):]]
If you want to reuse this:
def mergeUsNicely(a, b):
def tupleMe(val):
return (val,)
return zip(a, b) + map(tupleMe, a[len(b):]) + map(tupleMe, b[len(a):])
This answer is an extension of the top answer that allows for arbitrary inputs instead of only two.
import itertools
sentinel = object()
def zip_longest_no_fill(*args):
for i in itertools.zip_longest(*args, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)