How to do selective combination in Python List? - python

I'm not really sure how to frame my question, but here's a try. I have a list of strings and tuples of strings. I want all combinations such that I pick only one value from each tuple.
It's much easier to demonstrate with an example.
Input:
x = ['a', ('b', 'c'), ('d', 'e'), 'f']
Output:
y = [
['a', 'b', 'd', 'f'],
['a', 'c', 'd', 'f'],
['a', 'b', 'e', 'f'],
['a', 'c', 'e', 'f']
]
Example 2:
Input:
x = ['a', ('b', 'c'), ('d', 'e'), 'f', ('g', 'h')]
Output:
y = [
['a', 'b', 'd', 'f', 'g'],
['a', 'c', 'd', 'f', 'g'],
['a', 'b', 'e', 'f', 'g'],
['a', 'c', 'e', 'f', 'g'],
['a', 'b', 'd', 'f', 'h'],
['a', 'c', 'd', 'f', 'h'],
['a', 'b', 'e', 'f', 'h'],
['a', 'c', 'e', 'f', 'h']
]

x = ['a', ('b', 'c'), ('d', 'e'), 'f', ('g', 'h')]
First normalize your input
x = [tuple(xx) for xx in x if not isinstance(x, tuple)]
Then:
import iterools
list(itertools.product(*x))
In the output your have a list of tuples, it should be very easy to get list of list as you want.
Actually the normalization step is not necessary.

Related

Creating minimal groups from two lists

I have a tuple containing a letter/name and a list of chars. I also have a list of purely chars. The idea is that I want to create new lists containing all the possible combinations of the letter/name from the tuple. Where their respective char list contains all the chars which is in the other char list. However this new list should be minimal, meaning there shouldn't be any redudant letter/names. An example,
I have the following tuples,
('C', ['l'])
('D', ['l', 'q'])
('E', ['q', 's'])
('F', ['s'])
('H', ['q', 's'])
I should mention that the tuples are in a list aswell. So a list of tuples
The input list is, ['l', 'q', 's']
In this example I should get the following lists,
['C', 'E']
['C', 'H']
['D', 'E']
['D', 'F']
['D', 'H']
Because 'C' and 'E' combined gives ['l', 'q', 's'] and so forth. It should not return ['C', 'F', 'H'] Because here 'F' would be redundant.
My first thought was that I would check the result of appending each of them together. However this would not work for a larger example where the return list is larger than two chars.
This worked for me:
list_tuples = [('C', ['l']), ('D', ['l', 'q']),
('E', ['q', 's']), ('F', ['s']), ('H', ['q', 's'])]
input_list = ['l', 'q', 's']
out_list = []
for i in range(len(list_tuples)):
for one_tuple in list_tuples:
if list_tuples[i][0] != one_tuple[0]:
to_compare_list = list_tuples[i][1] + one_tuple[1]
counter = 0
for one_letter in input_list:
if one_letter in to_compare_list:
counter += 1
if counter >= len(input_list):
out_list.append([list_tuples[i][0], one_tuple[0]])
out_list = out_list[:int(len(out_list) / 2)]
print(out_list)
Output:
[['C', 'E'], ['C', 'H'], ['D', 'E'], ['D', 'F'], ['D', 'H']]
Please note that this code is not efficient, for better results refer to the itertools library in python that counts with permutation and combination modules.
EDIT
A corrected and more general solution:
import itertools
import copy
list_tuples = [('A', ['a', 'b', 'd', 'e']), ('B', ['d', 'e', 'i']), ('C', ['i', 'l', 'n']), ('D', ['l', 'n', 'o', 'r']), ('E', [
'n', 'o', 'r', 's', 't']), ('F', ['a', 'b', 'r', 's', 't']), ('G', ['a', 'e', 'i', 'o']), ('H', ['b', 'e', 's'])]
input_list = ['a', 'b', 'd', 'e', 'i', 'l', 'n', 'o', 'r', 's', 't']
dic_of_list_tuples = dict(list_tuples)
keys = dic_of_list_tuples.keys()
out_list = []
posible_combinations = []
letter_space = []
for i in range(len(keys)):
posible_combinations += list(itertools.combinations(keys, i + 1))
for one_combinations in posible_combinations:
for one_letter in one_combinations:
letter_space += dic_of_list_tuples[one_letter]
if (all(item in letter_space for item in input_list)):
out_list.append(list(one_combinations))
letter_space = []
filter_out_list = copy.deepcopy(out_list)
for one_list in out_list:
for i in range(len(out_list)):
if (one_list != out_list[i]) and (all(item in out_list[i] for item in one_list)):
try:
filter_out_list.remove(out_list[i])
except ValueError:
pass
print(filter_out_list)
Output:
[['A', 'C', 'E'], ['B', 'D', 'F'], ['A', 'B', 'D', 'E'], ['A', 'C', 'D', 'F'], ['A', 'C', 'F', 'G'], ['A', 'D', 'E', 'G'], ['A', 'D', 'F', 'G'], ['B', 'C', 'E', 'F'], ['B', 'C', 'F', 'G'], ['B', 'C', 'E', 'G', 'H'], ['B', 'D', 'E', 'G', 'H']]

