If you have a list my_list = ['a', 'd', 'e', 'c', 'b', 'f'] and you want to construct a sublist, containing all elements up to a given one, for example my_list_up_to_c = ['a', 'd', 'e'], how can this be done in a way that scales easily? Also can this be made faster by using numpy arrays?
The least amount of code would probably be using .index() (note that this searches till the first occurence of the element in said list):
>>> my_list = ['a', 'd', 'e', 'c', 'b', 'f']
>>> my_list
['a', 'd', 'e', 'c', 'b', 'f']
>>> my_list[:my_list.index('c')] # excluding the specified element
['a', 'd', 'e']
>>> my_list[:my_list.index('c')+1] # including the specified element
['a', 'd', 'e', 'c']
The time complexity of the call to .index() is O(n), meaning it will at most iterate once over the list. The list slicing has complexity O(k) (according to this source), meaning it depends on the size of the slice.
So in the worst case the element you look for is at the end of the list, so your search will run till the end of the list (O(n)) and the slice will copy the whole list as well (also O(n)), resulting in a worst case of O(2n) which is still linear complexity.
Use index() to get the first occurrence of a list item. Then use the slice notation to get the desired part of the list.
>>> my_list = ['a', 'd', 'e', 'c', 'b', 'f']
>>> my_list[:my_list.index('c')]
['a', 'd', 'e']
The itertools solution
In[9]: from itertools import takewhile
In[10]: my_list = ['a', 'd', 'e', 'c', 'b', 'f']
In[11]: list(takewhile(lambda x: x != 'c', my_list))
Out[11]: ['a', 'd', 'e']
In Haskell it would be
takeWhile ((/=) 'c') "adecbf"
Related
I am writing a python code and I need help with a task. I have a list of 8 elements
[A,B,C,D,E,F,G,H]
and I need to find all the combinations of shorter lists (4 elements) in lexicographic order such that two elements are taken from the subset A,C,E,G and the other two from B,D,F,H. I know that there is the library itertools, but I don't know how to combine its functions properly to perform this task
The wording of the question is unclear, but I think this is what you want:
array = ['f','g','d','e','c','b','h','a']
first = sorted(array[::2]) # ['c', 'd', 'f', 'h']
second = sorted(array[1::2]) # ['a', 'b', 'e', 'g']
I think this is what you want.
I need the set of all the new lists with length 4 such that the first two elements are taken from A,C,E,G and the other two are from B,D,F,H and I need them to be in lexicographic order.
We get the possible starting letters and ending letters then combine all possible pairs of each of them into all_lists:
from itertools import combinations
lst = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
starters = lst[::2] # ['A', 'C', 'E', 'G']
enders = lst[1::2] # ['B', 'D', 'F', 'H']
all_lists = []
for a in combinations(starters, 2):
for b in combinations(enders, 2):
all_lists.append(sorted(a + b))
print(all_lists) # Gives [['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'F'], ['A', 'B', 'C', 'H'], ['A', 'C', 'D', 'F'], ['A', 'C', 'D', 'H'], ['A', 'C', 'F', 'H'], ...
print(all_lists == sorted(all_lists)) # False now
(Updated to sort each mini-list.)
Come to think of it you could maybe do the second part with itertools.product.
I want to combine list of lists, here is the below sample
mylist = [[['a', 'b'], ['c', 'd']],
[['e', 'f'], ['g', 'h']]]
and the output should be:
output = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
I also tried using itertools, but here is what it returned
>>> combined = list(itertools.chain.from_iterable(mylist))
>>> combined
>>> [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g', 'h']]
How I can achieve this ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Can anyone highlight whats I'm missing?
The reason why the itertools method didn't work is because what you have isn't a list of lists, but a list of lists of lists. itertools is working properly, its just flattening the list once. Calling the exact same function again with the partially flattened list as an argument will work:
flat = list(itertools.chain.from_iterable(itertools.chain.from_iterable(mylist)))
Or, a simple list comprehension solution:
flat = [item for slist in mylist for sslist in slist for item in sslist]
This basically translates to:
for slist in mylist:
for sslist in slist:
for item in sslist:
flat.append(item)
Keep in mind, both these solutions are only good for dealing with double nesting. If there is a chance you will have to deal with even more nesting, I suggest you look up how to flatten arbitrarily nested lists.
As others have noted, you have two levels here so you need two calls to chain. But you don't actually need the from_iterable call; you can use the * syntax instead:
list(itertools.chain(*itertools.chain(*mylist)))
With numpy.ndarray.flatten():
import numpy as np
mylist = [ [['a', 'b'], ['c', 'd']], [['e', 'f'], ['g', 'h']] ]
a = np.array(mylist).flatten().tolist()
print(a)
The output:
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
I'm looking for a function that would take a list such as [a,b,c,d] and output a list of all the permutations where adjacent indices are swapped i.e. [[b,a,c,d], [a,c,b,d],[a,b,d,c], [d,b,c,a]]
Thanks
Simple way,you can just use a for loop and swap the adjacent items,tmp=l[:] will make a shallow copy,and it won't change original list l.
See more details from What exactly is the difference between shallow copy, deepcopy and normal assignment operation?:
l=['a', 'b', 'c', 'd']
for i in range(len(l)):
tmp=l[:]
tmp[i],tmp[i-1]=tmp[i-1],tmp[i]
print tmp
Result:
['d', 'b', 'c', 'a']
['b', 'a', 'c', 'd']
['a', 'c', 'b', 'd']
['a', 'b', 'd', 'c']
Suppose I have list
l = ['a', 'c', 'b']
and what I want is a list where those elements appear twice, one after the other, so
['a', 'a', 'c', 'c', 'b', 'b']
and I want to do this in the most pythonic way possible.
My half solution is doing something like
[[l[i], l[i]] for i in range(len(l))]
which yields
[['a', 'a'], ['c', 'c'], ['b', 'b']]
From here, I'd have to parse (walk) the list to remove the inner lists and obtain a single flat list.
Anyone has a better idea to do this in one go? Obviously things like l * 2 wouldn't help as it gives ['a', 'c', 'b', 'a', 'c', 'b'] and I want the same elements adjacent.
l_2 = [item for item in l for i in range(n)]
Link to origin: Stackoverflow: Repeating elements of a list n times
Using only list comprehension, you can do:
[i for j in my_list for i in [j]*2]
Output:
>>> my_list = ['a', 'c', 'b']
>>> [i for j in my_list for i in [j]*2]
['a', 'a', 'c', 'c', 'b', 'b']
You can zip the list against itself, then flatten it in a list comprehension.
>>> [i for j in zip(l,l) for i in j]
['a', 'a', 'c', 'c', 'b', 'b']
You can use zip function
l = ['a', 'c', 'b']
a = [i for j in zip(l,l) for i in j]
print(a)
Output
['a', 'a', 'c', 'c', 'b', 'b']
More general:
def ntimes(iterable, times=2):
for elt in iterable:
for _ in range(times):
yield elt
Here is a short solution without list comprehension, using the intuitive idea l*2:
sorted(l*2, key=l.index)
#['a', 'a', 'c', 'c', 'b', 'b']
If you like functional approaches, you can do this:
from itertools import chain, tee
l = ['a', 'c', 'b']
n = 2
list(chain.from_iterable(zip(*tee(l, n))))
While this might not perform as fast as the other answers, it can easily be used for arbitrary iterables (especially when they are infite or when you don't know when they end) by omitting list().
(Note that some of the other answers can also be adapted for arbitrary iterables by replacing their list comprehension by a generator expression.)
I am wondering how to duplicate each element in a list arbitrary of times, e.g.
l = ['a', 'b', 'c']
the duplicate elements in l result in a new list,
n = ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'c', 'c']
so 'a' has been duplicated 3 times, 'b' once, 'c' twice. The number of duplicates for each element are decided by numpy.random.poisson e.g. numpy.random.poisson(2).
Here's a NumPy based vectorized approach using np.repeat to create an array -
np.repeat(l, np.random.poisson([2]*len(l)))
If you need a list as output, append .tolist() there -
np.repeat(l, np.random.poisson([2]*len(l))).tolist()
If you would like to keep at least one entry for each element, add a clipping there with np.random.poisson([2]*len(arr)).clip(min=1).
Multiply each element in the list with the value returned from numpy.random.poisson(2), join it and then feed it to list:
r = list(''.join(i*random.poisson(2) for i in l))
For one run, this randomly results in:
['a', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c']
Since you use np either way, I'd go with Divakar's solution (which, for lists larger than your example, executes faster).
>>> l = ['a', 'b', 'c']
>>> n = []
>>> for e in l:
... n.extend([e] * numpy.random.poisson(2))
...
>>> n
['a', 'a', 'b', 'c']