Reputation: 29
I want to find a peak in a list.
def peaks(lst):
num = 0
leni = len(lst)
print(leni)
for i in range(1,leni - 1):
if lst[i] > lst[i-1] and lst[i] > lst[i+1]:
num = num + 1
for i in range(leni):
print(i)
if i == 0:
if lst[i] > lst[i+1]:
num = num + 1
elif i == leni+1:
if lst[i] > lst[i-1]:
num = num + 1
return num
This code doesn't work when it should check the last object.
When I try [1,2,3]
I get 0
instead of 1
.
Upvotes: 3
Views: 768
Reputation: 2180
Counting of direct-neighbor peaks can be a performance-relevant task and the list of implementations is getting longer. Good reasons to compare the runtime. In a first run, I decided to go for one test set only. Obviously, the different implementations may reveal their strength at different lengths of lists. For instance, the optimal implementation of border value handling seems to be a candidate that depends on the list length.
Output:
count_peaks_schwobaseggl: elapsed time 1.44 s
count_peaks_sahasrara62: elapsed time 1.50 s
count_peaks_saikat: elapsed time 2.27 s
count_peaks_tom_wojcik: elapsed time 4.11 s
count_peaks_igian: elapsed time 3.65 s
count_peaks_cloud_balancing: elapsed time 1.86 s
Implementation:
import random
import time
from typing import List
def measure_runtime_in_s(func, test_lists):
start = time.time()
results = []
for test_list in test_lists:
max_cnt = func(test_list)
results.append(max_cnt)
return time.time() - start, results
def run_experiment(funcs, nlists=1000, len_range=(20, 10000), num_range=(-100, 100)):
assert len(funcs) > 0
# generate test data
test_lists = [[random.randint(*num_range) for _ in range(random.randint(*len_range))]
for _ in range(nlists)]
# run it for all implementations and check results
_, ref_results = measure_runtime_in_s(funcs[0], test_lists)
for func in funcs:
failed = False
time_in_s, results = measure_runtime_in_s(func, test_lists)
if not all(result == ref_result for result, ref_result in zip(results, ref_results)):
failed = True
print(
f"{func.__name__}: elapsed time {time_in_s:.2f} s"
+ (" (FAILED TESTS!)" if failed else ""))
def count_peaks_schwobaseggl(lst):
lst = [float("-inf")] + lst + [float("-inf")]
return sum(a < b > c for a, b, c in zip(lst, lst[1:], lst[2:]))
def count_peaks_sahasrara62(array):
if len(array) < 2:
return 0
array2 = [array[1]] + array + [array[-2]]
count = sum([1 for i in range(len(array2) - 2) if array2[i + 2] < array2[i + 1] > array2[i]])
return count
def count_peaks_saikat(lst):
num = 0
leni = len(lst)
for i in range(leni):
if i == 0:
if lst[i] > lst[i + 1]:
num = num + 1
elif i == leni - 1:
if lst[i] > lst[i - 1]:
num = num + 1
else:
if lst[i] > lst[i - 1] and lst[i] > lst[i + 1]:
num = num + 1
return num
def count_peaks_igian(ls):
res = [ls[0] > ls[1], ls[-1] > ls[-2]]
for n in range(len(ls)-2):
a, b, c = ls[n:n+3]
res.insert(-1, b > max([a, c]))
return sum(res) # < modified
def count_peaks_tom_wojcik(lst: List[int]):
found = 0
for i, current_value in enumerate(lst):
is_first_object = i == 0
is_last_object = i == len(lst) - 1
if is_first_object:
previous_val = None
else:
previous_val = lst[i-1]
if is_last_object:
next_val = None
else:
next_val = lst[i+1]
if is_first_object and not is_last_object:
found += 1 if current_value > next_val else 0
elif is_last_object and not is_first_object:
found += 1 if current_value > previous_val else 0
elif not is_last_object and not is_last_object:
found += 1 if previous_val < current_value > next_val else 0
return found
def count_peaks_cloud_balancing(lst):
lst_len = len(lst)
hits = 0
for i in range(1, lst_len - 1):
num_to_test = lst[i]
before = lst[i - 1]
after = lst[i + 1]
if num_to_test > before and num_to_test > after:
hits += 1
# This is for the case lst contains only 1 member and you want to count it as 1
if lst_len == 1:
hits += 1
# For checking the edges
if lst_len > 1:
if lst[0] > lst[1]:
hits += 1
if lst[lst_len - 1] > lst[lst_len - 2]:
hits += 1
return hits
if __name__ == "__main__":
run_experiment([
count_peaks_schwobaseggl,
count_peaks_sahasrara62,
count_peaks_saikat,
count_peaks_tom_wojcik,
count_peaks_igian,
count_peaks_cloud_balancing,
])
Upvotes: 0
Reputation: 1696
Should do the trick
def peaks(lst):
lst_len = len(lst)
hits = 0
for i in range(1,lst_len-1):
num_to_test = lst[i]
before = lst[i-1]
after = lst[i+1]
if num_to_test > before and num_to_test > after:
hits+=1
# This is for the case lst contains only 1 member and you want to count it as 1
if lst_len == 1:
hits+=1
# For checking the edges
if lst_len > 1:
if lst[0] > lst[1]:
hits+=1
if lst[lst_len-1] > lst[lst_len-2]:
hits+=1
return hits
Upvotes: 0
Reputation: 113
try this one
def peaks(lst):
num = 0
leni = len(lst)
for i in range(leni):
if i == 0:
if lst[i] > lst[i+1]:
num = num + 1
elif i == leni-1:
if lst[i] > lst[i-1]:
num = num + 1
else:
if lst[i] > lst[i-1] and lst[i] > lst[i+1]:
num = num + 1
return num
Upvotes: 0
Reputation: 11247
you can add proxy value of first element and second last element, at front and last and then check the condition from the first element to second last element
def peaks(array):
if len(array)<2:
return 0
array2 = [array[1]] + array + [array[-2]]
count = sum([1 for i in range(len(array2)-2) if array2[i+2]<array2[i+1] >array2[i]])
return count
Upvotes: 0
Reputation: 6189
It seems like you're just starting with python. Your code is difficult to follow. There are many possible solutions for this problem but I'd advise to write simple code. Then worry about performance.
Readability first!
from typing import List
def peaks(lst: List[int]):
found = 0
for i, current_value in enumerate(lst):
is_first_object = i == 0
is_last_object = i == len(lst) - 1
if is_first_object:
previous_val = None
else:
previous_val = lst[i-1]
if is_last_object:
next_val = None
else:
next_val = lst[i+1]
if is_first_object and not is_last_object:
found += 1 if current_value > next_val else 0
elif is_last_object and not is_first_object:
found += 1 if current_value > previous_val else 0
elif not is_last_object and not is_last_object:
found += 1 if previous_val < current_value > next_val else 0
return found
print(peaks([1, 2, 3]))
Upvotes: 0
Reputation: 11193
Alternative way.
Check the extremes then check the core, three by three:
def count_peaks(ls):
res = [ls[0] > ls[1], ls[-1] > ls[-2]]
for n in range(len(ls)-2):
a, b, c = ls[n:n+3]
res.insert(-1, b > max([a, c]))
return res
Insert before the last element, to reflect the peaks position in the result.
So, in case of [1,2,1,2,4,5]
:
count_peaks([1,2,1,2,4,5])
#=>[False, True, False, False, False, True]
Just return sum(res)
instead to get the desired result: 2
Upvotes: 0
Reputation: 73498
You could do some trickery to count peaks by making the boundary special cases not so special any more:
def peaks(lst):
lst = [float("-inf")] + lst + [float("-inf")]
return sum(a < b > c for a, b, c in zip(lst, lst[1:], lst[2:]))
Upvotes: 4
Reputation: 8077
Hello and welcome to Stackoverflow!
Note that range(leni)
is a sequence of numbers from 0 to leni - 1
inclusive. So your condition i == leni+1
is never satisfied. You may replace it to i == leni - 1
.
Note also that you don't need a second loop. You may just replace it with
if lst[0] > lst[1]:
num = num + 1
if lst[-1] > lst[-2]:
num= num + 1
Here lst[-1]
is the same as lst[leni - 1]
and lst[-2]
is the same as lst[leni - 2]
.
Upvotes: 1