I have the foll. list in python:
[1, 2, 3, 4]
Is there an python itertools function that results in foll:
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
This is trivial without itertools:
def fall(it):
ls = []
for x in it:
ls.append(x)
yield ls
for x in fall(xrange(20)):
print x
Note that this works with any iterable, not just a list.
If you still want itertools, something like this should work (py3):
for x in itertools.accumulate(map(lambda x: [x], it)):
print(x)
Again, it's lazy and works with any iterable.
There isn't anything in itertools, that I can think of, but this should work:
def incremental(L):
for i in range(1, len(L)+1):
yield L[:i]
Output:
In [53]: print(*incremental([1, 2, 3, 4]), sep='\n')
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
This can be written as one-liner using list comprehension:
>>> [ list[:x+1] for x in range(len(list)) ]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
If it is must to use itertools, you may use itertools.islice as:
from itertools import islice
my_list = [1, 2, 3, 4]
for i in range(1, len(my_list)+1):
print list(islice(my_list, i))
However there is absolutely no need to use itertools here. You may achieve this via simple list slicing as:
for i in range(len(my_list)):
print my_list[:i+1]
Both of the above solutions will print the result as:
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
One line solution, using partial and islice,
from itertools import islice
from functools import partial
my_list = [1, 2, 3, 4]
[list(l) for l in map(partial(islice, my_list), range(1,len(my_list)+1))]
you get,
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
in other words,
from itertools import islice
from functools import partial
my_list = [1, 2, 3, 4]
p = partial(islice, my_list)
for i in range(1,5):
print(list(p(i)))
you get,
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
You don't need itertools, just use a map:
>>> l = [1, 2, 3, 4]
>>> for sub_list in map(lambda index: l[:index + 1], range(len(l))):
... print sub_list
Related
Let's use a simple example: say I have a list of lists
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
and I want to find all longest lists, which means all lists that maximize the len function. Of course we can do
def func(x):
return len(x)
maxlen = func(max(ll, key=lambda x: func(x)))
res = [l for l in ll if func(l) == maxlen]
print(res)
Output
[[1, 2, 3], [2, 3, 4]]
But I wonder if there are more efficient way to do this, especially when the function is very expensive or the list is very long. Any suggestions?
From a computer science/algorithms perspective, this is a very classical "reduce" problem.
so, pseudocode. It's honestly very straightforward.
metric():= a mapping from elements to non-negative numbers
winner = []
maxmetric = 0
for element in ll:
if metric(element) larger than maxmetric:
winner = [ element ]
maxmetric = metric(element)
else if metric(element) equal to maxmetric:
append element to winner
when the function is very expensive
Note that you do compute func(x) for each element twice, first there
maxlen = func(max(ll, key=lambda x: func(x)))
then there
res = [l for l in ll if func(l) == maxlen]
so it would be beneficial to store what was already computed. functools.lru_cache allow that easily just replace
def func(x):
return len(x)
using
import functools
#functools.lru_cache(maxsize=None)
def func(x):
return len(x)
However, beware as due to way how data are stored argument(s) must be hashable, so in your example you would first need convert list e.g. to tuples i.e.
ll = [(1, 2), (1, 3), (2, 3), (1, 2, 3), (2, 3, 4)]
See descripiton in docs for further discussion
Is not OK use dictionary like below, (this is O(n))
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
from collections import defaultdict
dct = defaultdict(list)
for l in ll:
dct[len(l)].append(l)
dct[max(dct)]
Output:
[[1, 2, 3], [2, 3, 4]]
>>> dct
defaultdict(list, {2: [[1, 2], [1, 3], [2, 3]], 3: [[1, 2, 3], [2, 3, 4]]})
OR use setdefault and without defaultdict like below:
ll = [[1, 2], [1, 3], [2, 3], [1, 2, 3], [2, 3, 4]]
dct = {}
for l in ll:
dct.setdefault(len(l), []).append(l)
Output:
>>> dct
{2: [[1, 2], [1, 3], [2, 3]], 3: [[1, 2, 3], [2, 3, 4]]}
So I have a function which takes a variable number of lists as an argument, then combines those lists into one single list:
def comb_lists(*lists):
sublist = []
for l in lists:
sublist.extend(l)
print(sublist)
>>> comb_lists([1, 2], [3, 4], [5, 6])
[1, 2, 3, 4, 5, 6]
And it works. But I was just wondering if there was a simpler solution? I tried a list comprehension using list unpacking, but that returned a SyntaxError:
def comb_lists(*lists):
sublist = [*l for l in lists]
>>> comb_lists([1, 2], [3, 4], [5, 6])
SyntaxError: iterable unpacking cannot be used in comprehension
Is there any neater or quicker way to do this?
EDIT: itertools looks really useful for this sort of thing. I'd be interested to know if there's any way of doing it that doesn't rely on imports though.
here is the simplest solution
result = sum(lists, [])
There's built-in function chain.form_iterable() in itertools module to do this:
>>> from itertools import chain
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> list(chain.from_iterable(my_list))
[1, 2, 3, 4, 5, 6]
If you do not want to import any module, you can write nested list comprehension to achieve this as:
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> [e for l in my_list for e in l]
[1, 2, 3, 4, 5, 6]
I want to generate or return an append-accumulated list from a given list (or iterator). For a list like [1, 2, 3, 4], I would like to get, [1], [1, 2], [1, 2, 3] and [1, 2, 3, 4]. Like so:
>>> def my_accumulate(iterable):
... grow = []
... for each in iterable:
... grow.append(each)
... yield grow
...
>>> for x in my_accumulate(some_list):
... print x # or something more useful
...
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
This works but is there an operation I could use with itertools.accumulate to facilitate this? (I'm on Python2 but the pure-python implementation/equivalent has been provided in the docs.)
Another problem I have with my_accumulate is that it doesn't work well with list(), it outputs the entire some_list for each element in the list:
>>> my_accumulate(some_list)
<generator object my_accumulate at 0x0000000002EC3A68>
>>> list(my_accumulate(some_list))
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
Option 1:
I wrote my own appending accumulator function to use with itertools.accumulate but considering the LoC and final useful-ness, it seems like a waste of effort, with my_accumulate being more useful, (though may fail in case of empty iterables and consumes more memory since grow keeps growing):
>>> def app_acc(first, second):
... if isinstance(first, list):
... first.append(second)
... else:
... first = [first, second]
... return first
...
>>> for x in accumulate(some_list, app_acc):
... print x
...
1
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
>>> list(accumulate(some_list, app_acc)) # same problem again with list
[1, [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
(and the first returned elem is not a list, just a single item)
Option 2: Figured it would be easier to just do incremental slicing but using the ugly iterate over list length method:
>>> for i in xrange(len(some_list)): # the ugly iterate over list length method
... print some_list[:i+1]
...
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
The easiest way to use accumulate is to make each item in the iterable a list with a single item and then the default function works as expected:
from itertools import accumulate
acc = accumulate([el] for el in range(1, 5))
res = list(acc)
# [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
I have a function that creates all combinations of list items, representing this as a list of lists:
def makeCombos(arr):
yield (sum([map(list, combinations(arr, i)) for i in range(len(arr) + 1)], []))
Calling makeCombos([1,2,3,4,5]) gives me a generator object, but calling .next() does not give me one combo at a time, it gives me the entire list of combos.
How can I turn this into a generator function that I can call?
sum(iterable, []) doesn't create a list of lists. It actually flattens things.
yield (sum(...)) in this line you're just yielding a single item, the flattened list of all combinations.
For Python 2.X sum([(map(list, combinations(arr, i))) ...]) will work, but in Python 3.X map no longer returns a list. Instead it returns a map object. So, if anyones on Python 3.X simply turn this into list(map(.....)) for this to run on 3.X.
I think what you actually want is something like this:
from itertools import combinations
def makeCombos(arr):
for i in range(len(arr) + 1):
for combo in map(list, combinations(arr, i)):
yield combo
#Or call next
combos = makeCombos([1, 2, 3, 4, 5])
for combo in combos:
print combo
An alternative from the comment(s) for a one-liner:
Instead of yielding we can return a generator object and cycle through just as we would with the yield.
e.g. -
from itertools import combinations
def makeCombos(arr):
return (combo for i in range(len(arr) + 1) for combo in map(list, combinations(arr, i)))
combos = makeCombos([1, 2, 3, 4, 5])
....
As for this being "Pythonic" I wouldn't really say so. I actually prefer the nested forloop it is by far more readable.
Although, we can still try to clean it up some more / compact it by doing a few "tricks"
from itertools import combinations as cs #or some other name)
def makeCombos(arr):
return (c for i in range(len(arr) + 1) for c in map(list, cs(arr, i)))
But, now you've lost all readability and this looks like something you'd see in Perl. (the horror!)
Output:
[]
[1]
[2]
[3]
[4]
[5]
[1, 2]
[1, 3]
[1, 4]
[1, 5]
[2, 3]
[2, 4]
[2, 5]
[3, 4]
[3, 5]
[4, 5]
[1, 2, 3]
[1, 2, 4]
[1, 2, 5]
[1, 3, 4]
[1, 3, 5]
[1, 4, 5]
[2, 3, 4]
[2, 3, 5]
[2, 4, 5]
[3, 4, 5]
[1, 2, 3, 4]
[1, 2, 3, 5]
[1, 2, 4, 5]
[1, 3, 4, 5]
[2, 3, 4, 5]
[1, 2, 3, 4, 5]
itertools already has a method for joining iterables together: it's called chain. What you want is something like the following:
def makeCombos(arr):
return chain.from_iterable(combinations(arr, i) for i in range(len(arr) + 1))
Simple, short, and fairly Pythonic in my opinion.
[0.0, 1.0, 2.0, 3.0, 4.0]
I have 5 numbers and two groups, left and right.
Each number has two choices - it can go left or right.
I need a list that contains all partitioning of the list [0,1,2,3,4] into two non empty parts. For example: [([0], [1,2,3,4]), ([0,1], [2,3,4]), ...,]
Note that there are a total of (2^5 -2)/2 partitioning - order doesn't matter and I don't want repeats. Meaning I don't want something like this (if my list was [1,2,3,4]):
[] [1, 2, 3, 4]
[1] [2, 3, 4]
[2] [1, 3, 4]
[1, 2] [3, 4]
[3] [1, 2, 4]
[1, 3] [2, 4]
[2, 3] [1, 4]
[1, 2, 3] [4]
[4] [1, 2, 3]
[1, 4] [2, 3]
[2, 4] [1, 3]
[1, 2, 4] [3]
[3, 4] [1, 2]
[1, 3, 4] [2]
[2, 3, 4] [1]
[1, 2, 3, 4] []
I've looked into all of the itertools functions and none seem to work.
Edit:
for list [i for i in range(16)], which has 16 elements, If I do the following, this is what I see:
n = len(l)//2 + 1
>>> xs = list(chain(*[combinations(l, i) for i in range(1, n)]))
>>> pairs = [(list(x), list(set(l) - set(x))) for x in xs]
>>> print len(pairs)
39202
>>> (2**16-2)/2
32767
In fact, it doesn't work for a list with 6 elements either. I don't see why...
The problem occurs for all even length lists. For example, when I try a length 2 list, I get:
[([0.0], [1.0]), ([1.0], [0.0])]
The stuff is there in itertools, maybe you just weren't looking in the right places.
Here is teh codez:
from collections import OrderedDict
from itertools import chain, combinations
def partition(L):
n = len(L)//2 + 1
xs = chain(*[combinations(L, i) for i in range(1, n)])
pairs = (tuple(sorted([x, tuple(set(L) - set(x))])) for x in xs)
return OrderedDict.fromkeys(pairs).keys()
Output:
>>> for pair in partition([1,2,3,4]):
... left, right = map(list, sorted(pair, key=len))
... print left, right
...
[1] [2, 3, 4]
[2] [1, 3, 4]
[3] [1, 2, 4]
[4] [1, 2, 3]
[1, 2] [3, 4]
[1, 3] [2, 4]
[1, 4] [2, 3]