all possible permutations for a string - python

Having a string='january' ,
how can I generate following cases:
case1(Replacing 1 character) => taking j and replace it with all ASCII letters(a-z). then do the same with: a , n , u , a , r , y.
Basically we would have
(Aanuary , Banuary ,..... ,Zanuary )+ (jAnuary , jBanuary .....jZanuary) + ....+(januarA , januarB , ....., januarZ)
I have done this part using following code, However, I have no idea how to do it for more than one letter since there are lots of permutations.
monthName= 'january'
asci_letters = ['a' , 'b' , .... , 'z']
lst = list(monthName)
indxs = [i for i , _ in enumerate(monthName)]
oneLetter=[]
for i in indxs:
word = monthName
pos = list(word)
for j in asci_letters:
pos[i] = j
changed = ("".join(pos))
oneLetter.append(changed)
Case2: Taking 2 characters and replacing them:
(AAnuary , ABnuary ,.....,AZanuary) + (BAnuary , BBanuary, .... , BZanuary) + (AaAuary , AaBuary,.....,AaZuary) + ...... + (januaAB , .... , januaAZ)
Case3 : doing the same for 3 characters
Case7: doing the same for 7 characters(length of string)
To summarize, I want to create all possible cases of replacing, 1 letter, 2 letters,3 letters, up to all letters of a string.

It's very likely that you can't hold all these permutations in memory because it will quickly become very crowded.
But to get all indices for the cases you can use itertools.combinations. For 1 it will give the single indices:
from itertools import combinations
string_ = 'january'
length = len(string_)
print(list(combinations(range(length), 1)))
# [(0,), (1,), (2,), (3,), (4,), (5,), (6,)]
Likewise you can get the indices for case 2-7:
print(list(combinations(range(length), 2)))
# [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 2), (1, 3), (1, 4),
# (1, 5), (1, 6), (2, 3), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6),
# (4, 5), (4, 6), (5, 6)]
Then it's just a matter of inserting the itertools.product of string.ascii_uppercase at the given indices:
from itertools import product
import string
print(list(product(string.ascii_uppercase, repeat=1)))
# [('A',), ('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',)]
Likewise for different repeats given the "case".
Putting this all together:
def all_combinations(a_string, case):
lst = list(a_string)
length = len(lst)
for combination in combinations(range(length), case):
for inserter in product(string.ascii_uppercase, repeat=case):
return_string = lst.copy()
for idx, newchar in zip(combination, inserter):
return_string[idx] = newchar
yield ''.join(return_string)
Then you can get all desired permutations for each case by:
list(all_combinations('january', 2)) # case2
list(all_combinations('january', 4)) # case4
list(all_combinations('january', 7)) # case7
Or if you need all of them:
res = []
for case in [1, 2, 3, 4, 5, 6, 7]:
res.extend(all_combinations('january', case))
But that will require a lot of memory.

