Return index with enumerate - python

I am basically trying to return the starting index of a DNA string where A and G form the majority over the next five letters.
See code
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
I always get a string index out of range error. The correct answer for the dna at the end is [0,1,4,5,8,9]

Use the count method to count the letters of interest. Start the run of five up until you don't have enough positions left:
def a_g_majority_positions(dna):
lst = []
for start5 in range(len(dna)-4):
five = dna[start5:start5+5]
if five.count('A') + five.count('G') >= 3:
lst.append(start5)
return lst
Or, for the one-statement version, checking each character for being in "AG":
lst = [start5 for start5 in range(len(dna)-4)
if sum(char in "AG"
for char in dna[start5:start5+5]) >= 3]
Output in either case is
[0, 1, 4, 5, 8, 9]

You need to break out of the for loop when there are less than 5 letters left:
def a_g_majority_positions(dna):
result = list()
for index, item in enumerate(dna):
if index + 5 >= len(dna):
break
count = 0
x = 0
while count < 5:
letter = dna[index + count]
count += 1
if letter == 'A':
x += 1
elif letter == 'G':
x += 1
if x >= 3:
result.append(index)
return result
print(a_g_majority_positions("AACCGGTTAACCGGTT"))
Output
[0, 1, 4, 5, 8, 9]
Note
Don't use list as a variable name. It's built-in class and you will introduce hard to find errors by using it as a variable name.

You need to break early from the function when index is greater than len(dna) - 5. Otherwise you try to access dna[len(dna)], which is out of range.
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
if index > len(dna) - 5:
break
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
# Result [0, 1, 4, 5, 8, 9]

Related

Selection sort code is not outputting the right values

I am trying my hand at algorithms for the first time and tried to create a sequential sorting algorithm. I have come up with the code below.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
prev_num = i
counter += 1
new_list.append(i)
else:
prev_num = i
for n in new_list:
if i >= n:
new_list_counter += 1
pass
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
return my_list
print(SelectionSort([1, 3, 4, 2, 6, 5]))
print(SelectionSort([3, 4, 2, 6]))
print(SelectionSort([3, 7, 4, 1]))
print(SelectionSort([3, 10, 2, 5]))
Whilst it seems to sort the first 3 lists just fine, when it comes to the last list it outputs [2, 3, 10, 5].
Can anyone possible lend a hand?
I am sure the logic of your code could be improved and simplified. But with a small change it's working, at least with your 4 test cases.
My solution is to save "prev_num" with the higest existing number in the list using max(). And only save this value at the end of each loop.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
counter += 1
new_list.append(i)
else:
for n in new_list:
if i >= n:
new_list_counter += 1
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
prev_num = max(new_list) # <-- This is the key: Save at the end of each loop the highest existing number
return my_list
Improvements:
The renaming of the "prev_num" variable to something like "highest_num".
"elif" could be used instead of "else - if"
Comment the code, not only for the reviewrs, it's useful for yourself too.

Python remove N consecutive duplicates from the list

