I have a list of list and I want to remove zero values that are between numbers in each list. All my lists inside my list have same lenght.
For example:
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
desired output:
list2=[[0,1,2,3,0,0],[0,5,6,9,0]]
I was thinking about using indices to identify the first non zero value and last non zero value, but then I don't know how I can remove zeros between them.
You have the right idea, I think, with finding the first and last indices of nonzeroes and removing zeroes between them. Here's a function that does that:
def remove_enclosed_zeroes(lst):
try:
first_nonzero = next(
i
for (i, e) in enumerate(lst)
if e != 0
)
last_nonzero = next(
len(lst) - i - 1
for (i, e) in enumerate(reversed(lst))
if e != 0
)
except StopIteration:
return lst[:]
return lst[:first_nonzero] + \
[e for e in lst[first_nonzero:last_nonzero] if e != 0] + \
lst[last_nonzero:]
list1 = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
list2 = [remove_enclosed_zeroes(sublist) for sublist in list1]
# [[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Inspired by #python_user I thought about this a bit more and came up with this simpler solution:
def remove_internal_zeros(lst):
return [v for i, v in enumerate(lst) if v or not any(lst[i+1:]) or not any(lst[:i])]
This works by passing any value from the original list which is either
not zero (v); or
zero and not preceded by a non-zero value (not any(lst[:i])); or
zero and not followed by a non-zero value (not any(lst[i+1:]))
It can also be written as a list comprehension:
list2 = [[v for i, v in enumerate(lst) if v or not any(lst[:i]) or not any(lst[i+1:])] for lst in list1]
Original Answer
Here's another brute force approach, this pops all the zeros off either end of the list into start and end lists, then filters the balance of the list for non-zero values:
def remove_internal_zeros(l):
start_zeros = []
# get starting zeros
v = l.pop(0)
while v == 0 and len(l) > 0:
start_zeros.append(0)
v = l.pop(0)
if len(l) == 0:
return start_zeros + [v]
l = [v] + l
# get ending zeros
end_zeros = []
v = l.pop()
while v == 0 and len(l) > 0:
end_zeros.append(0)
v = l.pop()
# filter balance of list
if len(l) == 0:
return start_zeros + [v] + end_zeros
return start_zeros + list(filter(bool, l)) + [v] + end_zeros
print(remove_internal_zeros([0,1,0,2,3,0,0]))
print(remove_internal_zeros([0,5,6,0,0,9,0]))
print(remove_internal_zeros([0,0]))
print(remove_internal_zeros([0,5,0]))
Output:
[0, 1, 2, 3, 0, 0]
[0, 5, 6, 9, 0]
[0, 0]
[0, 5, 0]
I think this has to be done with brute force.
new = []
for sub in List1:
# Find last non-zero.
for j in range(len(sub)):
if sub[-1-j]:
lastnonzero = len(sub)-j
break
print(j)
newsub = []
firstnonzero = False
for i,j in enumerate(sub):
if j:
firstnonzero = True
newsub.append(j)
elif i >= lastnonzero or not firstnonzero:
newsub.append(j)
new.append(newsub)
print(new)
Please try this, remove all 0 between numbers in each list.:
list1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
rowIndex=len(list1) # count of rows
colIndex=len(list1[0]) # count of columns
for i in range(0, rowIndex):
noZeroFirstIndex = 1
noZeroLastIndex = colIndex - 2
for j in range(1, colIndex - 1):
if(list1[i][j] != 0):
noZeroFirstIndex = j
break
for j in range(colIndex -2, 0, -1):
if(list1[i][j] != 0):
noZeroLastIndex = j
break
for j in range(noZeroLastIndex, noZeroFirstIndex, -1):
if(list1[i][j] == 0 ):
del list1[i][j]
print(list1)
Result:
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
I wrote a pretty straight-forward approach. Try this.
def removeInnerZeroes(list):
listHold=[]
listNew = []
firstNonZeroFound = False
for item in list:
if item==0:
if firstNonZeroFound:
listHold.append(item)
else:
listNew.append(item)
else:
firstNonZeroFound=True
listHold.clear()
listNew.append(item)
listNew.extend(listHold)
return listNew
complexList = [[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
print(complexList)
complexListNew = []
for listi in complexList:
complexListNew.append(removeInnerZeroes(listi))
print(complexListNew)
One way to do it is to treat each sublist as 3 sections:
Zeros at the front, if any
Zeros at the end, if any
Numbers in the middle from which zeros are to be purged
itertools.takewhile is handy for the front and end bits.
from itertools import takewhile
List1=[[0,1,0,2,3,0,0],[0,5,6,0,0,9,0]]
def purge_middle_zeros(numbers):
is_zero = lambda x: x==0
leading_zeros = list(takewhile(is_zero, numbers))
n_lead = len(leading_zeros)
trailing_zeros = list(takewhile(is_zero, reversed(numbers[n_lead:])))
n_trail = len(trailing_zeros)
mid_numbers = numbers[n_lead:-n_trail] if n_trail else numbers[n_lead:]
mid_non_zeros = [x for x in mid_numbers if x]
return leading_zeros + mid_non_zeros + trailing_zeros
list2 = [purge_middle_zeros(sub_list) for sub_list in List1]
list2
[[0, 1, 2, 3, 0, 0], [0, 5, 6, 9, 0]]
Other notes:
the lambda function is_zero tells takewhile what the criteria are for continuing, in this case "keep taking while it's a zero"
for the mid_non_zeros section the list comprehension [x for.... ] takes all the numbers except for the zeros (the if x at the end applies the filter)
slicing notation to pick out the middle of the list, numbers[from_start:-from_end] with the negative -from_end meaning 'except for this many elements at the end'. The case where there are no trailing zeros requires a different slice expression, i.e. numbers[from_start:]
I wrote the following code out of curiosity, that is somewhat intuitive and mimics a "looking item-by-item" approach.
def remove_zeros_inbetween(list_):
new_list = list_.copy()
for j, l in enumerate(list_): # loop through the inner lists
checking = False
start = end = None
i = 0
deleted = 0
while i < len(l): # loop through the values of an inner list
if l[i] == 0: # ignore
i += 1
continue
if l[i] != 0 and not checking: # non-zero value found
checking = True # start checking for zeros
start = i
elif l[i] != 0 and checking: # if got here and checking, the finish checking
checking = False
end = i
if start and end: # if both values have been set, i.e, different to None
# delete values in-between
new_list[j] = new_list[j][:(start+1-deleted)] + new_list[j][(end-deleted):]
deleted += end - start - 1
if l[i] != 0: # for the case of two non-zero values
start = i
checking = True
else:
i = end # ignore everything up to end
end = None # restart check
i += 1
return new_list
>>> remove_zeros_inbetween([[0, 1, 0, 2, 3, 0, 5], [0, 5, 6, 0, 0, 9, 4]])
[[0, 1, 2, 3, 5], [0, 5, 6, 9, 4]]
>>> remove_zeros_inbetween([[0, 0], [0, 3, 0], [0]]))
[[0, 0], [0, 3, 0], [0]]
>>> remove_zeros_inbetween([[0, 0, 0, 0]]))
[[0, 0, 0, 0]]
You start by replacing 0 by "0" - which is not necessary. Secondly your filter call does not save the resulting list; try:
list1[i] = list(filter(lambda a: a !=0, list1[1:-1])) # changed indexing , I suppose this could work
Related
I have two lists:
lookup_list = [1,2,3]
my_list = [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
I want to count how many times the lookup_list appeared in my_list with the following logic:
The order should be 1 -> 2 -> 3
In my_list, the lookup_list items doesn't have to be next to each other: 1,4,2,1,5,3 -> should generate a match since there is a 2 comes after a 1 and a 3 comes after 2.
The mathces based on the logic:
1st match: [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
2nd match: [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
3rd match: [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
4th match: [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
The lookup_list is dynamic, it could be defined as [1,2] or [1,2,3,4], etc. How can I solve it? All the answers I've found is about finding matches where 1,2,3 appears next to each other in an ordered way like this one: Find matching sequence of items in a list
I can find the count of consecutive sequences with the below code but it doesn't count the nonconsecutive sequences:
from nltk import ngrams
lookup_list = [1,2,3]
my_list = [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
all_counts = Counter(ngrams(l2, len(l1)))
counts = {k: all_counts[k] for k in [tuple(lookup_list)]}
counts
>>> {(1, 2, 3): 2}
I tried using pandas rolling window functions but they don't have a custom reset option.
def find_all_sequences(source, sequence):
def find_sequence(source, sequence, index, used):
for i in sequence:
while True:
index = source.index(i, index + 1)
if index not in used:
break
yield index
first, *rest = sequence
index = -1
used = set()
while True:
try:
index = source.index(first, index + 1)
indexes = index, *find_sequence(source, rest, index, used)
except ValueError:
break
else:
used.update(indexes)
yield indexes
Usage:
lookup_list = [1,2,3]
my_list = [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
print(*find_all_sequences(my_list, lookup_list), sep="\n")
Output:
(0, 1, 2)
(6, 7, 11)
(9, 10, 15)
(14, 16, 17)
Generator function find_all_sequences() yields tuples with indexes of sequence matches. In this function we initialize loop which will be stopped when list.index() call will throw ValueError. Internal generator function find_sequence() yields index of every sequence item.
According to this benchmark, my method is about 60% faster than one from Andrej Kesely's answer.
The function find_matches() returns indices where the matches from lookup_list are:
def find_matches(lookup_list, lst):
buckets = []
def _find_bucket(i, v):
for b in buckets:
if lst[b[-1]] == lookup_list[len(b) - 1] and v == lookup_list[len(b)]:
b.append(i)
if len(b) == len(lookup_list):
buckets.remove(b)
return b
break
else:
if v == lookup_list[0]:
buckets.append([i])
rv = []
for i, v in enumerate(my_list):
b = _find_bucket(i, v)
if b:
rv.append(b)
return rv
lookup_list = [1, 2, 3]
my_list = [1, 2, 3, 4, 5, 2, 1, 2, 2, 1, 2, 3, 4, 5, 1, 3, 2, 3, 1]
print(find_matches(lookup_list, my_list))
Prints:
[[0, 1, 2], [6, 7, 11], [9, 10, 15], [14, 16, 17]]
Here is a recursive solution:
lookup_list = [1,2,3]
my_list = [1,2,3,4,5,2,1,2,2,1,2,3,4,5,1,3,2,3,1]
def find(my_list, continue_from_index):
if continue_from_index > (len(my_list) - 1):
return 0
last_found_index = 0
found_indizes = []
first_occuring_index = 0
found = False
for l in lookup_list:
for m_index in range(continue_from_index, len(my_list)):
if my_list[m_index] is l and m_index >= last_found_index:
if not found:
found = True
first_occuring_index = m_index
last_found_index = m_index
found += 1
found_indizes.append(str(m_index))
break
if len(found_indizes) is len(lookup_list):
return find(my_list, first_occuring_index+1) + 1
return 0
print(find(my_list, 0))
my_list = [5, 6, 3, 8, 2, 1, 7, 1]
lookup_list = [8, 2, 7]
counter =0
result =False
for i in my_list:
if i in lookup_list:
counter+=1
if(counter==len(lookup_list)):
result=True
print (result)
I have an array and given an array of size N containing positive integers and I want to count number of smaller elements on right side of each array.
for example:-
Input:
N = 7
arr[] = {12, 1, 2, 3, 0, 11, 4}
Output: 6 1 1 1 0 1 0
Explanation: There are 6 elements right
after 12. There are 1 element right after
1. And so on.
And my code for this problem is like as :-
# python code here
n=int(input())
arr=list(map(int,input().split()))
ans=0
ANS=[]
for i in range(n-1):
for j in range(i+1,n):
if arr[i]>arr[j]:
ans+=1
ANS.append(ans)
ans=0
ANS.append(0)
print(ANS)
but the above my code take O(n^2) time complexity and I want to reduce the this. If anyone have any idea to reduce above python code time complexity please help me. Thank you.
This solution is O(n log(n)) as it is three iterations over the values and one sorting.
arr = [12, 1, 2, 3, 0, 11, 4]
# Gather original index and values
tups = []
for origin_index, el in enumerate(arr):
tups.append([origin_index, el])
# sort on value
tups.sort(key=lambda t: t[1])
res = []
for sorted_index, values in enumerate(tups):
# check the difference between the sorted and original index
# If there is a positive value we have the n difference smaller
# values to the right of this index value.
if sorted_index - values[0] > 0:
res.append([values[0], (sorted_index - values[0])])
elif sorted_index - values[0] == 0:
res.append([values[0], (sorted_index - values[0]) + 1])
else:
res.append([values[0], 0])
origin_sort_res = [0 for i in range(len(arr))]
for v in res:
# Return the from the sorted array to the original indexing
origin_sort_res[v[0]] = v[1]
print(origin_sort_res)
try this(nlog2n)
def solution(nums):
sortns = []
res = []
for n in reversed(nums):
idx = bisect.bisect_left(sortns, n)
res.append(idx)
sortns.insert(idx,n)
return res[::-1]
print(solution([12, 1, 2, 3, 0, 11, 4]))
# [6, 1, 1, 1, 0, 1, 0]
The Goal
I would like to get the ranges where values are not None in a list, so for example:
test1 = [None, 0, None]
test2 = [2,1,None]
test3 = [None,None,3]
test4 = [1,0,None,0,0,None,None,1,None,0]
res1 = [[1,1]]
res2 = [[0,1]]
res3 = [[2,2]]
res4 = [[0,1],[3,4],[7,7],[9,9]]
What I have tried
This is my super lengthy implementation, which does not perfectly work...
def get_not_None_ranges(list_):
# Example [0, 2, None, 1, 4] -> [[0, 1], [3, 4]]
r = []
end_i = len(list_)-1
if list_[0] == None:
s = None
else:
s = 0
for i, elem in enumerate(list_):
if s != None:
if elem == None and end_i != i:
r.append([s,i-1])
s = i+1
if end_i == i:
if s > i:
r=r
elif s==i and elem == None:
r=r
else:
r.append([s,i])
else:
if elem != None:
s = i
if end_i == i:
if s > i:
r=r
else:
r.append([s,i])
return r
As you can see the results are sometimes wrong:
print(get_not_None_ranges(test1))
print(get_not_None_ranges(test2))
print(get_not_None_ranges(test3))
print(get_not_None_ranges(test4))
[[1, 2]]
[[0, 2]]
[[2, 2]]
[[0, 1], [3, 4], [6, 5], [7, 7], [9, 9]]
So, I was wondering if you guys know a much better way to achieve this?
Use itertools.groupby:
from itertools import groupby
test1 = [None, 0, None]
test2 = [2, 1, None]
test3 = [None, None, 3]
test4 = [1, 0, None, 0, 0, None, None, 1, None, 0]
def get_not_None_ranges(lst):
result = []
for key, group in groupby(enumerate(lst), key=lambda x: x[1] is not None):
if key:
index, _ = next(group)
result.append([index, index + sum(1 for _ in group)])
return result
print(get_not_None_ranges(test1))
print(get_not_None_ranges(test2))
print(get_not_None_ranges(test3))
print(get_not_None_ranges(test4))
Output
[[1, 1]]
[[0, 1]]
[[2, 2]]
[[0, 1], [3, 4], [7, 7], [9, 9]]
A non-groupby solution that doesn't need extra treatment for the last group:
def get_not_None_ranges(lst):
result = []
it = enumerate(lst)
for i, x in it:
if x is not None:
first = last = i
for i, x in it:
if x is None:
break
last = i
result.append([first, last])
return result
Whenever I find the first of a non-None streak, I use an inner loop to right away run to the last of that streak. To allow both loops to use the same iterator, I store it in a variable.
You just need to iterate over the list, and check for two conditions:
If the previous element is None and the current element is not None, start a new "range".
If the previous element is not None and the current element is None, end the currently active range at the previous index.
def gnnr(lst):
all_ranges = []
current_range = []
prev_item = None
for index, item in enumerate(lst):
# Condition 1
if prev_item is None and item is not None:
current_range.append(index)
# Condition 2
elif prev_item is not None and item is None:
current_range.append(index - 1) # Close current range at the previous index
all_ranges.append(current_range) # Add to all_ranges
current_range = [] # Reset current_range
prev_item = item
# If current_range isn't closed, close it at the last index of the list
if current_range:
current_range.append(index)
all_ranges.append(current_range)
return all_ranges
Calling this function with your test cases gives the expected output:
[[1, 1]]
[[0, 1]]
[[2, 2]]
[[0, 1], [3, 4], [7, 7], [9, 9]]
Well, we can solve this by using classic sliding window approach.
Here is the solution which works fine:
def getRanges(nums):
left = right = 0
ranges, n = [], len(nums)
while right < n:
while left < n and nums[left] == None:
left += 1
right += 1
while right < n and nums[right] != None:
right += 1
if right >= n:
break
ranges.append([left, right - 1])
left = right = right + 1
return ranges + [[left, right - 1]] if right - 1 >= left else ranges
Lets test it:
test = [
[1, 0, None, 0, 0, None, None, 1, None, 0],
[None, None, 3],
[2, 1, None],
[None, 0, None],
]
for i in test:
print(getRanges(i))
Output:
[[0, 1], [3, 4], [7, 7], [9, 9]]
[[2, 2]]
[[0, 1]]
[[1, 1]]
Give it a try. Code uses Type Hint and a named tuple in order to increase readablity.
from typing import NamedTuple,List,Any
class Range(NamedTuple):
left: int
right: int
def get_ranges(lst: List[Any]) -> List[Range]:
ranges : List[Range] = []
left = None
right = None
for i,x in enumerate(lst):
is_none = x is None
if is_none:
if left is not None :
right = right if right is not None else left
ranges.append(Range(left,right))
left = None
right = None
else:
if left is None:
left = i
else:
right = i
if left is not None:
right = right if right is not None else left
ranges.append(Range(left,right))
return ranges
data = [[1,0,None,0,0,None,None,1,None,0],[None,None,3],[2,1,None],[None, 0, None]]
for entry in data:
print(get_ranges(entry))
outut
[Range(left=0, right=1), Range(left=3, right=4), Range(left=7, right=7), Range(left=9, right=9)]
[Range(left=2, right=2)]
[Range(left=0, right=1)]
[Range(left=1, right=1)]
Using first and last of each group of not nones:
from itertools import groupby
def get_not_None_ranges(lst):
result = []
for nones, group in groupby(enumerate(lst), lambda x: x[1] is None):
if not nones:
first = last = next(group)
for last in group:
pass
result.append([first[0], last[0]])
return result
Here's my example. It is definitely NOT the most efficient way, but I think it is more intuitive and you can optimize it later.
def get_not_None_ranges(list_: list):
res = []
start_index = -1
for i in range(len(list_)):
e = list_[i]
if e is not None:
if start_index < 0:
start_index = i
else:
if start_index >= 0:
res.append([start_index, i - 1])
start_index = -1
if start_index >= 0:
res.append([start_index, len(list_) - 1])
return res
The main thought of this function:
start_index is initialized with -1
When we meet not None element, set start_index to its index
When we meet None, save [start_index, i - 1 (since the previous element is the end of the session)]. Then set start_index back to -1.
When we meet None but start_index is -1, we need to do nothing since we have not met the not None element this turn. For the same reason, do nothing when we meet not None when start_index > 0.
When the loop end but start_index still larger than 0, it means we haven't record this valid turn. So we need to do that manually.
I think it may be a little bit complex, it may help to paste the code above and debug it line by line in a debugger.
How about:-
test1 = [None, 0, None]
test2 = [2, 1, None]
test3 = [None, None, 3]
test4 = [1, 0, None, 0, 0, None, None, 1, None, 0]
def goal(L):
r = []
_r = None
for i, e in enumerate(L):
if e is not None:
if _r:
_r[1] = i
else:
_r = [i, i]
else:
if _r:
r.append(_r)
_r = None
if _r:
r.append(_r)
return r
for _l in [test1, test2, test3, test4]:
print(goal(_l))
Another solution (one-liner with itertools.groupby):
from itertools import groupby
out = [[(v := list(g))[0][1], v[-1][1]] for _, g in groupby(enumerate(i for i, v in enumerate(testX) if not v is None), lambda k: k[0] - k[1],)]
Tests:
test1 = [None, 0, None]
test2 = [2, 1, None]
test3 = [None, None, 3]
test4 = [1, 0, None, 0, 0, None, None, 1, None, 0]
tests = [test1, test2, test3, test4]
for t in tests:
out = [
[(v := list(g))[0][1], v[-1][1]]
for _, g in groupby(
enumerate(i for i, v in enumerate(t) if not v is None),
lambda k: k[0] - k[1],
)
]
print(out)
Prints:
[[1, 1]]
[[0, 1]]
[[2, 2]]
[[0, 1], [3, 4], [7, 7], [9, 9]]
I have a list such as
[0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
I want to find start and end position where element are positive:
[[3,5],[9,12],[16,18]]
what is the best way to do this in python?
(build in function in python such as: find,lambda,itemgetter and so on.)
And lastly, regex version. ;)
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
input3 = str(list(
map(lambda i_x: i_x[0] * (i_x[1] and (1, -1)[i_x[1] < 0]), enumerate(input))
))
import re
s = re.sub(r'([\[ ]0[\],])+', ' ', input3)
s = s.replace(', ', '], [')
if s[-1:] != ']':
s = s[:-2] + ']'
s = '[' + s[2:]
s = re.sub(r' [0-9]+,', '', s)
output = list(eval(s))
print(output) # [[3, 5], [9, 12], [16, 18]]
Crude for solution. :(
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
pair = []
for i in range(len(input)):
if input[i] > 0:
if len(pair) > 1:
pair.pop()
pair.append(i)
else:
if pair:
output.append(pair)
pair = []
print(output) # [[3, 5], [9, 12], [16, 18]]
Not sure if ranges can go off the ends of the array or not.
def get_positive_ranges(a):
in_range = False
result = []
for i in range(len(a)):
if not in_range:
if a[i] > 0:
in_range = True
first = i
else: # Inside a range
if a[i] <= 0: # End of range
in_range = False
result.append([first, i - 1])
if in_range: # Tidy
result.append([first, i])
return result
print(get_positive_ranges([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]))
print(get_positive_ranges([]))
print(get_positive_ranges([1]))
print(get_positive_ranges([0, 1]))
print(get_positive_ranges([0, 1, 0]))
Here is a numpy solution, not sure if this is better than a naive for-loop though; see inline comments for explanation.
import numpy as np
a = np.array([0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0])
# get indices of non-zero elements in a
nze = a.nonzero()[0]
# check where the differences of these indices are unequal to one; there you have a jump to/from 0
nze_diff = np.where(np.diff(nze) > 1)[0] + 1
# if a starts with 0, add the index 0
if nze_diff[0] != 0:
nze_diff = np.insert(nze_diff, 0, 0)
# store output
res = []
# loop through the indices and add the desired slices
for ix, i in enumerate(nze_diff):
try:
sl = nze[i:nze_diff[ix + 1]]
res.append([sl[0], sl[-1]])
# means we reached the end of nze_diff
except IndexError:
sl = nze[i:]
res.append([sl[0], sl[-1]])
If you run it for your a, you receive the desired output:
[[3, 5], [9, 12], [16, 18]]
There are probably smarter solutions than this, but this might get you started.
If you want to get the entire range, it simplifies a bit:
res2 = []
for ix, i in enumerate(nze_diff):
try:
res2.append(nze[i:nze_diff[ix + 1]])
except IndexError:
res2.append(nze[i:])
Then res2 would be:
[array([3, 4, 5]), array([ 9, 10, 11, 12]), array([16, 17, 18])]
This works
lst = [0, 0, 0, 12, 34, 86, 0, 0, 0, 95, 20, 1, 6, 0, 0, 0, 11, 24, 67, 0, 0, 0]
n = len(lst)
starting_points = [i for i in range(n) if lst[i] > 0 and (lst[i - 1] == 0 or i == 0)]
end_points = [next((i for i in range(j + 1, n) if lst[i] == 0), n) - 1 for j in starting_points]
print zip(starting_points, end_points)
output
[(3, 5), (9, 12), (16, 18)]
If performance is the key, you should test which implementaion is the most fast with your very long list. Anyway, this is 'no array acess by index' version, hopefully for boosting speed. And it uses map, lambda, index(find), if it pleases you. Though, of course it uses while.
input = [0,0,0,12,34,86,0,0,0,95,20,1,6,0,0,0,11,24,67,0,0,0]
output = []
input2 = list(map(lambda x: x and (1, -1)[x < 0], input)) # mapping by 'math.sign'-like func
start = end = 0
while end < len(input2):
try:
start = input2.index(1, end + 1)
end = input2.index(0, start) - 1
output.append([start, end])
except ValueError:
break
if start >= end:
output.append([start, len(input2) - 1])
print(output) # [[3, 5], [9, 12], [16, 18]]
I am searching for a clean and pythonic way of checking if the contents of a list are greater than a given number (first threshold) for a certain number of times (second threshold). If both statements are true, I want to return the index of the first value which exceeds the given threshold.
Example:
# Set first and second threshold
thr1 = 4
thr2 = 5
# Example 1: Both thresholds exceeded, looking for index (3)
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
# Example 2: Only threshold 1 is exceeded, no index return needed
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
I don't know if it's considered pythonic to abuse the fact that booleans are ints but I like doing like this
def check(l, thr1, thr2):
c = [n > thr1 for n in l]
if sum(c) >= thr2:
return c.index(1)
Try this:
def check_list(testlist)
overages = [x for x in testlist if x > thr1]
if len(overages) >= thr2:
return testlist.index(overages[0])
# This return is not needed. Removing it will not change
# the outcome of the function.
return None
This uses the fact that you can use if statements in list comprehensions to ignore non-important values.
As mentioned by Chris_Rands in the comments, the return None is unnecessary. Removing it will not change the result of the function.
If you are looking for a one-liner (or almost)
a = filter(lambda z: z is not None, map(lambda (i, elem) : i if elem>=thr1 else None, enumerate(list1)))
print a[0] if len(a) >= thr2 else false
A naive and straightforward approach would be to iterate over the list counting the number of items greater than the first threshold and returning the index of the first match if the count exceeds the second threshold:
def answer(l, thr1, thr2):
count = 0
first_index = None
for index, item in enumerate(l):
if item > thr1:
count += 1
if not first_index:
first_index = index
if count >= thr2: # TODO: check if ">" is required instead
return first_index
thr1 = 4
thr2 = 5
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
print(answer(list1, thr1, thr2)) # prints 3
print(answer(list2, thr1, thr2)) # prints None
This is probably not quite pythonic though, but this solution has couple of advantages - we keep the index of the first match only and have an early exit out of the loop if we hit the second threshold.
In other words, we have O(k) in the best case and O(n) in the worst case, where k is the number of items before reaching the second threshold; n is the total number of items in the input list.
I don't know if I'd call it clean or pythonic, but this should work
def get_index(list1, thr1, thr2):
cnt = 0
first_element = 0
for i in list1:
if i > thr1:
cnt += 1
if first_element == 0:
first_element = i
if cnt > thr2:
return list1.index(first_element)
else:
return "criteria not met"
thr1 = 4
thr2 = 5
list1 = [1, 1, 1, 5, 1, 6, 7, 3, 6, 8]
list2 = [1, 1, 6, 1, 1, 1, 2, 1, 1, 1]
def func(lst)
res = [ i for i,j in enumerate(lst) if j > thr1]
return len(res)>=thr2 and res[0]
Output:
func(list1)
3
func(list2)
false