Is there a pythonic way of permuting a list of list?

I have a list of lists containing unique strings and I want to produce an arbitrary number of different ways of sorting it. The list might look like the following:
list = [[a], [b,c], [d], [e,f,g]]
The order of the lists need to be the same but I want to shuffle the ordering within a list and then have them in a single list, e.g
list1 = [a,b,c,d,e,f,g]
list2 = [a,c,b,d,f,e,g]
...
...
listN = [a,c,b,d,f,g,e]
What is a good pythonic way of achieving this? I'm on python 2.7.
from itertools import permutations, product
L = [['a'], ['b','c'], ['d'], ['e', 'f', 'g']]
for l in product(*map(lambda l: permutations(l), L)):
print([item for s in l for item in s])
produces:
['a', 'b', 'c', 'd', 'e', 'f', 'g']
['a', 'b', 'c', 'd', 'e', 'g', 'f']
['a', 'b', 'c', 'd', 'f', 'e', 'g']
['a', 'b', 'c', 'd', 'f', 'g', 'e']
['a', 'b', 'c', 'd', 'g', 'e', 'f']
['a', 'b', 'c', 'd', 'g', 'f', 'e']
['a', 'c', 'b', 'd', 'e', 'f', 'g']
['a', 'c', 'b', 'd', 'e', 'g', 'f']
['a', 'c', 'b', 'd', 'f', 'e', 'g']
['a', 'c', 'b', 'd', 'f', 'g', 'e']
['a', 'c', 'b', 'd', 'g', 'e', 'f']
['a', 'c', 'b', 'd', 'g', 'f', 'e']
You can do this by taking the Cartesian product of the permutations of the sub-lists, and then flattening the resulting nested tuples.
from itertools import permutations, product, chain
lst = [['a'], ['b', 'c'], ['d'], ['e', 'f', 'g']]
for t in product(*[permutations(u) for u in lst]):
print([*chain.from_iterable(t)])
output
['a', 'b', 'c', 'd', 'e', 'f', 'g']
['a', 'b', 'c', 'd', 'e', 'g', 'f']
['a', 'b', 'c', 'd', 'f', 'e', 'g']
['a', 'b', 'c', 'd', 'f', 'g', 'e']
['a', 'b', 'c', 'd', 'g', 'e', 'f']
['a', 'b', 'c', 'd', 'g', 'f', 'e']
['a', 'c', 'b', 'd', 'e', 'f', 'g']
['a', 'c', 'b', 'd', 'e', 'g', 'f']
['a', 'c', 'b', 'd', 'f', 'e', 'g']
['a', 'c', 'b', 'd', 'f', 'g', 'e']
['a', 'c', 'b', 'd', 'g', 'e', 'f']
['a', 'c', 'b', 'd', 'g', 'f', 'e']
If you need to do this in Python 2, you can replace the print line with this:
print list(chain.from_iterable(t))
Here's a more compact version, inspired by ewcz's answer:
for t in product(*map(permutations, lst)):
print list(chain.from_iterable(t))
This might not be the most elegant solution, but I think it does what you want
from itertools import permutations
import numpy as np
def fac(n):
if n<=1:
return 1
else:
return n * fac(n-1)
lists = [['a'], ['b','c'], ['d'], ['e','f','g']]
combined = [[]]
for perm in [permutations(l,r=len(l)) for l in lists]:
expanded = []
for e in list(perm):
expanded += [list(l) + list(e) for l in combined]
combined = expanded
## check length
print np.prod(map(fac,map(len,lists))), len(combined)
print '\n'.join(map(str,combined))
You can flatten the list then simply generate its permutations:
from itertools import chain, permutations
li = [['a'], ['b','c'], ['d'], ['e','f','g']]
flattened = list(chain.from_iterable(li))
for perm in permutations(flattened, r=len(flattened)):
print(perm)
>> ('a', 'b', 'c', 'd', 'e', 'f', 'g')
('a', 'b', 'c', 'd', 'e', 'g', 'f')
('a', 'b', 'c', 'd', 'f', 'e', 'g')
('a', 'b', 'c', 'd', 'f', 'g', 'e')
('a', 'b', 'c', 'd', 'g', 'e', 'f')
('a', 'b', 'c', 'd', 'g', 'f', 'e')
('a', 'b', 'c', 'e', 'd', 'f', 'g')
('a', 'b', 'c', 'e', 'd', 'g', 'f')
('a', 'b', 'c', 'e', 'f', 'd', 'g')
...
...
...
from itertools import chain, permutations
your_list = [[a], [b,c], [d], [e,f,g]]
flattened = chain.from_iterable(your_list)
perms = permutations(flattened)
for perm in perms:
print perm
References:
permutations in Python 2
chain in Python 2