Here's the problem. The input is a list of integers. If more than three adjacent numbers appear next to each other they should be dropped and the operation goes again. Kind of similar to the Iphone game, where player needs to pop lines of three or more balls of the same colors. The output should be the count of the balls that will be removed.
The algorithm is as follows. Starting with a sample list of say [3,3,4,4,4,4,3,2].
First iteration should remove the 4,4,4,4 - so the list would become [3,3,3,2], and the intermediary output of removed numbers will be 4.
Second iteration should remove 3,3,3 - so the final list would be [2] and final count of removed numbers - 7.
The first implementation for three consecutive items came from another stackoverflow thread - Remove triplets of adjacent numbers from the list
Here's the working function implementation for exactly 3 consecutive numbers:
def balls(l):
values = l
while len(values) >= 3:
print(values) #for demonstrative purposes of this question
for x in range(0,len(values)-2):
if values[x] == values[x+1] and values[x] == values[x+2]:
values = values[:x] + values[x+3:]
break
else:
break
print(values) #for demonstrative purposes of this question
return len(l) - len(values)
balls([3, 3, 4, 4, 4, 3, 4])
Output:
[3, 3, 4, 4, 4, 3, 4]
[3, 3, 3, 4]
[4]
6
How could I update the implementation to include the more general solution of removing 3+ consecutive numbers. I am thinking about tracking the start and end index of the consecutive duplicates, then subsetting the list. However, not sure how to implement that. Here are the tests that should work.
if __name__ == "__main__":
test1 = [3, 3, 4, 4, 4, 3, 4]
print(balls(test1))
#Output should be 6
test2 = [5, 5, 5, 5, 5, 5, 5, 5]
print(balls(test2))
#Output should be 8
test3 = [5, 7, 8, 3]
print(balls(test3))
#Output should be 0
def remove_consecutive(l, length):
amount = len(l)
count = 1
start = 0
current = l[0]
i = 1
while i < len(l):
if l[i] == current:
count += 1
else:
if count >= length:
for i in range(count):
l.pop(start)
start = 0
i = 0
current = l[0]
else:
start = i
current = l[i]
count = 1
i+=1
if count >= length:
for i in range(count):
l.pop(start)
return amount - len(l)
Wuff, i got it. My brain is kinda stinky lately so it took so long.
Here is my code, it works well. But I think there may be better ways to achieve higher efficiency.
def remove_consecutive(lst):
len_init = len(lst)
contain_tuplets = True
while contain_tuplets:
for i in range(len(lst)-2):
indices_to_pop = []
if lst[i]==lst[i+1]==lst[i+2]:
indices_to_pop.extend([i, i+1, i+2])
for j in range(i+3,len(lst)):
if lst[j] == lst[i]:
indices_to_pop.append(j)
else:
break
[lst.pop(i) for _ in indices_to_pop]
contain_tuplets = True
break
else:
contain_tuplets = False
count_removed_numbers = len_init - len(lst)
return count_removed_numbers, lst
test case1:
lst = [3,3,4,4,4,4,3,2]
remove_consecutive(lst)
output
(7, [2])
test case 2:
lst = [2, 2, 1, 1, 1, 2, 1]
remove_consecutive(lst)
output:
(6, [1])
def remove_consecutive(l, length):
amount = 0
count = 1
current = l[0]
for i in range(1, len(l)):
if l[i] == current:
count += 1
if count > length:
amount += 1
elif count == length:
amount += length
else:
current = l[i]
count = 1
return amount

How can I eliminate one of the arguments in this function?

I want to eliminate the third argument in my code below which is just an empty array that I think I should be able to create as a local variable within the function itself.
As a bonus, I would also like to build this into a single function although I don't think this can be achieved with the current recursive structure of the code.
I tried creating an empty array as a local variable (See the commented out integers list)
I also tried to create a count variable to increment with each combination found (See the commented out count variable)
def count_combinations(number, integers_available, integers):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
def count_total(number, integers_available, integers):
return len(count_combinations(number, integers_available, integers))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available, []))
I expect to get the same results but with fewer arguments in the function since one of the arguments will have been switched to a local variable instead.
As discussed in the comments, a dynamic programming approach may be more Pythonic here.
from collections import Counter
def ways(total, coins=(1,2,5,10,20,50,100)):
counts = [[Counter()]] + [[] for _ in range(total)]
for coin in coins:
for i in range(coin, total + 1):
counts[i] += [c + Counter({coin: 1}) for c in counts[i-coin]]
return counts[total]
Demo:
>>> ways(15, coins=(1,5,10))
[Counter({1: 15}),
Counter({1: 10, 5: 1}),
Counter({1: 5, 5: 2}),
Counter({5: 3}),
Counter({1: 5, 10: 1}),
Counter({5: 1, 10: 1})]
>>> len(ways(15, coins=(1,5,10)))
6
Without regard to the algorithm or the goal of the code, here's how you can tidy it up a little by making it into a single function and giving integers parameter a default value:
def count_total(number, integers_available):
def count_combinations(number, integers_available, integers=[]):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
return len(count_combinations(number, integers_available))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available))

Generate triangular numbers

