My list looks like this:
G = [ [0,3,4],[1,0,0],[9,5,4],[4,3,2],[3,2,3],[0,1,4],[1,0,0],[1,0,0],[1,0,0],[1,0,0] ]
and I want to find the index, from which only [1, 0, 0] follows consecutively. In this case [1,0,0] occurs 4 times consecutively, i.e if [1,0,0] occurs 4 times, then the output should be 7. The output of my expression is wrong!
The output of
index =[i for i, j in enumerate(G) if j == [1,0,0]]
is
index = [1, 6, 7, 8, 9]
How can i solve this problem?
Edited
Very important was in this question to find the index of the pattern [1,0,0], if after this no another pattern occurs. In list G is the index 6 not 7!
Given the application, it's probably best to iterate through the list backwards using reversed and find the first non-target element.
I'm also going to use enumerate to iterate through the elements and indices of the reversed list at the same time. The first time we see a non-target element, we can break out of the loop. At this point, subtracting the index counter from the length of the list will give us the index of the last non-target element.
for ind, el in enumerate(reversed(G)):
if el != [1, 0, 0]:
break
else: # This block is only run when no breaks are encountered in the for loop
ind = ind + 1 # Adjust for when the entire list is the target
result = len(G) - ind
# 6
Note that Python indexing starts at 0, so 6 is actually the correct answer here, not 7. If you need the 1-indexed version, you can simply add 1.
>>> G = [ [0,3,4],[1,0,0],[9,5,4],[4,3,2],[3,2,3],[0,1,4],
... [1,0,0],[1,0,0],[1,0,0],[1,0,0] ]
>>> next(i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y)
6
(Use .. if x == y == [1, 0, 0] if you want to only check for [1, 0, 0])
This will raise a StopIteration if there's no consecutive items.
>>> G = [ [1,2,3] ]
>>> next(i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
To prevent that, pass default value:
>>> next((i for i, (x,y) in enumerate(zip(G, G[1:])) if x == y), -1)
-1
UPDATE according to the OP's comment:
import itertools
def find_target(lst, target):
target = tuple(target)
i, maxidx, maxcnt = 0, -1, 0
for key, grp in itertools.groupby(lst, key=tuple):
cnt = sum(1 for _ in grp)
if key == target and cnt > maxcnt:
maxidx, maxcnt = i, cnt
i += cnt
return maxidx
Usage:
>>> G = [[0,3,4], [1,0,0], [9,5,4], [1,0,0], [1,0,0],
... [1,0,0], [1,0,0], [23,4,1], [1,0,0], [1,0,0],
... [1,0,0], [1,0,0],[1,0,0], [1,0,0], [1,0,0],
... [1,0,0]]
>>>
>>> find_target(G, [1, 0, 0])
8
>>> find_target([], [1, 0, 0])
-1
>>> find_target([[1, 0, 0]], [1, 0, 0])
0
>>> find_target([[2, 3, 4], [1, 0, 0], [1, 0, 0]], [1, 0, 0])
1
Something like this?
for ii in range(len(G)-1, 0, -1):
if G[ii-1] != [1,0,0]:
break
And the resulting ii is the desired index
If you mean the last element, and not expecifically [1,0,0] obviously you should replace [1,0,0] with G[-1], that would work for:
G = [ [2,3,4], [1,3,4], [1,3,4]]
giving 1 as an answer
Related
I'm new in the "python game", so maybe the following question is for some of you very easy to answer:
I have an array like this:
store=([9,4,5],[9,4,1],[1,2,3],[9,4,1],[3,7,5],[2,4,1])
I want to "loop" the array and creat a new structure:
store_new=([0,1,2],[0,1,3],[3,4,5],[0,1,3],[5,6,2],[4,1,3])
The first value starts with 0, the second is 1 and so on. If there is a duplicate it should take the same new value as before. For example for "9"(old) to "0"(new)....and for the next "9"(old) again to "0" (new). Is there a nice way to handle this problem?
I tried something with “enumerate” in a “for loop”...but this don't really work.
You can use itertools.count and dict.setdefault in a list comprehension:
store=([9,4,5],[9,4,1],[1,2,3],[9,4,1],[3,7,5],[2,4,1])
from itertools import count
d = {}
c = count()
out = tuple([d[x] if x in d else d.setdefault(x, next(c)) for x in l]
for l in store)
Output:
([0, 1, 2], [0, 1, 3], [3, 4, 5], [0, 1, 3], [5, 6, 2], [4, 1, 3])
With a classical python loop:
out = []
d = {}
max_val = 0
for l in store:
out.append([])
for x in l:
if x in d:
out[-1].append(d[x])
else:
d[x] = max_val
out[-1].append(max_val)
max_val += 1
out = tuple(out)
I am given a 1D array of numbers.
I need to go through the array adding each consecutive element to form a sum. Once this sum reaches a certain value, it forms the first element of a new array. The sum is then reset and the process repeats, thus iterating over the whole array.
For example if given:
[1, 3, 4, 5, 2, 5, 3]
and requiring the minimum sum to be 5,
the new array would be:
[8, 5, 7]
Explicity: [1 + 3 + 4, 5, 2 + 5]
I then also need to keep a record of the way the elements were combined for that particular array: I need to be to take a different array of the same length and combine the elements in the same way as above.
e.g. give the array
[1, 2, 1, 1, 3, 2, 1]
I require the output
[4, 1, 5]
Explicity: [1 + 2 + 1, 1, 3 + 2]
I have accomplished this with i loops and increment counters, but it is very ugly. The array named "record" contains the number of old elements summed to make each element of the new array i.e. [3, 1, 2]
import numpy as np
def bin(array, min_sum):
num_points = len(array)
# Create empty output.
output = list()
record = list()
i = 0
while i < num_points:
sum = 0
j = 0
while sum < min_sum:
# Break out if it reaches end of data whilst in loop.
if i+j == num_points:
break
sum += array[i+j]
j += 1
output.append(sum)
record.append(j)
i += j
# The final data point does not reach the min sum.
del output[-1]
return output
if __name__ == "__main__":
array = [1, 3, 4, 5, 2, 5, 3]
print bin(array, 5)
I would advice you to simply walk through the list. Add it to an accumulator like the_sum (do not use sum, since it is a builtin), and in case the_sum reaches a number higher than the min_sum, you add it, and reset the_sum to zero. Like:
def bin(array, min_sum):
result = []
the_sum = 0
for elem in array:
the_sum += elem
if the_sum >= min_sum:
result.append(the_sum)
the_sum = 0
return result
The lines where the accumulator is involved, are put in boldface.
I leave combining the other array the same way as an exercise, but as a hint: use an additional accumulator and zip to iterate over both arrays concurrently.
Here is a straightforward solution. which computes a list of boolean values where the value is true when accumulated element equals or exceeds the target value and calc computes an accumulation using this list.
def which(l, s):
w, a = [], 0
for e in l:
a += e
c = (a >= s)
w.append(c)
if c:
a = 0
return w
def calc(l, w):
a = 0
for (e, c) in zip(l, w):
a += e
if c:
yield a
a = 0
here is an interactive demonstration
>>> l1 = [1, 3, 4, 5, 2, 5, 3]
>>> w = which(l1, 5)
>>> w
[False, False, True, True, False, True, False]
>>> list(calc(l1, w))
[8, 5, 7]
>>> l2 = [1, 2, 1, 1, 3, 2, 1]
>>> list(calc(l2, w))
[4, 1, 5]
You can use short solutions I found out after a long struggle with flattening arrays.
For getting bounded sums use:
f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])<x]
This outputs:
>>> f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])< x]
>>> a= [1, 3, 4, 5, 2, 5, 3]
>>> f(a,5,0,7)
[1, 3, 4]
>>> sum(f(a,5,0,7))
8
>>> sum(f(a,5,3,7))
5
>>> sum(f(a,5,4,7))
7
>>>
To get your records use the function:
>>> y = lambda a,x,f,j,l: [] if j>=l else list(np.append(j,np.array(y(a,x,f,j+len(f(a,x,j,l)),l))))
From here, you can get both array of records and sums:
>>> listt=y(a,5,f,0,len(a))
>>> listt
[0.0, 3.0, 4.0, 6.0]
>>> [sum(f(a,5,int(listt[u]),len(a))) for u in range(0,len(listt)-1)]
[8, 5, 7]
>>>
Now, the bit of magic you can even use it as an index-conditional boundary for the second vector:
>>> b=[1, 2, 1, 1, 3, 2, 1]
>>> [sum(f(b,5,int(listt[u]),int(listt[u+1]))) for u in range(0,len(listt)-1)]
[4, 1, 5]
>>>
How can I shift and merge elements of a matrix to have the following result ?
Move right:
[[0,0,2,2,0,2],[8,4,2,2,0,2]] ==> [[0,0,0,0,4,2],[0,0,8,4,4,2]]
or
Move left:
[[0,0,2,2,0,2],[8,4,2,2,0,2]] ==> [[4,2,0,0,0,0],[8,4,4,2,0,0]]
It's like the 2048 game. For example, when the user do a left move, every numbers go to the left of the list and if 2 numbers side-by-side are equals, tere is an addition of the two numbers.
I would like to do it with loops.
I tried with some codes that I have found on the internet but as a begineer, I didn't found a straightforward code to understand how to do this.
Thanks in advance for the help.
If I don't misunderstand your meaning,I write some code,hope this helps:
a = [[0, 0, 2, 2, 0, 2], [8, 4, 2, 2, 0, 2]]
f = lambda x: [2 * x[0]] if x[0] == x[1] else x
def move_left(l):
c, l = [], l + [0] if len(l) % 2 else l
for i in range(0, len(l), 2):
c = c + f(l[i:i + 2])
c = list(filter(lambda x: x != 0, c))
return c + ([0] * (len(l) - len(c)))
def move_right(l):
c, l = [], l + [0] if len(l) % 2 else l
for i in range(len(l), 0, -2):
c = f(l[i - 2:i]) + c
c = list(filter(lambda x: x != 0, c))
return ([0] * (len(l) - len(c))) + c
for i in a:
print(move_left(i))
Output:
[4, 2, 0, 0, 0, 0]
[8, 4, 4, 2, 0, 0]
It seems that you're using Python3.x,so you should use list(filter(lambda x: x != 0, c)) to get the list.
Here is an example for moving the elements to the right:
def move_right(matrix):
for row in matrix:
for i, number in reversed(list(enumerate(row))):
if number == row[i-1]:
row[i] = number + row[i-1]
row[i-1] = 0
row.sort(key=lambda v: v != 0)
return matrix
Then doing:
matrix = [[0,0,2,2,0,2],[8,4,2,2,0,2]]
print(move_right(matrix))
Outputs:
[[0, 0, 0, 0, 4, 2], [0, 0, 8, 4, 4, 2]]
How it works:
First we loop through the matrix. During the first iteration: row = [0, 0, 2, 2, 0, 2]
Then we check loop over the numbers in that row using the enumerate() function. We then apply reversed() to go traverse the list backwards as to not combine a previous sum with another element in a single turn.
e.g: [4,4,8] => [0, 8, 8] => [0, 0, 16]. This should actually equal [0, 0, 8]
For the current row, this will output a result as so:
i number
5 2
4 0
3 2
2 2
1 0
0 0
Then we use the index (i) to refer to the previous number in the list. e.g. At index 2. The current number is 2, and the previous number (i-1) was 2. Since these are equal the code within the 'if' statement will execute.
The current element will then be assigned to the sum of itself and the previous element. row[i] = number + row[i-1]
The previous number will become 0, at it merged with the current number. row[i-1] = 0
Once we have looped over every number, the row will be: [0, 0, 0, 4, 0, 2]. The code row.sort(key=lambda v: v != 0) will then sort so that the zeroes are pushed to the left. See here for more details.
When moving elements to the left instead of the right, this would need to be changed to row.sort(key=lambda v: v != 0, reverse=True) to push the zeros in the other direction.
(No, this is not a homework assignment nor a contest, even though it might look like one.)
I have a list A in Python that contains the numbers range(0, len(A)). The numbers are not in order, but all of them exist in the list.
I'm looking for a simple way to build a list B where the indices and values have been swapped, i.e. a list that, for each integer n, contains the position of n in A.
Example:
A = [0, 4, 1, 3, 2]
B = [0, 2, 4, 3, 1]
I can put the code to generate B either separately or in the code that generates A. In particular, here's how I generate A:
A = [value(i) for i in range(length)]
What would be the best way to do this?
How about assigning to the pre-allocated B:
>>> A = [0, 4, 1, 3, 2]
>>> B = [0] * len(A)
>>> for k, v in enumerate(A): B[v] = k
>>> B
[0, 2, 4, 3, 1]
That would be O(n).
Using the enumerate() function to decorate each value with their index, sorting with sorted() on the values, and then un-decorate again to extract the indices in value order:
[i for i, v in sorted(enumerate(A), key=lambda iv: iv[1])]
This has a O(NlogN) time complexity because we used sorting.
Demo:
>>> A = [0, 4, 1, 3, 2]
>>> [i for i, v in sorted(enumerate(A), key=lambda iv: iv[1])]
[0, 2, 4, 3, 1]
We can also use a pre-built list to assign indices to for a O(N) solution:
B = [0] * len(A)
for i, v in enumerate(A):
B[v] = i
Demo:
>>> B = [0] * len(A)
>>> for i, v in enumerate(A):
... B[v] = i
...
>>> B
[0, 2, 4, 3, 1]
This is probably the better option if time complexity is of a big issue; for N = 100 the sorting approach will take about 461 steps vs. 100 for the pre-built list approach.
A = [0, 4, 1, 3, 2]
B = [None]*len(A)
for i, x in enumerate(A):
B[x] = i
print B
res: [0, 2, 4, 3, 1]
This is very naive;
[ [ x[0] for x in enumerate(A) if x[1] == i][0] for i in range(len(A)) ]
This works perfect,
[A.index(A.index(i)) for i in A]
This is better;
[A.index(i[0]) for i in enumerate(A)]
This one beats the other;
[A.index(i) for i in range(len(A))]
Proof;
import random as r
for l in [ r.sample(range(5),5) for n in range(5) ]:
A = l
B = [A.index(A.index(i)) for i in A]
print "A is : ", A
print "B is : ", B
print "Are B's elements indices of A? : ", A == [B.index(B.index(i)) for i in B]
print
I have an array: x = [ [1, 2], 1, 1, [2, 1, [1, 2]] ]
in which I want to count every occurrence of the number 1, and store that number in the variable one_counter. x.count(1) returns only 2 occurrences of 1, which is insufficient.
My code below serves my purpose and stores 5 in one_counter, however it looks messy and feels unpythonic to me.
Any suggestions how I can improve its pythonicity and expand it into more-dimensional lists?
Thanks!
x = [[1, 2], 1, 1, [2, 1, [1, 2]]]
one_counter = 0
for i in x:
if type(i) == list:
for j in i:
if type(j) == list:
for k in j:
if k == 1:
one_counter += 1
else:
if j == 1:
one_counter += 1
else:
if i == 1:
one_counter += 1
You could use recursion:
def flatten_count(iterable, element):
count = 0
for item in iterable:
if item == element:
count += 1
if isinstance(item, list):
count += flatten_count(item, element)
return count
Or more concisely:
def flatten_count(iterable, element):
return sum(
flatten_count(item, element) if isinstance(item, list) else item == element
for item in iterable
)
Use like this:
>>> x = [[1, 2], 1, 1, [2, 1, [1, 2]]]
>>> print(flatten_count(x, 1))
5
A hacky solution, working by conversion of datatype to string :
http://codepad.org/vNEv6B8M
import re
x = [ [1, 2], 1, 1, [2, 1, [1, 2]] ]
nums = [int(i) for i in re.findall(r'\d+', str(x))]
print(nums.count(1))
I think it's better to separate this task into 2 parts.
Part 1
Part 1 is to create an generator which will flatten the input list.
def flatten_list(L):
for i in L:
if isinstance(i,list):
for j in flatten_list(i):
yield j
else:
yield i
Testing the output:
x = [[1, 2], 1, 1, [2, 1, [1, 2]]]
for i in flatten_list(x):
print i
Output:
1
2
1
1
2
1
1
2
Part 2
Part 2 is to use the flattened list to count the number of occurrences of 1 in it:
print(sum(i==1 for i in flatten_list(x)))
Output:
5
Note that i==1 returns True if i=1, and False if i is not equal to 1. But Trueis equal to 1 and False is equal to 0, so sum just calculates the number of True occurrences (which is equal to 5 in this case).