Related
I'm working with lists that look as follows:
[2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
In the end I want to extract only the first and last integer in a consecutive series, as such:
[(2,8),(13,20),(30,35)]
I am new to working with Python, below my code for trying to solve this problem
helix = []
single_prot_helices = []
for ind,pos in enumerate(prot[:-1]):
if pos == prot[ind+1]-1: #if 2 == 3-1 essentially
helix.append(pos)
elif pos < prot[ind+1]-1: #if 8 < 13-1 for example
helix.append(pos)
single_prot_helices.append(helix) #save in a permanent list, clear temp list
helix.clear()
In this case prot is a list just like the example above. I expected single_prot_helices to look something like this:
[[2,3,4,5,6,7,8],[13,14,15,16,17,18,19,20],[30,31,32,33,34,35]]
and at this point it would have been easy to get the first and last integer from these lists and put them in a tuple, but instead of the expected list I got:
[[20,30,31,32,33,34,35],[20,30,31,32,33,34,35]]
Only the last series of numbers was returned and I got 1 less lists than expected (expected 3, received 2). I don't understand where I made a mistake since I believe my code follows my logic: look at the number (pos), look at the next number, if the next number is larger by 1 then add the number (pos) to a list (helix); if the next number is larger by more than 1 then add the smaller number (pos) to the list (helix), append the list to a permanent list (single_prot_helices) and then clear the list (helix) to prepare it for the next series of numbers to be appended.
Any help will be highly appreciated.
You could do something like this:
foo = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
series = []
result = []
for i in foo:
# if the series is empty or the element is consecutive
if (not series) or (series[-1] == i - 1):
series.append(i)
else:
# append a tuple of the first and last item of the series
result.append((series[0], series[-1]))
series = [i]
# needed in case foo is empty
if series:
result.append((series[0], series[-1]))
print(result) # [(2, 8), (13, 20), (30, 35)]
Or, as a generator:
def generate_series(list_of_int):
series = []
for i in list_of_int:
if not series or series[-1] == i - 1:
series.append(i)
else:
yield (series[0], series[-1])
series = [i]
if series:
yield (series[0], series[-1])
foo = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
print([item for item in generate_series(foo)]) # [(2, 8), (13, 20), (30, 35)]
Yours has a few problems.
The main one is that helix is a mutable list and you only ever clear it. This is causing you to append the same list multiple times which is why they're all identical.
The first fix is to assign a new list to helix rather than clearing.
prot = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
helix = []
single_prot_helices = []
for ind,pos in enumerate(prot[:-1]):
if pos == prot[ind+1]-1: #if 2 == 3-1 essentially
helix.append(pos)
elif pos < prot[ind+1]-1: #if 8 < 13-1 for example
helix.append(pos)
single_prot_helices.append(helix) #save in a permanent list, clear temp list
helix = []
print(single_prot_helices) # [[2, 3, 4, 5, 6, 7, 8], [13, 14, 15, 16, 17, 18, 19, 20]]
As you can see the last list is missed. That is because the last helix is never appended.
You could add:
if helix:
single_prot_helices.append(helix)
But that still only gives you:
[[2, 3, 4, 5, 6, 7, 8], [13, 14, 15, 16, 17, 18, 19, 20], [30, 31, 32, 33, 34]]
leaving out the last element since you only ever iterate to the second from last one.
Which means you would need to do something complicated and confusing like this outside of your loop:
if helix:
if helix[-1] == prot[-1] - 1:
helix.append(prot[-1])
single_prot_helices.append(helix)
else:
single_prot_helices.append(helix)
single_prot_helices.append(prot[-1])
else:
single_prot_helices.append(prot[-1])
Giving you:
[[2, 3, 4, 5, 6, 7, 8], [13, 14, 15, 16, 17, 18, 19, 20], [30, 31, 32, 33, 34, 35]]
If you're still confused by names and mutability Ned Batchelder does a wonderful job of explaining the concepts with visual aids.
You can try this one that uses zip().
res = []
l, u = prot[0], -1
for x, y in zip(prot, prot[1:]):
if y-x > 1:
res.append((l, x))
u, l = x, y
res.append((l, prot[-1])) # [(2, 8), (13, 20), (30, 35)]
The solution of #Axe319 works but it doesn't explain what is happening with your code.
The best way to copy data from a list is by using copy(), otherwise you will copy its pointer.
When you add helix to single_prot_helices you will add a pointer of that list so you will have:
# helix = [2, 3, 4, 5, 6, 7, 8]
# single_prot_helices = [[2, 3, 4, 5, 6, 7, 8]]
And when you do helix.clear() you will have:
# helix = []
# single_prot_helices = [[]]
Why ? Because in single_prot_helices you added the pointer and not the elements of that list.
After the second iteration you will have
# helix = [13, 14, 15, 16, 17, 18, 19, 20]
# single_prot_helices = [[13, 14, 15, 16, 17, 18, 19, 20], [13, 14, 15, 16, 17, 18, 19, 20]]
Why two lists in single_prot_helices ? Because you added a second pointer to the list, and first was still there.
Add some prints to your code to understand it well. Note that I added 40, 41 in the list so that you can have better visualisation:
prot = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35,40, 41]
helix = []
single_prot_helices = []
for ind,pos in enumerate(prot[:-1]):
if pos == prot[ind+1]-1: #if 2 == 3-1 essentially
helix.append(pos)
elif pos < prot[ind+1]-1: #if 8 < 13-1 for example
helix.append(pos)
print("helix")
print(helix)
single_prot_helices.append(helix) #save in a permanent list, clear temp list
print(single_prot_helices)
helix.clear()
print("clear")
print(helix)
print(single_prot_helices)
In a single line
data = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
mask = iter(data[:1] + sum(([i1, i2] for i1, i2 in zip(data, data[1:] + data[:1]) if i2 != i1+1), []))
print (list(zip(mask, mask)))
Gives #
[(2, 8), (13, 20), (30, 35)]
Functional approch
Using groupby and count from itertools considering taking diffrence between index and item if they are same group accordingly. Count them uisng ittertools counter - counter
data = [2,3,4,5,6,7,8,13,14,15,16,17,18,19,20,30,31,32,33,34,35]
from itertools import groupby, count
final = []
c = count()
for key, group in groupby(data, key = lambda x: x-next(c)):
block = list(group)
final.append((block[0], block[-1]))
print(final)
Also gives #
[(2, 8), (13, 20), (30, 35)]
How do i covert this into a Python code. I dont get how to do the compare between players and the winNum. The players is a 2D array and winNum is a 1D array.
below is a sample data set that i have been using
in the function what im trying to achive is that for every player in players i want to compare all their numbers with the winNum numbers and find if they match and if they do match i want to make 1 increment to the count. Both arrays have 8 elements and they are sorted from 0-6 and 6-8.
winNum = [0, 5, 20, 22, 23, 25, 0, 26]
player = [[0, 5, 20, 22, 23, 25, 0, 26],[14, 15, 21, 25, 26, 29, 30, 30],[3, 6, 8, 16, 25, 30, 0, 13]]
for i in range (len(player)):
for j in range(6):
x = player[i][j]
and compare with winNum[](0-6)
if x is in winNum:
count +=c1
and move on to the next number
else
ignore and move on to the next number
You could try the following code:
count=0
for i in range(len(player)):
for j in range(8):
x = player[i][j]
if x in winNum:
count+=1
You could also store the 'count' further by appending it to a new empty list. I wasn't sure if you wanted that too, but doing that is also straight forward.
count_list = []
for i in range(len(player)):
count=0
for j in range(8):
x = player[i][j]
if x in winNum:
count+=1
count_list.append(count)
Hi I need help to create a program that receives a list and returns a tuple with the following guidelines:
The first value of the tuple is the same of the first in the list.
The second element has to be greater than the first one but with
the shortest distance between them.
Has to be a smaller value
than the second one, but with the maximum distance between them.
For example:
list=[12, 6, 30, 25, 40, 3, 7]
result:
tuple= (12, 25, 3, 6, 7, 30, 40)
There is a function tuple() for the desired outcome
my_list = [1,2]
print(tuple(my_list))
Output: (1, 2)
I have added an answer for the list to tuple conversion. For the algorithm, you can generate the new list and convert it to a tuple with this function.
I think I reached what you want.
The Code I created:
def target(inputs):
output = []
output.append(inputs[0])
inputs = inputs[1:]
# The Second Value
second = [x for x in inputs if x > output[0]]
new = []
for num in second:
new.append(num - output[0])
output.append(second[new.index(min(new))])
inputs.remove(second[new.index(min(new))])
for i in sorted(inputs):
output.append(i)
return tuple(output)
I tested it and it is clear of bugs
I ran it with your list.
inputs=[12, 6, 30, 25, 40, 3, 7]
print(target(inputs))
and this is the output
(12, 25, 3, 6, 7, 30, 40)
I also tested it with this list
inputs=[19, 21, 25, 100, 8, 13]
and the output is
(19, 21, 8, 13, 25, 100)
I have a list of lists. The lists within these list look like the following:
[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23],
[9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]
Every small list has 8 values from 0-23 and there are no value repeats within a small list.
What I need now are the three lists which have the values 0-23 stored. It is possible that there are a couple of combinations to accomplish it but I do only need one.
In this particular case the output would be:
[0,2,5,8,7,12,16,18], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15]
I thought to do something with the order but I'm not a python pro so it is hard for me to handle all the lists within the list (to compare all).
Thanks for your help.
The following appears to work:
from itertools import combinations, chain
lol = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
for p in combinations(lol, 3):
if len(set((list(chain.from_iterable(p))))) == 24:
print(p)
break # if only one is required
This displays the following:
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
If it will always happen that 3 list will form numbers from 0-23, and you only want first list, then this can be done by creating combinations of length 3, and then set intersection:
>>> li = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
>>> import itertools
>>> for t in itertools.combinations(li, 3):
... if not set(t[0]) & set(t[1]) and not set(t[0]) & set(t[2]) and not set(t[1]) & set(t[2]):
... print t
... break
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
Let's do a recursive solution.
We need a list of lists that contain these values:
target_set = set(range(24))
This is a function that recursively tries to find a list of lists that match exactly that set:
def find_covering_lists(target_set, list_of_lists):
if not target_set:
# Done
return []
if not list_of_lists:
# Failed
raise ValueError()
# Two cases -- either the first element works, or it doesn't
try:
first_as_set = set(list_of_lists[0])
if first_as_set <= target_set:
# If it's a subset, call this recursively for the rest
return [list_of_lists[0]] + find_covering_lists(
target_set - first_as_set, list_of_lists[1:])
except ValueError:
pass # The recursive call failed to find a solution
# If we get here, the first element failed.
return find_covering_lists(target_set, list_of_lists[1:])
I have an array A
A = [5,2,8,14,6,13]
I want to get an array where each element is added to every other element, so the first five elements would be 5 + each element, then the next four would be 2 + each element etc.
So the result would be
B = [7,13,19,11,18, 10,16,8,15, 22,14,21, 20,27, 19]
What is the quickest way to do this without using for loops?
Note: The problem I am trying to solve involves large boolean arrays instead of integers and the actual operation is a boolean 'and', not merely addition. I have simplified the question for ease of explanation. I have been using for loops up to now, but I am looking for a faster alternative.
Use ` itertools.combinations
from itertools import combinations
a = [5,2,8,14,6,13]
print [sum(i) for i in list(combinations(a, 2))]
No need of list(). Thanks to #PeterWood
print [sum(i) for i in combinations(a, 2)]
Output:
[7, 13, 19, 11, 18, 10, 16, 8, 15, 22, 14, 21, 20, 27, 19]
Demo
You could do it recursively:
def add_value_to_rest(sequence):
if not sequence:
return []
else:
additional = sequence[0]
return ([additional + value for value in sequence] +
add_value_to_rest(sequence[1:]))
With generators, in Python 3:
def add_value_to_rest(sequence):
if sequence:
additional = sequence[0]
for value in sequence:
yield additional + value
yield from add_value_to_rest(sequence[1:])
Or with Python 2.7:
def add_value_to_rest(sequence):
if sequence:
additional = sequence[0]
for value in sequence:
yield additional + value
for value in add_value_to_rest(sequence[1:]):
yield value
A = [5,2,8,14,6,13]
B = []
for i, x in enumerate(A):
for l in range(len(A) - i - 1):
B.append(A[i] + A[i + l + 1])
print B
#[7, 13, 19, 11, 18, 10, 16, 8, 15, 22, 14, 21, 20, 27, 19]