This function is supposed to take an integer, generate the list of triangular numbers within range of that integer, check that list for the longest list of numbers whose sum == number and return them in a list, otherwise if there is no such list within that range of triangular numbers, return an empty list. I thought I had it somewhat, and it runs on python tutor.com, but when I run it in IDLE, nothing happens.
def checkio(number):
x = 4
lst = [1, 3, 6]
new = []
if number == 0:
return []
elif number == 1:
return [1]
elif number == 3:
return []
elif number == 6:
return []
elif number == 4:
return [1, 3]
elif number == 7:
return [1, 6]
elif number == 10:
return [1, 3, 6]
elif number > 10:
while number > lst[-1]: # Generates a list of all the triangular numbers up to number
for item in range(lst[-1]):
lst.append(x + lst[-1])
x += 1
go = []
start = 0
end = 0
count = 0
while count < len(lst) * 2:
if sum(lst[start:end+1]) < number:
end += 1
count += 1
elif sum(lst[start:end+1]) > number:
start += 1
count += 1
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
if count >= len(lst) * 2:
return []
In the code you post you are just declaring a function. In order to run it, you have to make a call to that function. In your case, it receives one argument, so you have to pass it inside the parentheses ():
number = 5 # for example
checkio(number) # this is the function call
As Bakuriu commented: If you want to get a result change the order of this lines:
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
To :
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
return go
break
This will return a value before escaping the while loop. As noted in the comments (thanks Andrea Corbellini) you can also remove the break statement and it will work well. Because after the return statement by definition escapes the function.
Also to run in idle once defined (you copied the code and pressed return), call it as Christian says.
This way you will check if works.
Note that you don't check in the ifelse clauses for the numbers 2, 5, 8 and 9. If you call this function with checkio(5), like suggested by Crhistian, it will not return anything because it doesn't have anything to return!

How to create a list that results a number from a given list, not the indices?

I am having trouble getting the results to produce the integer in the list, not which index it falls under.
#this function takes, as input, a list of numbers and an option, which is either 0 or 1.
#if the option is 0, it returns a list of all the numbers greater than 5 in the list
#if the option is 1, it returns a list of all the odd numbers in the list
def splitList(myList, option):
#empty list for both possible options
oddList = []
greaterList = []
#if option is 0, use this for loop
if int(option) == 0:
#create for loop the length of myList
for i in range(0, len(myList)):
#check if index is greater than 5
if ((myList[i])>5):
#if the number is greater than 5, add to greaterList
greaterList.append(i)
#return results
return greaterList
#if option is 1, use this for loop
if int(option) == 1:
#create for loop the length of myList
for i in range(0, len(myList)):
#check if index is odd by checking if it is divisible by 2
if ((myList[i])%2!=0):
#if index is not divisible by 2, add the oddList
oddList.append(i)
#return results
return oddList
the results I receive are as follows:
>>>splitList([1,2,6,4,5,8,43,5,7,2], 1)
[0, 4, 6, 7, 8]
I am trying to get the results to be [1, 5, 43, 5, 7]
You're iterating over the range of the index. Iterate over the list instead.
for i in myList:
#check if index is greater than 5
if i >5:
#if the number is greater than 5, add to greaterList
greaterList.append(i)
So, your code gets rewritten as (with some minor changes)
def splitList(myList, option):
final_list = []
if int(option) == 0:
for i in myList:
if i > 5:
final_list.append(i)
elif int(option) == 1:
for i in myList:
if i%2 != 0:
final_list.append(i)
return final_list
You could reduce it by doing
def splitList(myList, option):
if int(option) == 0:
return [elem for elem in myList if elem > 5]
elif int(option) == 1:
return [elem for elem in myList if elem % 2 != 0]
Output -
>>> splitList([1,2,6,4,5,8,43,5,7,2], 1)
[1, 5, 43, 5, 7]
List comprehensions greatly simplify your code.
def split_list(xs, option):
if option:
return [x for x in xs if x % 2]
else:
return [x for x in xs if x > 5]
if ((myList[i])>5):
#if the number is greater than 5, add to greaterList
greaterList.append(i)
Instead of adding the index i, add the value (myList[i]):
if ((myList[i])>5):
#if the number is greater than 5, add to greaterList
greaterList.append(myList[i])
Same thing for the oddList case.
Note: #Sukrit Kalra's solution is preferable, but I'm leaving this up to show that there's multiple ways of solving this.
Take a closer look at your .append() commands... in your compare you're using:
if ((mylList[i])%2!=0)
or
if ((myList[i])>5)
...but when you put it into the list, you're just using
greaterList.append(i)
instead of
greaterList.append(myList[i])
This must be a homework or class somewhere?

Categories

Resources