Calculate the difference between 2 strings (Levenshtein distance) - python

I am trying to calculate the distance between two strings. The distance/difference between two strings refers to the minimum number of character insertions, deletions, and substitutions required to change one string to the other.
The method I have tried is to: convert two strings into lists, compare lists, check the differences, then add the differences
first_string = "kitten"
second_string = "sitting"
list_1 = list(first_string)
list_2 = list(second_string)
print("list_1 = ", list_1)
print("list_2 = ", list_2)
print(" ")
lengths = len(list_2) - len(list_1)
new_list = set(list_1) - set(list_2)
print(lengths)
print(new_list)
difference = lengths + int(new_list)
print(difference)
the output I get is:
list_1 = ['k', 'i', 't', 't', 'e', 'n']
list_2 = ['s', 'i', 't', 't', 'i', 'n', 'g']
1
{'e', 'k'}
Of which then I am trying to find out how to add these differences so it equals 3. I don't know how to make the outputs similar to add them together (adding 1 with {'e', 'k'} to equal a distance of 3).

You're almost there. Calculate the length of new_list using len() like you did with lengths:
difference = lengths + len(new_list)

Looks like you just need to change this line:
difference = lengths + int(len(new_list))
That should give you 3 like you want :)

This is referred to as the Levenshtein distance. Check out this implementation as further reading.

Related

How to rotate a list(not 2D) 90 degree clockwise?

As a beginner in Python, I think the biggest problem I have is overcomplicating a problem when it can be done a lot simpler. I have not found a solution for a list that is not two-dimensional, hence why I chose to ask.
Here is an example of what I am trying to do:
# Before
alphabet = ["ABCDEFG",
"HIJKLMN",
"OPQRSTU"]
# After
rotated_alphabet = ["OHA",
"PIB",
"QJC",
"RKD",
"SLE",
"TMF",
"UNG"]
What I have done so far:
length_of_column = len(alphabet)
length_of_row = len(alphabet[0])
temp_list = []
x = -1
for i in range(length_of_column):
while x < length_of_row-1:
x += 1
for row in alphabet:
temp_list.append(row[x])
temp_list = temp_list[::-1]
Output
print(temp_list)
>>> ['U', 'N', 'G', 'T', 'M', 'F', 'S','L','E','R','K','D','Q','J','C','P','I','B', 'O', 'H', 'A']
I need to make the list above in the desired format.
-How would I do this?
-Is there a simpler way to do it?
You can just zip the list of strings, and it will make tuples character by character, then you'll only have to join the tuples in reverse order. Here it is in just one line:
rotated_alphabet = [''.join(list(i)[::-1]) for i in zip(*alphabet)]
A variant of #MuhammadAhmad answer will be to use reversed, as reversed works with iterables, no need to convert to a list.
alphabet = ["ABCDEFG",
"HIJKLMN",
"OPQRSTU"]
rotated = [''.join(reversed(a)) for a in zip(*alphabet)]
print(rotated)
Output
['OHA', 'PIB', 'QJC', 'RKD', 'SLE', 'TMF', 'UNG']

How to get certain number of alphabets from a list?