Extracting Unique String Combinations from List of List in Python

I'm trying to extract all the unique combinations of strings from a list of lists in Python. For example, in the code below, ['a', 'b','c'] and ['b', 'a', 'c'] are not unique, while ['a', 'b','c'] and ['a', 'e','f'] or ['a', 'b','c'] and ['d', 'e','f'] are unique.
I've tried converting my list of lists to a list of tuples and using sets to compare elements, but all elements are still being returned.
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
# converting list of list to list of tuples, so they can be converted into a set
combos = [tuple(item) for item in combos]
combos = set(combos)
grouping_list = set()
for combination in combos:
if combination not in grouping_list:
grouping_list.add(combination)
##
print grouping_list
>>> set([('a', 'b', 'c'), ('c', 'a', 'b'), ('d', 'e', 'f'), ('c', 'b', 'a'), ('c', 'f', 'b')])
How about sorting, (and using a Counter)?
from collections import Counter
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
combos = Counter(tuple(sorted(item)) for item in combos)
print(combos)
returns:
Counter({('a', 'b', 'c'): 3, ('d', 'e', 'f'): 1, ('b', 'c', 'f'): 1})
EDIT: I'm not sure if I'm correctly understanding your question. You can use a Counter to count occurances, or use a set if you're only interested in the resulting sets of items, and not in their uniqueness.
Something like:
combos = set(tuple(sorted(item)) for item in combos)
Simply returns
set([('a', 'b', 'c'), ('d', 'e', 'f'), ('b', 'c', 'f')])
>>> set(tuple(set(combo)) for combo in combos)
{('a', 'c', 'b'), ('c', 'b', 'f'), ('e', 'd', 'f')}
Simple but if we have same elements in the combo, it will return wrong answer. Then, sorting is the way to go as suggested in others.
How about this:
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
print [list(y) for y in set([''.join(sorted(c)) for c in combos])]

Sorting a list by a key

