Related
I have a list of lists. I want to find all flat lists that keeps the order of each sublist. As an example, let's say I have a list of lists like this:
ll = [['D', 'O', 'G'], ['C', 'A', 'T'], ['F', 'I', 'S', 'H']]
It is trivial to get one solution. I managed to write the following code which can generate a random flat list that keeps the order of each sublist.
import random
# Flatten the list of lists
flat = [x for l in ll for x in l]
# Shuffle to gain randomness
random.shuffle(flat)
for l in ll:
# Find the idxs in the flat list that belongs to the sublist
idxs = [i for i, x in enumerate(flat) if x in l]
# Change the order to match the order in the sublist
for j, idx in enumerate(idxs):
flat[idx] = l[j]
print(flat)
This can generate flat lists that looks as follows:
['F', 'D', 'O', 'C', 'A', 'G', 'I', 'S', 'T', 'H']
['C', 'D', 'F', 'O', 'G', 'I', 'S', 'A', 'T', 'H']
['C', 'D', 'O', 'G', 'F', 'I', 'S', 'A', 'T', 'H']
['F', 'C', 'D', 'I', 'S', 'A', 'H', 'O', 'T', 'G']
As you can see, 'A' always appears after 'C', 'T' always appears after 'A', 'O' always appears after 'D', and so on...
However, I want to get all possible solutions.
Please note that :
I want a general code that works for any given list of lists, not just for "dog cat fish";
It does not matter whether there are duplicants or not because every item is distinguishable.
Can anyone suggest a fast Python algorithm for this?
Suppose you are combining the lists by hand. Instead of shuffling and putting things back in order, you would select one list and take its first element, then again select a list and take its first (unused) element, and so on. So the algorithm you need is this: What are all the different ways to pick from a collection of lists with these particular sizes?
In your example you have lists of length 3, 3, 4; suppose you had a bucket with three red balls, three yellow balls and four green balls, which orderings are possible? Model this, and then just pick the first unused element from the corresponding list to get your output.
Say what? For your example, the (distinct) pick orders would be given by
set(itertools.permutations("RRRYYYGGGG"))
For any list of lists, we'll use integer keys instead of letters. The pick orders are:
elements = []
for key, lst in enumerate(ll):
elements.extend( [ key ] * len(lst))
pick_orders = set(itertools.permutations(elements))
Then you just use each pick order to present the elements from your list of lists, say with pop(0) (from a copy of the lists, since pop() is destructive).
Yet another solution, but this one doesn't use any libraries.
def recurse(lst, indices, total, curr):
done = True
for l, (pos, index) in zip(lst, enumerate(indices)):
if index < len(l): # can increment index
curr.append(l[index]) # add on corresponding value
indices[pos] += 1 # increment index
recurse(lst, indices, total, curr)
# backtrack
indices[pos] -= 1
curr.pop()
done = False # modification made, so not done
if done: # no changes made
total.append(curr.copy())
return
def list_to_all_flat(lst):
seq = [0] * len(lst) # set up indexes
total, curr = [], []
recurse(lst, seq, total, curr)
return total
if __name__ == "__main__":
lst = [['D', 'O', 'G'], ['C', 'A', 'T'], ['F', 'I', 'S', 'H']]
print(list_to_all_flat(lst))
Try:
from itertools import permutations, chain
ll = [["D", "O", "G"], ["C", "A", "T"], ["F", "I", "S", "H"]]
x = [[(i1, i2, o) for i2, o in enumerate(subl)] for i1, subl in enumerate(ll)]
l = sum(len(subl) for subl in ll)
def is_valid(c):
seen = {}
for i1, i2, _ in c:
if i2 != seen.get(i1, -1) + 1:
return False
else:
seen[i1] = i2
return True
for c in permutations(chain(*x), l):
if is_valid(c):
print([o for *_, o in c])
Prints:
['D', 'O', 'G', 'C', 'A', 'T', 'F', 'I', 'S', 'H']
['D', 'O', 'G', 'C', 'A', 'F', 'T', 'I', 'S', 'H']
['D', 'O', 'G', 'C', 'A', 'F', 'I', 'T', 'S', 'H']
['D', 'O', 'G', 'C', 'A', 'F', 'I', 'S', 'T', 'H']
['D', 'O', 'G', 'C', 'A', 'F', 'I', 'S', 'H', 'T']
['D', 'O', 'G', 'C', 'F', 'A', 'T', 'I', 'S', 'H']
['D', 'O', 'G', 'C', 'F', 'A', 'I', 'T', 'S', 'H']
['D', 'O', 'G', 'C', 'F', 'A', 'I', 'S', 'T', 'H']
...
['F', 'I', 'S', 'H', 'C', 'D', 'A', 'O', 'T', 'G']
['F', 'I', 'S', 'H', 'C', 'D', 'A', 'T', 'O', 'G']
['F', 'I', 'S', 'H', 'C', 'A', 'D', 'O', 'G', 'T']
['F', 'I', 'S', 'H', 'C', 'A', 'D', 'O', 'T', 'G']
['F', 'I', 'S', 'H', 'C', 'A', 'D', 'T', 'O', 'G']
['F', 'I', 'S', 'H', 'C', 'A', 'T', 'D', 'O', 'G']
You can use a recursive generator function:
ll = [['D', 'O', 'G'], ['C', 'A', 'T'], ['F', 'I', 'S', 'H']]
def get_combos(d, c = []):
if not any(d) and len(c) == sum(map(len, ll)):
yield c
elif any(d):
for a, b in enumerate(d):
for j, k in enumerate(b):
yield from get_combos(d[:a]+[b[j+1:]]+d[a+1:], c+[k])
print(list(get_combos(ll)))
Output (first ten permutations):
[['D', 'O', 'G', 'C', 'A', 'T', 'F', 'I', 'S', 'H'], ['D', 'O', 'G', 'C', 'A', 'F', 'T', 'I', 'S', 'H'], ['D', 'O', 'G', 'C', 'A', 'F', 'I', 'T', 'S', 'H'], ['D', 'O', 'G', 'C', 'A', 'F', 'I', 'S', 'T', 'H'], ['D', 'O', 'G', 'C', 'A', 'F', 'I', 'S', 'H', 'T'], ['D', 'O', 'G', 'C', 'F', 'A', 'T', 'I', 'S', 'H'], ['D', 'O', 'G', 'C', 'F', 'A', 'I', 'T', 'S', 'H'], ['D', 'O', 'G', 'C', 'F', 'A', 'I', 'S', 'T', 'H'], ['D', 'O', 'G', 'C', 'F', 'A', 'I', 'S', 'H', 'T'], ['D', 'O', 'G', 'C', 'F', 'I', 'A', 'T', 'S', 'H']]
For simplicity, let's start with two list item in a list.
from itertools import permutations
Ls = [['D', 'O', 'G'], ['C', 'A', 'T']]
L_flattened = []
for L in Ls:
for item in L:
L_flattened.append(item)
print("L_flattened:", L_flattened)
print(list(permutations(L_flattened, len(L_flattened))))
[('D', 'O', 'G', 'C', 'A', 'T'), ('D', 'O', 'G', 'C', 'T', 'A'), ('D', 'O', 'G', 'A', 'C', 'T'), ('D', 'O', 'G', 'A', 'T', 'C'), ('D', 'O', 'G', 'T', 'C', 'A'), ('D', 'O', 'G', 'T', 'A', 'C'), ('D', 'O', 'C', 'G', 'A', 'T'),
('D', 'O', 'C', 'G', 'T', 'A'), ('D', 'O', 'C', 'A', 'G', 'T'), ('D', 'O', 'C', 'A', 'T', 'G'),
...
Beware that permutations grow very quickly in their sizes.
In your example there are 10 items and Permutation(10, 10) = 3628800.
I suggest you to calculate permutation here to get an idea before running actual code (which may cause memory error/freeze/crash in system).
You can try verifying all possible permutations:
import random
import itertools
import numpy as np
ll = [['D', 'O', 'G'], ['C', 'A', 'T'], ['F', 'I', 'S', 'H']]
flat = [x for l in ll for x in l]
all_permutations = list(itertools.permutations(flat))
good_permutations = []
count = 0
for perm in all_permutations:
count += 1
cond = True
for l in ll:
idxs = [perm.index(x) for i, x in enumerate(flat) if x in l]
# check if ordered
if not np.all(np.diff(np.array(idxs)) >= 0):
cond = False
break
if cond == True:
good_permutations.append(perm)
if count >= 10000:
break
print(len(good_permutations))
It is only a basic solution as it is really slow to compute (I set the count to limit the number of permutations that are verified).
This question already has answers here:
How do I split a list into equally-sized chunks?
(66 answers)
Closed 6 years ago.
I'm trying to create and initialize a matrix. Where I'm having an issue is that each row of my matrix I create is the same, rather than moving through the data set.
I've tried to correct it by checking if the value was already in the matrix and that didn't solve my problem.
def createMatrix(rowCount, colCount, dataList):
mat = []
for i in range (rowCount):
rowList = []
for j in range (colCount):
if dataList[j] not in mat:
rowList.append(dataList[j])
mat.append(rowList)
return mat
def main():
alpha = ['a','b','c','d','e','f','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
mat = createMatrix(5,5,alpha)
print (mat)
The output should be like this:
['a','b','c','d','e'] , ['f','h','i','j','k'], ['l','m','n','o','p'] , ['q','r','s','t','u'], ['v','w','x','y','z']
My issue is I just keep getting the first a,b,c,d,e list for all 5 lists returned
You need to keep track of the current index in your loop.
Essentially you want to turn a list like 0,1,2,3,4,....24 (these are the indices of your initial array, alpha) into:
R1C1, R1C2, R1C3, R1C4, R1C5
R2C1, R2C2... etc
I added the logic to do this the way you are currently doing it:
def createMatrix(rowCount, colCount, dataList):
mat = []
for i in range(rowCount):
rowList = []
for j in range(colCount):
# you need to increment through dataList here, like this:
rowList.append(dataList[rowCount * i + j])
mat.append(rowList)
return mat
def main():
alpha = ['a','b','c','d','e','f','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
mat = createMatrix(5,5,alpha)
print (mat)
main()
which then prints out:
[['a', 'b', 'c', 'd', 'e'], ['f', 'h', 'i', 'j', 'k'], ['l', 'm', 'n', 'o', 'p'], ['q', 'r', 's', 't', 'u'], ['v', 'w', 'x', 'y', 'z']]
The reason you were always receiving a,b,c,d,e is because when you write this:
rowList.append(dataList[j])
what it is effectively doing is it is iterating 0-4 for every row. So basically:
i = 0
rowList.append(dataList[0])
rowList.append(dataList[1])
rowList.append(dataList[2])
rowList.append(dataList[3])
rowList.append(dataList[4])
i = 1
rowList.append(dataList[0]) # should be 5
rowList.append(dataList[1]) # should be 6
rowList.append(dataList[2]) # should be 7
rowList.append(dataList[3]) # should be 8
rowList.append(dataList[4]) # should be 9
etc.
You can use a list comprehension:
>>> li= ['a','b','c','d','e','f','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
>>> [li[i:i+5] for i in range(0,len(li),5)]
[['a', 'b', 'c', 'd', 'e'], ['f', 'h', 'i', 'j', 'k'], ['l', 'm', 'n', 'o', 'p'], ['q', 'r', 's', 't', 'u'], ['v', 'w', 'x', 'y', 'z']]
Or, if you don't mind tuples, use zip:
>>> zip(*[iter(li)]*5)
[('a', 'b', 'c', 'd', 'e'), ('f', 'h', 'i', 'j', 'k'), ('l', 'm', 'n', 'o', 'p'), ('q', 'r', 's', 't', 'u'), ('v', 'w', 'x', 'y', 'z')]
Or apply list to the tuples:
>>> map(list, zip(*[iter(li)]*5))
[['a', 'b', 'c', 'd', 'e'], ['f', 'h', 'i', 'j', 'k'], ['l', 'm', 'n', 'o', 'p'], ['q', 'r', 's', 't', 'u'], ['v', 'w', 'x', 'y', 'z']]
I'm attempting to do work with lists in Python and there is a certain part I've been stuck on:
Objective: Iterate through a master list (alphabet) of x amount of elements, and compare whether the index of said element is a factor of 7. If so, append this element to a new list (final). It seems very simple, and here is the code I've written so far:
def test():
alphabet = ['a', 'a', 'b' 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'li', 'a']
final = []
for letter in alphabet:
if (alphabet.index(letter) % 7 == 0):
final.append(letter)
print final
The output I am getting: ['a', 'a', 'g', 'n', 'u', 'a']
The output I am expecting should return a list of every element in the original list that has an index divisible by 7. I cannot figure out how to account for the duplicates.
Any assistance with this would be much appreciated - thank you very much in advance!
Do:
>>> a = ['a', 'a', 'b' 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'li', 'a']
>>> [j for i,j in enumerate(a) if i%7==0]
['a', 'g', 'n', 'u', 'a']
Note that, on index 2, you have 'b' 'b' which results in 'bb'.
I think this is what you're after
for index, letter in enumerate(alphabet):
if (index % 7 == 0):
final.append(letter)
print final
Two things.
First, instead of using a list to accumulate the results, use a set. Duplicates are automatically eliminated.
And, why look at every letter instead of just at every seventh letter?
final = set()
for i in range(len(alphabet)/7):
final.add(alphabet[i*7])
print final
try this:
>>> alphabet = ['a', 'a', 'b' 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'li', 'a']
>>> [letter for i,letter in enumerate(alphabet) if i%7==0]
['a', 'g', 'n', 'u', 'a']
One of the review questions for my midterm is to understand what this function does and I cannot read it because I don't understand where the parameters come from and how it works. Can somebody with programming experience help?
def enigma(numList, n, pos):
length = len(numList)
if pos == length:
print('Error')
return
newList = []
for i in range(pos):
newList = newList + [numList[i]]
newList = newList + [n]
tailLength = length - pos
counter = tailLength
while counter < length:
newList = newList + [numList[counter]]
counter = counter + 1
return newList
Many times trying some test data reveals the functionality quickly:
>>> enigma('abcdefghijklm', 'X', 0)
['X']
>>> enigma('abcdefghijklm', 'X', 1)
['a', 'X', 'm']
>>> enigma('abcdefghijklm', 'X', 2)
['a', 'b', 'X', 'l', 'm']
>>> enigma('abcdefghijklm', 'X', 3)
['a', 'b', 'c', 'X', 'k', 'l', 'm']
>>> enigma('abcdefghijklm', 'X', 4)
['a', 'b', 'c', 'd', 'X', 'j', 'k', 'l', 'm']
>>> enigma('abcdefghijklm', 'X', 12)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'X', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm']
>>> enigma('abcdefghijklm', 'X', 13)
Error
The code starts with an empty new_list and builds it up in three sections
The repeating the first pos number of elements
Adding the n element
Repeating the trailing pos number of elements
As pos becomes larger than the midpoint, the beginning and trailing elements sections cross over to repeat some items.
I have a list:
word_list = ['dog', 'downvote', 'gestapo']
I would like this:
['d', 'o', 'g', 'w', 'n', 'v', 't', 'e', 's', 'a', 'p']
This is my code;
[list(word_list[j]) for j in range(len(word_list))]
This code returns this:
[['d', 'o', 'g'], ['d', 'o', 'w', 'n', 'v', 'o', 't', 'e'], ['g', 'e', 's', 't', 'a', 'p', 'o']]
Instead I tried this:
[(word_list[j])[k] for j in range(len(word_list)) for k in len(word_list[j])]
This returns an error: 'int' object is not iterable
I would like to rectify and update my final attempt so that I get the desired output.
If you want to preserve the original order of characters (as in the words from word_list):
def f(seq):
seen = set()
for x in (x for word in word_list for x in word):
if x not in seen:
seen.add(x)
yield x
list(f(word_list)) # ['d', 'o', 'g', 'w', 'n', 'v', 't', 'e', 's', 'a', 'p']
If you don't, just construct the set using set comprehension:
{x for word in word_list for x in word} # {'e', 'd', 'n', 'a', 't', 'w', 'o', 'g', 'v', 's', 'p'}
Although I think you are all technically correct.
The right way to do it in python would probably be:
from itertools import chain
set(chain(*word_list))
k=[]
for j in [list(i) for i in word_list]:
k += j
print list(set(k))
>>>>['d', 'o', 'g', 'w', 'n', 'v', 't', 'e', 's', 'a', 'p']
You could use reduce with operator.add, and a set.
>>> import operator
>>> words = ['dog', 'downvote', 'gestapo']
>>> set(reduce(operator.add, words))
set(['a', 'e', 'd', 'g', 'o', 'n', 'p', 's', 't', 'w', 'v'])
If you want it to be a list:
>>> list(set(reduce(operator.add, words)))
['a', 'e', 'd', 'g', 'o', 'n', 'p', 's', 't', 'w', 'v']
Note: it's not alphabetical order.
If the order is important, a simple way is to use(abuse?) an OrderedDict
>>> word_list = ['dog', 'downvote', 'gestapo']
>>> from collections import OrderedDict
>>> from itertools import chain
>>> OrderedDict.fromkeys(chain.from_iterable(word_list)).keys()
['d', 'o', 'g', 'w', 'n', 'v', 't', 'e', 's', 'a', 'p']