I have a 26-digit list. I want to print out a list of alphabets according to the numbers. For example, I have a list(consisting of 26-numbers from input):
[0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
I did like the output to be like this:
[e,e,l,s]
'e' is on the output 2-times because on the 4-th index it is the 'e' according to the English alphabet formation and the digit on the 4-th index is 2. It's the same for 'l' since it is on the 11-th index and it's digit is 1. The same is for s. The other letters doesn't appear because it's digits are zero.
For example, I give another 26-digit input. Like this:
[1,2,2,3,4,0,3,4,4,1,3,1,4,4,1,0,0,0,0,0,4,2,3,2,2,1]
The output should be:
[a,b,b,c,c,d,d,d,e,e,e,e,g,g,g,h,h,h,h,i,i,i,i,j,k,k,k,l,m,m,m,m,n,n,n,n,o,u,u,u,u,v,v,w,w,w,x,x,y,y,z]
Is, there any possible to do this in Python 3?
You can use chr(97 + item_index) to get the respective items and then multiply by the item itself:
In [40]: [j * chr(97 + i) for i, j in enumerate(lst) if j]
Out[40]: ['ee', 'l', 's']
If you want them separate you can utilize itertools module:
In [44]: from itertools import repeat, chain
In [45]: list(chain.from_iterable(repeat(chr(97 + i), j) for i, j in enumerate(lst) if j))
Out[45]: ['e', 'e', 'l', 's']
Yes, it is definitely possible in Python 3.
Firstly, define an example list (as you did) of numbers and an empty list to store the alphabetical results.
The actual logic to link with the index is using chr(97 + index), ord("a") = 97 therefore, the reverse is chr(97) = a. First index is 0 so 97 remains as it is and as it iterates the count increases and your alphabets too.
Next, a nested for-loop to iterate over the list of numbers and then another for-loop to append the same alphabet multiple times according to the number list.
We could do this -> result.append(chr(97 + i) * my_list[i]) in the first loop itself but it wouldn't yield every alphabet separately [a,b,b,c,c,d,d,d...] rather it would look like [a,bb,cc,ddd...].
my_list = [1,2,2,3,4,0,3,4,4,1,3,1,4,4,1,0,0,0,0,0,4,2,3,2,2,1]
result = []
for i in range(len(my_list)):
if my_list[i] > 0:
for j in range(my_list[i]):
result.append(chr(97 + i))
else:
pass
print(result)
An alternative to the wonderful answer by #Kasramvd
import string
n = [0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
res = [i * c for i, c in zip(n, string.ascii_lowercase) if i]
print(res) # -> ['ee', 'l', 's']
Your second example produces:
['a', 'bb', 'cc', 'ddd', 'eeee', 'ggg', 'hhhh', 'iiii', 'j', 'kkk', 'l', 'mmmm', 'nnnn', 'o', 'uuuu', 'vv', 'www', 'xx', 'yy', 'z']
Splitting the strings ('bb' to 'b', 'b') can be done with the standard schema:
[x for y in something for x in y]
Using a slightly different approach, which gives the characters individually as in your example:
import string
a = [0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
alphabet_lookup = np.repeat(np.arange(len(a)), a)
letter_lookup = np.array(list(string.ascii_lowercase))
res = letter_lookup[alphabet_lookup]
print(res)
To get
['e' 'e' 'l' 's']

How to wrap a string or an array around and slice the wrapped string or array in Python?

Before anything: I did read Wrapping around a python list as a slice operation and wrapping around slices in Python / numpy
This question is not a duplicate of any of those two questions simply because this question is a totally different question. So stop downvoting it and do not mark it as a duplicate. In the first mentioned thread, the "wrap" there means something different. For the second mentioned thread, they dealt with ndarray and can only work for integers only.
Real question:
How to slice a string or an array from a point to another point with an end between them?
Essentially, we want to do something like this,
n = whatever we want
print(string[n-5:n+6])
The above code may look normal. But it doesn't work near the edges (near the beginning of the string/array or the end of the string/array). Because Python's slicing doesn't allow slicing through the end of the array and continuing from the beginning. What if n is smaller than 5 or length of string longer than n+6?
Here's a better example, consider that we have
array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
We want to print an element with its nearest two neighbors in string for all elements in an array
print("Two neighbors:")
for i, x in enumerate(array):
print(array[i-1] + array[i] + array[(i+1)%len(array)])
Output:
Two neighbors:
kab
abc
bcd
cde
def
efg
fgh
ghi
hij
ijk
jka
So far so good, let's do it with four neighbors.
print("Four neighbors:")
for i, x in enumerate(array):
print(array[i-2] + array[i-1] + array[i] + array[(i+1)%len(array)] + array[(i+2)%len(array)])
Output:
Four neighbors:
jkabc
kabcd
abcde
bcdef
cdefg
defgh
efghi
fghij
ghijk
hijka
ijkab
You can see where this is going, as the desired number of neighbors grow, the number of times we must type them out one by one increases.
Is there a way instead of s[n-3]+s[n-2]+s[n-1]+s[n]+s[n+1]+s[n+2]+s[n+3], we can do something like s[n-3:n+4]?
Note that s[n-3:n]+s[n:(n+4)%len(s)] doesn't work at the edges.
NOTE:
For the particular example above, it is possible to do a 3*array or add a number of elements to the front and to the back to essentially "pad" it.
However, this type of answer cost a bit of memory AND cannot work when we want to wrap it many folds around.
Consider the following,
# len(string) = 10
# n = 0 or any number we want
print(string[n-499:n+999])
If the start and end indices can be flexible instead of mirroring each other(eg. string[n-2:n+9] instead of string[n-3:n+4]), it is even better.
A solution which doesn't use an excessive amount of memory is as follows
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def get_sequences(a_list, sequence_length):
sequences = []
for i in range(len(my_list)):
sequences.append("".join(str(my_list[(x + i) % len(my_list)]) for x in range(sequence_length)))
return sequences
print(get_sequences(my_list, 2))
print(get_sequences(my_list, 3))
will output
['12', '23', '34', '45', '56', '67', '78', '89', '91']
['123', '234', '345', '456', '567', '678', '789', '891', '912']
This is nice because it utilizes a generator everywhere that it can.
This could give ideas. The only thing to check is the order in your interval. Works with any n.
array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
def print_neighbors(n_neighbors):
for idx in range(len(array)):
start = (idx- n_neighbors//2) % len(array)
end = (idx+n_neighbors//2) % len(array) + 1
if start > end:
print(''.join(array[start:] + array[:end]))
else:
print(''.join(array[start:end]))
>>> print_neighbors(6)
ijkabcd
jkabcde
kabcdef
abcdefg
bcdefgh
cdefghi
defghij
efghijk
fghijka
ghijkab
hijkabc
You could create a class to wrap your original iterable like this:
class WrappingIterable():
def __init__(self, orig):
self.orig=orig
def __getitem__(self, index):
return self.orig[index%len(self.orig)]
def __len__(self):
return len(self.orig)
>>> w = WrappingIterable("qwerty")
>>> for i in range(-2, 8):
... print(w[i])
t
y
q
w
e
r
t
y
q
w
For this particular issue you can use a snippet like this:
def print_neighbors(l, n):
wrapped = l[-(n//2):] + l + l[:(n//2)]
for i in range(len(l)):
print(''.join(wrapped[i:i+n+1]))
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
print_neighbors(l, 2)
print_neighbors(l, 4)
Hope it makes sense!

Count letter differences of two strings Python

I'm having some problems with an exercise about strings in python.
I have 2 different lists:
list1= "ABCDEFABCDEF"
and
list2= "AZBYCXDWEVFABCDEF"
I need to compare those 2 lists according to their position so the 1 letter together, then the 2...using the min length (so here length of list1) and store the letters in a new variable according to if they are different or the same.
identicals=[]
different=[]
I tried to code something and it seems to find the same ones, but doesn't work on the different ones since it copies them multiple times.
for x in list1:
for y in list2:
if list1>list2:
if x==y:
identicals.append(x)
if x!=y :
different.append(x)
if list2>list1:
if y==x:
identicals.append(y)
if y!=x:
different.append(y)
EDIT: Output result should be something like this:
identicals=['A']
different=["Z","B","Y","C","X","D","W","E","V",F","A"]
The thing is that the letter A is only shown on identicals but not in different even if F!=A.
You are getting unwanted duplicates because you have a nested pair of for loops, so each item in list2 get tested for every item in list1.
The key idea is to iterate over the two strings in parallel. You can do that with the built-in zip function, which yields a tuple of the corresponding items from each iterable you feed it, stopping as soon as one of the iterables runs out of items.
From your example code, it looks like you want to take the items for the different list from the longer string. To do that efficiently, figure out which string is the longer before you start looping.
I've renamed your strings because it's confusing to give strings a name starting with "list".
s1 = "ABCDEFABCDEF"
s2 = "AZBYCXDWEVFABCDEF"
identicals = []
different = []
small, large = (s1, s2) if len(s1) <= len(s2) else (s2, s1)
for x, y in zip(small, large):
if x == y:
identicals.append(y)
else:
different.append(y)
print(identicals)
print(different)
output
['A']
['Z', 'B', 'Y', 'C', 'X', 'D', 'W', 'E', 'V', 'F', 'A']
We can make the for loop more compact at the expense of readability. We put our destination lists into a tuple and then use the equality test to select which list in that tuple to append to. This works because False has a numeric value of 0, and True has a numeric value of 1.
for x, y in zip(small, large):
(different, identicals)[x == y].append(y)
The problem is the inner loop. You are comparing each of the letters in list1 with all the letters of list2.
Instead you should have a single loop:
identicals=[]
different=[]
short_list = list1 if len(list1)<= len(list2) else list2
for i in range(len(short_list):
if list1[i] == list2[i]:
identicals.append(list1[i])
else:
different.append(short_list[i])
Try this
a = "ABCDEFABCDEF"
b = "AZBYCXDWEVFABCDEF"
import numpy
A = numpy.array(list(a))
B = numpy.array(list(b))
common = A[:len(B)] [ (A[:len(B)] == B[:len(A)]) ]
different = A[:len(B)] [ - (A[:len(B)] == B[:len(A)]) ]
>>> list(common)
['A']
>>> list(different)
['B', 'C', 'D', 'E', 'F', 'A', 'B', 'C', 'D', 'E', 'F']

Compare lists to find common elements in python [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python - Intersection of two lists
i'm trying to compare two lists in order to find the number of elements they have in common.
The main problem I'm having is when either list contains repeated elements, for example
A = [1,1,1,1] and
B = [1,1,2,3]
using the code
n = 0
for x in A:
if x in B:
n += 1
print n
gives me the output that n = 4, as technically all elements of A are in B
I'd like to get the output that n = 2, preferably without using sets, Is there anyway I can adapt my code, or a new way of thinking about the problem to achieve this?
Thanks
It's not entirely clear what your specification is, but if you want the number of elements in A that appear in B, without regard to order, but with regard to multiplicity, use collections.Counter:
>>> from collections import Counter
>>> A = [1,1,1,1]
>>> B = [1,1,2,3]
>>> C = Counter(A) & Counter(B)
>>> sum(C.itervalues())
2
>>> list(C.elements())
[1, 1]
Here is an efficient (O(n logn)) way to do it without using sets:
def count_common(a, b):
ret = 0
a = sorted(a)
b = sorted(b)
i = j = 0
while i < len(a) and j < len(b):
c = cmp(a[i], b[j])
if c == 0:
ret += 1
if c <= 0:
i += 1
if c >= 0:
j += 1
return ret
print count_common([1,1,1,1], [1,1,2,3])
If your lists are always sorted, as they are in your example, you can drop the two sorted() calls. This would give an O(n) algorithm.
Here's an entirely different way of thinking about the problem.
Imagine I've got two words, "hello" and "world". To find the common elements, I could iterate through "hello", giving me ['h', 'e', 'l', 'l', 'o']. For each element in the list, I'm going to remove it from the second list(word).
Is 'h' in ['w', 'o', 'r', 'l', 'd']? No.
Is 'e' in ['w', 'o', 'r', 'l', 'd']? No.
Is 'l' in ['w', 'o', 'r', 'l', 'd']? Yes!
Remove it from "world", giving me ['w', 'o', 'r', 'd'].
is 'l' in ['w', 'o', 'r', 'd']? No.
Is 'o' in ['w', 'o', 'r', 'd']?
Yes! Remove it ['w', 'o', 'r', 'd'], giving me ['w', 'r', 'd']
Compare the length of the original object (make sure you've kept a copy around) to the newly generated object and you will see a difference of 2, indicating 2 common letters.
So you want the program to check whether only elements at the same indices in the two lists are equal? That would be pretty simple: Just iterate over the length of the two arrays (which I presume, are supposed to be of the same length), say using a variable i, and compare each by the A.index(i) and B.index(i) functions.
If you'd like, I could post the code.
If this is not what you want to do, please do make your problem clearer.

Categories

Resources