Currently working on a transposition problem. What I have so far is that a user enters a message and that message is encrypted into a list, like below:
['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
What I have for the next stage of the cipher is putting this into a table with a key inputted from the user. So if the user enters 'CODE' it outputs this:
2: Enter the keyword for final encryption: code
C O D E
['B', 'C', 'D', 'E']
['D', 'E', 'D', 'A']
['F', 'D', 'D', 'D']
['B', 'E', 'F', 'E']
['D', 'A', 'E', 'A']
['F', 'E', 'B', 'C']
The next stage is to take each value of each column and print the values corresponding to its alphabetical column. So my expected output would be:
C D E O
['B', 'D', 'E', 'C']
['D', 'D', 'A', 'E']
['F', 'D', 'D', 'D']
['B', 'F', 'E', 'E']
['D', 'E', 'A', 'A']
['F', 'B', 'C', 'E']
The problem I'm having is trying to know how to put each of the values in their corresponding column and printing them.
Here's what I have so far:
def encodeFinalCipher():
matrix2 = []
# Convert keyword to upper case
key = list(keyword.upper())
# Convert firstEncryption to a string
firstEncryptionString = ''.join(str(x) for x in firstEncryption)
# Print the first table that will show the firstEncryption and the keyword above it
keywordList = list(firstEncryptionString)
for x in range(0,len(keywordList),len(keyword)):
matrix2.append(list(keywordList[x:x+len(keyword)]))
# Print the un-ordered matrix to the screen
print (' %s' % ' '.join(map(str, key)))
for letters in matrix2:
print (letters)
unOrderedMatrix = [[matrix2[i][j] for i in range(len(matrix2))] for j in range(len(matrix2[0]))]
for index, item in enumerate (unOrderedMatrix):
print("\n",index, item)
index = sorted(key)
print(index)
I get the output of the sorted key:
['A', 'K', 'M', 'R']
What I would like to know is how can this sorted key be applied to the values they represent? I know I can get the first column by doing this:
print(unOrderedMatrix[0])
Which gets me the list of the first column.
Any help would be much appreciated. Complete beginner on Python
msg = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC', '12']
key = 'CODE'
# 'flatten' the message
msg = ''.join(msg)
key_length = len(key)
#create a dictionary with the letters of the key as the keys
#use a slice to create the values
columns = {k:msg[i::key_length] for i, k in enumerate(key)}
print columns
# sort the columns on the key letters
columns = sorted(columns.items())
print columns
# separate the key from the columnar data
header, data = zip(*columns)
print header
# transpose and print
for thing in zip(*data):
print thing
>>>
{'C': 'BDFBDF1', 'E': 'EADEAC', 'D': 'DDDFEB', 'O': 'CEDEAE2'}
[('C', 'BDFBDF1'), ('D', 'DDDFEB'), ('E', 'EADEAC'), ('O', 'CEDEAE2')]
('C', 'D', 'E', 'O')
('B', 'D', 'E', 'C')
('D', 'D', 'A', 'E')
('F', 'D', 'D', 'D')
('B', 'F', 'E', 'E')
('D', 'E', 'A', 'A')
('F', 'B', 'C', 'E')
>>>
code = raw_input("Enter the keyword for final encryption:")
user_input = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
user_input = ''.join(user_input)
matrix = [user_input[i:i+len(code)] for i in range(0, len(user_input), len(code))]
matrix.insert(0, code)
result = sorted([[matrix[j][ind] for j in range(len(matrix))] for ind in range(len(code)) ], key= lambda i:i[0])
for row in [[each[ind] for each in result] for ind in range(len(result[0]))]:
print row
Print row results as:
Enter the keyword for final encryption:CODE
['C', 'D', 'E', 'O']
['B', 'D', 'E', 'C']
['D', 'D', 'A', 'E']
['F', 'D', 'D', 'D']
['B', 'F', 'E', 'E']
['D', 'E', 'A', 'A']
['F', 'B', 'C', 'E']
Here is something to get you started (you may want to separate the loop one-liner to smaller bits):
# define data
data = [['B', 'C', 'D', 'E'], ['D', 'E', 'D', 'A'], ['F', 'D', 'D', 'D'], ['B', 'E', 'F', 'E'], ['D', 'A', 'E', 'A'], ['F', 'E', 'B', 'C']]
# choose code word
code = 'code'
# add original locations to code word [(0, c), (1, o), (2, d), (3, e))]
# and sort them alphabetically!
code_with_locations = list(sorted(enumerate(code)))
print code_with_locations # [(0, 'c'), (2, 'd'), (3, 'e'), (1, 'o')]
# re-organize data according to new indexing
for index in range(len(data)):
# check if code is shorter than list in current index,
# or the other way around, don't exceed either list
max_substitutions = min(map(len, [code_with_locations, data[index]]))
# create a new list according to new indices
new_list = []
for i in range(max_substitutions):
current_index = code_with_locations[i][0]
new_list.append(data[index][current_index])
# replace old list with new list
data[index] = new_list
print data
Output for 'code' would be:
[['B', 'D', 'E', 'C'],
['D', 'D', 'A', 'E'],
['F', 'D', 'D', 'D'],
['B', 'F', 'E', 'E'],
['D', 'E', 'A', 'A'],
['F', 'B', 'C', 'E']]
With help from itertools
from pprint import pprint
from itertools import chain, izip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
z = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
input = 'CODE'
pprint([[b for (a, b) in sorted(zip(input, x))]
for x in grouper(chain.from_iterable(z), len(input))])
[['B', 'D', 'E', 'C'],
['D', 'D', 'A', 'E'],
['F', 'D', 'D', 'D'],
['B', 'F', 'E', 'E'],
['D', 'E', 'A', 'A'],
['F', 'B', 'C', 'E']]

Sorting a list of lists in python alphabetically by "column"

I have a 2d list of characters like
[['J', 'A', 'M', 'E', 'S'],
['F', 'C', 'A', 'A', 'A'],
['F', 'A', 'B', 'B', 'B']]
What is the best way to go about sorting the first list alphabetically, with the proceeding lists following, ie:
[['A', 'E', 'J', 'M', 'S'],
['C', 'A', 'F', 'A', 'A'],
['A', 'B', 'F', 'B', 'B']]
You can use zip():
>>> [list(t) for t in zip(*sorted(zip(*s)))]
[['A', 'E', 'J', 'M', 'S'], ['C', 'A', 'F', 'A', 'A'], ['A', 'B', 'F', 'B', 'B']]
where s is your list of lists.
The other answers demonstrate how it can be done in one line. This answer illustrates how this works:
Given a list, l:
In [1]: l = [['J', 'A', 'M', 'E', 'S'],
...: ['F', 'C', 'A', 'A', 'A'],
...: ['F', 'A', 'B', 'B', 'B']]
Group the columns into tuples, by passing each row into zip():
In [2]: zip(*l)
Out[2]:
[('J', 'F', 'F'),
('A', 'C', 'A'),
('M', 'A', 'B'),
('E', 'A', 'B'),
('S', 'A', 'B')]
Sort this list of tuples:
In [3]: sorted(zip(*l))
Out[3]:
[('A', 'C', 'A'),
('E', 'A', 'B'),
('J', 'F', 'F'),
('M', 'A', 'B'),
('S', 'A', 'B')]
Note that if the first list contains duplicate items then this sort is not a stable sort.
Transpose the list again to get three lists of tuples:
In [4]: zip(*sorted(zip(*l)))
Out[4]:
[('A', 'E', 'J', 'M', 'S'),
('C', 'A', 'F', 'A', 'A'),
('A', 'B', 'F', 'B', 'B')]
Convert the list of tuples back to a list of lists, using a list comprehension:
In [5]: [list(t) for t in zip(*sorted(zip(*l)))]
Out[5]:
[['A', 'E', 'J', 'M', 'S'],
['C', 'A', 'F', 'A', 'A'],
['A', 'B', 'F', 'B', 'B']]
>>> l = [['J', 'A', 'M', 'E', 'S'],
... ['F', 'C', 'A', 'A', 'A'],
... ['F', 'A', 'B', 'B', 'B']]
>>> zip(*sorted(zip(*l)))
[('A', 'E', 'J', 'M', 'S'), ('C', 'A', 'F', 'A', 'A'), ('A', 'B', 'F', 'B', 'B')]
if you need lists in result:
>>> map(list, zip(*sorted(zip(*l))))
[['A', 'E', 'J', 'M', 'S'], ['C', 'A', 'F', 'A', 'A'], ['A', 'B', 'F', 'B', 'B']]

Categories