You can use itertools.combinations_with_replacement for this, which gives you an iterator with all permutations:
from itertools import combinations_with_replacement
# First Param is an iterable of possible values, second the length of the
# resulting permutations
combinations = combinations_with_replacement('ABCDEFGHIJKLMNOPQRSTUVWXYZ',7)
# Then you can iterate like this:
for combination in combinations:
#Do Stuff here
Don't try to convert this iterator to a list of all values, because you probably gonna get a MemoryException.
For your distance you might want to use python distance package. (You need to install it via pip first).
For your case, that you want to get all combinations for Characters a-z with length = 7 (because of January):
import distance
from itertools import combinations_with_replacement
str_to_compary_with = "JANUARY"
for i in range(len(str_to_compare_with):
combinations = combinations_with_replacement('ABCDEFGHIJKLMNOPQRSTUVWXYZ', i+1)
# Then you can iterate like this:
for combination in combinations:
# This is calculating the hamming distance for the combination with the string you want to compare to
# Here you have to figure out yourself if you want to save that output to a file or whatever you wanna do with the distance
hamming_dist = distance.hamming(''.join(combination), str_to_compare_with)

This should do everything that you wanted with help of product and permutations:
from itertools import product, permutations
monthName= 'january'
letters = list('abcdefghijklmnopqrstuvwxyz')
n = len(monthName)
indxs = range(n)
mn = list(monthName)
cases = {k: [] for k in range(2, n+1)}
for num in range(2, n+1):
letter_combos = list(product(*[letters for _ in range(num)]))
positions = permutations(indxs, num)
for p in positions:
for l in letter_combos:
l = iter(l)
for i in p:
mn[i] = next(l)
mn = ''.join(mn)
cases[num].append(mn)
mn = list(monthName)

If you want to know how it is working, you can test this with a subset of letters, say from A-F:
x = []
for i in range(65,70): #subset of letters
x.append(chr(i))
def recurse(string,index,arr):
if(index>len(string)-1):
return
for i in range(index,len(string)):
for item in x:
temp = string[:i]+item+string[i+1:]
arr.append(temp)
recurse(temp,i+1,arr)
arr = []
recurse('abc',0,arr)
print arr

Related

List comprehension from a set whose elements are tuples composed of two tuples

I have a huge list of tuples each containing another two tuples like e.g.
lst = [((0,2,1), (2,1,3)), ((3,2,1), (0,1,1)), ...]
Many of the elements of this list are not acceptable under certain criteria and I am trying to create a new list composed by those elements satisfying those conditions. Among others, I would like to remove those elements whose left (resp. right) tuple contains a zero but the right (resp. left) tuple has no zero in the same position. Also those elements whose left tuple contains two consecutive non zero numbers (in a given range) and the number located in the right tuple in the same position than the one where the repetition appears is not a 1. To do this, I have tried:
acceptable_lst = [elem for elem in lst for j in range(3) if not ((elem[0][j] == 0 and
elem[1][j] != 0) or (j < 2 and elem[0][j] != 0 and elem[0][j] == elem[0][j+1]
and elem[1][j+1] != 1)]
When I apply this code to e.g.
lst = [((3,2,2), (1,2,3)),
((0,1,3), (2,2,3)),
((1,1,2), (3,3,3)),
((0,2,2), (3,3,3)),
((2,2,1), (3,1,3))]
I would like to get:
acceptable_lst = [((2,2,1), (3,1,3))]
Why? The first element in lst has a rep of 2 in the left tuple, the second 2 in the third position, but the third element in the right tuple is not a 1. The second element has a zero in the first position of the left tuple and a non zero in the same position of the right tuple, so on ... Only the last element in lst satisfies the above conditions.
However what I get is
[((3, 2, 2), (1, 2, 3)),
((3, 2, 2), (1, 2, 3)),
((0, 1, 3), (2, 2, 3)),
((0, 1, 3), (2, 2, 3)),
((1, 1, 2), (3, 3, 3)),
((1, 1, 2), (3, 3, 3)),
((0, 2, 2), (3, 3, 3)),
((2, 2, 1), (3, 1, 3)),
((2, 2, 1), (3, 1, 3)),
((2, 2, 1), (3, 1, 3))]
which indicates that my code is completely wrong. How can I implement what I need?
Check the validity in a function
def valid(elemLeft, elemRight):
lastItem = None
for i in range(3):
if elemLeft[i] == 0 and elemRight[i] != 0:
return False
if lastItem != None:
if elemLeft[i] == lastItem and elemRight[i] != 1:
return False
lastItem = elemLeft[i]
return True
lst = [((3,2,2),(1,2,3)), ((0,1,3),(2,2,3)), ((1,1,2),(3,3,3)), ((0,2,2),(3,3,3)), ((2,2,1),(3,1,3))]
acceptable_lst = [elem for elem in lst if valid(elem[0],elem[1])]
print(acceptable_lst)
You are doing two for loops in your list comprehension.
[ elem for elem in lst for j in range(3) if condition ]
is equivalent to:
out_list = []
for elem in lst:
for j in range(3):
if condition:
out_list.append(elem)
If you have to use list comprehension for this task, you could modify it:
import numpy as np
acceptable_lst = [elem for elem in lst
if not (np.any([(elem[0][j] == 0 and elem[1][j] != 0) for j in range(3)]))
and not np.any([(j < 2 and elem[0][j] != 0 and elem[0][j] == elem[0][j+1] and elem[1][j+1] != 1) for j in range(3)])]

How to find position of letters in array of strings

I have a problem where I need to find where letters are in a matrix of strings. The input is: maze1=['*****','* * *','* G**','*D***','* ***']
The expected output is a tuple of the letters coordinates. For this example, the expected output is [(2,2),(3,1)]
Here is my code so far I run into a problem when checking if the element is a letter:
treasure=[]
for i in range(len(maze)):
for j in range(len(maze)):
if maze[i][j].lower().isAlpha():
treasure[i] = maze[i][j]
print(treasure)
In [2]: maze1=['*****','* * *','* G**','*D***','* ***']
In [3]: [(i,j) for i,s in enumerate(maze1) for j,char in enumerate(s) if char.isalpha()]
Out[3]: [(2, 2), (3, 1)]
Using regex
import re
result=[]
maze1 =['*****','* * *','* G**','*D***','* ***']
for counter, value in enumerate(maze1):
m=re.search('([a-zA-Z]+)',value)
if m :
result.append((counter, m.start()))
Output
[(2, 2), (3, 1)]
Assuming your maze has 5 columns:
[(p//5,p%5) for p,l in enumerate("".join(maze1)) if l.isalpha()]
returns:
[(2, 2), (3, 1)]

How to make combinations nCr from x to y (nCx -nCy)

I want to make some combinations of all 160 elements in my list, but I don't want to make all possible combinations or it will never end. I just want some, lets say 1,2,3,4.
Instead of doing one by one:
combination = itertools.combinations(lst, 1)
combination = itertools.combinations(lst, 2)
combination = itertools.combinations(lst, 3)
combination = itertools.combinations(lst, 4)
How can I do all 4???
How about this simple for loop:
comb = []
for i in range (1,5): # (start, end + 1)
comb[i] = itertools.combinations(lst, i)
You can create single iterator containing all the combinations with itertools.chain.from_iterable:
combination = chain.from_iterable(combinations(lst, i) for i in range(1,5))
Example with shorter input:
>>> list(chain.from_iterable(combinations(range(3), i) for i in range(1,3)))
[(0,), (1,), (2,), (0, 1), (0, 2), (1, 2)]

Out of range index

I am trying to make a program that will count the numbers in the list number, and would search for a sum of 10 in sequence_len numbers.
In the minute it gets a 10, it should stop.
1. With this code I have an error. what should I do?
total=total+(list_n[i+n])
IndexError: list index out of range
2.I want the first for to be stop if Im finding a sum of then. Is it write to "break" at the end as I did or should I write i=len(list_n)?
number = 1234
sequence_len = 2
list_n=[]
total=0
b="false"
list_t=[]
for j in str(number):
list_n.append(int(j))
c=len(list_n)
for i in list_n:
n=0
while n<sequence_len:
total=total+(list_n[i+n])
n=n+1
if total==10:
b=true
seq=0
while seq>sequence_len:
list_t.append(list_t[i+seq])
seq=seq+1
break
else:
total=0
if b=="true":
break
if b=="false":
print "Didn’t find any sequence of size", sequence_len
else:
print "Found a sequence of size", sequence_len ,":", list_t
You have a couple of errors. First with the basic:
b=true
This needs to the True, otherwise, python will look for the true variable.
Secondly, i actually contains the value of the variable for that iteration (loop). For example:
>>> l = ['a', 'b', 'c']
>>> for i in l: print i
a
b
c
Because of this, you cannot use it as an index, as indexes have to be integers. So, what you need to do is use enumerate, this will generate a tuple of both the index and the value, so something like:
for i, var in enumerate(list_n):
n = 0
An example of enumerate in action:
>>> var = enumerate([1,6,5,32,1])
>>> for x in var: print x
(0, 1)
(1, 6)
(2, 5)
(3, 32)
(4, 1)
And this statement should has logical problems I believe:
total = total + (list_n[i + n - 1])
If you want to get a sum of 10 from a list of numbers, you can use this brute-force technique:
>>> list_of_n = [1,0,5,4,2,1,2,3,4,5,6,8,2,7]
>>> from itertools import combinations
>>> [var for var in combinations(list_of_n, 2) if sum(var) == 10]
[(5, 5), (4, 6), (2, 8), (2, 8), (3, 7), (4, 6), (8, 2)]
So, if you want a 10 from 3 numbers in the list, you would put combinations(list_of_n, 3) instead of combinations(list_of_n, 2).
When you say
for i in list_n:
i will not refer to the indices, but to the list elements themselves. If you want just the indices,
for i in range(len(list_n)):
len(list_n) will give you the size of the list and range(len(list_n)) will give you a range of numbers starting from 0 and ending with len(list_n) - 1

Python: determine length of sequence of equal items in list

I have a list as follows:
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
I want to determine the length of a sequence of equal items, i.e for the given list I want the output to be:
[(0, 6), (1, 6), (0, 4), (2, 3)]
(or a similar format).
I thought about using a defaultdict but it counts the occurrences of each item and accumulates it for the entire list, since I cannot have more than one key '0'.
Right now, my solution looks like this:
out = []
cnt = 0
last_x = l[0]
for x in l:
if x == last_x:
cnt += 1
else:
out.append((last_x, cnt))
cnt = 1
last_x = x
out.append((last_x, cnt))
print out
I am wondering if there is a more pythonic way of doing this.
You almost surely want to use itertools.groupby:
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
answer = []
for key, iter in itertools.groupby(l):
answer.append((key, len(list(iter))))
# answer is [(0, 6), (1, 6), (0, 4), (2, 3)]
If you want to make it more memory efficient, yet add more complexity, you can add a length function:
def length(l):
if hasattr(l, '__len__'):
return len(l)
else:
i = 0
for _ in l:
i += 1
return i
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
answer = []
for key, iter in itertools.groupby(l):
answer.append((key, length(iter)))
# answer is [(0, 6), (1, 6), (0, 4), (2, 3)]
Note though that I have not benchmarked the length() function, and it's quite possible it will slow you down.
Mike's answer is good, but the itertools._grouper returned by groupby will never have a __len__ method so there is no point testing for it
I use sum(1 for _ in i) to get the length of the itertools._grouper
>>> import itertools as it
>>> L = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
>>> [(k, sum(1 for _ in i)) for k, i in it.groupby(L)]
[(0, 6), (1, 6), (0, 4), (2, 3)]

Categories

Resources