I have a large data set and I am trying to find the local maxima and their indices. I have made it to get the local maxima but can't find a way to get their indices.
The thing is that I need the maxima only for positive values and we should ignore the negative values. In other words, it would be like splitting the list in several segments with positive values and getting those maxima.
For example, the list can be smth like this:
test_data = [2, 35, -45, 56, 5, 67, 21, 320, 55, -140, -45, -98, -23, -23, 35, 67, 34, -30, -86, 4, -93, 35, 88, 32, 98, -6]
My code is:
`
def global_peaks(test_data):
counter1 = []
index = []
global_peak = []
global_idx = []
for idx, data in enumerate(test_data):
if data > 0:
counter1.append(data)
index.append(idx)
else:
if(len(counter1) != 0):
global_peak.append(max(counter1))
index.append(idx)
global_idx.append(index)
counter1.clear()
index.clear()
return global_peak, global_idx
```
global_peaks(test_data)
```
([35, 320, 67, 4, 98], [[], [], [], [], []])
```
result are correct when it comes to the values, but not the indices
def global_peaks(test_data):
counter1 = []
index = []
global_peak = []
global_idx = []
for idx, data in enumerate(test_data):
if data > 0:
counter1.append(data)
index.append(idx)
else:
if(len(counter1) != 0):
global_peak.append(max(counter1))
index.append(idx)
global_idx.append(index)
counter1.clear()
index.clear()
index.append(1) # <-- for demonstration
return global_peak, global_idx
global_peaks(test_data)
# Output
([35, 320, 67, 4, 98], [[1], [1], [1], [1], [1]])
One problem comes with appending a list (global_idx.append(index)), which is a mutable object. You are appending a reference to this list so your output will show whatever is within this list at the moment of outputting.
What you need to use instead is a copy of that list (index.copy()), though this still does not give you the result you need.
Why should these lines give you the corresponding index
index.append(idx)
global_idx.append(index)
This should give you its corresponding index:
max_idx = index[counter1.index(max(counter1))]
One comment on your general approach: Be aware that if you have 2 or more local maxima within a region of only positive values, you would only find a single one. Is that really what you want?
The full code looks like this:
def global_peaks(test_data):
counter1 = []
index = []
global_peak = []
global_idx = []
for idx, data in enumerate(test_data):
if data > 0:
counter1.append(data)
index.append(idx)
else:
if(len(counter1) != 0):
global_peak.append(max(counter1))
max_idx = index[counter1.index(max(counter1))] # <- changed
global_idx.append(max_idx) # <- changed
counter1.clear()
index.clear()
return global_peak, global_idx
#Output
globla_peaks(test_data)
([35, 320, 67, 4, 98], [1, 7, 15, 19, 24])
Related
I was trying to create a python program which swaps the first and last elements of a list. I passed a pre-created list into the algorithm and it worked perfectly. Here's my code:
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = [12,33,42,76,46,97]
swapFirstAndLast(l)
print(l)
Output:
[97, 33, 42, 76, 46, 12]
Then I tried to create functions; one function to create a list of randomly generated numbers, and the second function to perform the swapping operation. Although everything makes sense to me, it is not performing the swapping operation now. This is the code I came up with:
import random
def generateList(size):
list1 = []
for i in range(size):
list1.append(random.randint(0,99))
return list1
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = generateList(5)
l1 = swapFirstAndLast(l)
print(l,l1)
Output:
[49, 78, 63, 82, 72] [49, 78, 63, 82, 72]
As you can see, it does not perform the swapping operation now. I am not able to understand where I am going wrong.
You are swapping the first and the last element of the initial list (i.e., l) too! Please look at this slightly modified example:
import random
def generateList(size):
list1 = []
for i in range(size):
list1.append(random.randint(0,99))
return list1
def swapFirstAndLast(list_to_be_swapped):
size = len(list_to_be_swapped)
list_to_be_swapped[0],list_to_be_swapped[size-1] = list_to_be_swapped[size-1],list_to_be_swapped[0]
return list_to_be_swapped
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l, l1)
Output:
[54, 14, 3, 38, 87]
[87, 14, 3, 38, 54] [87, 14, 3, 38, 54]
As you can see, the list l has been changed.
The thing here is that you are not creating a new list, but you're modifying the existing one. It doesn't matter if it has a different name within the function.
If you want to retain the original list l, and also return a separate swapped list l1, you have to create a new list! Here is how you can do it:
import random
def generateList(size):
return [random.randint(0, 99) for _ in range(size)]
def swapFirstAndLast(list_to_be_swapped):
new_list = list_to_be_swapped.copy()
new_list[0], new_list[-1] = new_list[-1], new_list[0]
return new_list
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l, l1)
Output:
[38, 59, 86, 26, 19]
[38, 59, 86, 26, 19] [19, 59, 86, 26, 38]
your program works ! your function just modifies the list directly, you can see it better if you do this :
l = generateList(5)
print(l)
l1 = swapFirstAndLast(l)
print(l1)
It turns out that you have already swapped the list (i.e. l) it's just when your print (l,l1) that it looks like you haven't swapped it because it's printing the swapped version of (l). put the print(l) line above ( l1 = swapFirstAndLast(l) ) to see it!
the swapping can be done by using index:
def swapFirstAndLast(lst):
lst[0], lst[-1] = lst[-1], lst[0]
return lst
lst = [12,33,42,76,46,97]
print(swapFirstAndLast(lst))
result is: [97, 33, 42, 76, 46, 12]
Lets say that in Python I need to generate a 2d matrix that is n * n. My function get's a parameter "n" and it generates a n*n list, such as
for n = 3
[[1,2,3],[4,5,6],[7,8,9]]
That isn't the problem. My function needs to make a 2d list which is first filled left to right, then right to left, and so on... How do i do this?
Example;
for n = 3
it should generate:
[[1,2,3],[6,5,4],[7,8,9]]
and for n = 4:
[[1,2,3,4],[8,7,6,5],[9,10,11,12],[16,15,14,13]]
the simpler way is just to reverse the list every other step
m = []
for line in range(n):
if line%2 == 0:
m.append([*range(line*n,line*n+n)])
else:
m.append([*range(line*n+n,line*n,-1)])
Here I have used some cool python methods like list slicing and ternary operator to make it short and sweet.
n = 5
primes =
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
matrix = [primes[i:i+n] if i%2==0 else primes[i:i+n][::-1] for i in
range(0,len(primes),n)]
print(matrix)
here a straightforward solution but probably not the best:
n = 5
matrix = []
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
def fill_matrix(arr):
index = 0
for i in range(n):
holder = []
for _ in range(n):
holder.append(primes[index])
index += 1
if i%2 != 0:
arr.append(list(reversed(holder)))
else:
arr.append(holder)
fill_matrix(matrix)
print(matrix)
this will output for n=5: [[2, 3, 5, 7, 11], [29, 23, 19, 17, 13], [31, 37, 41, 43, 47], [71, 67, 61, 59, 53], [73, 79, 83, 89, 97]]
We can try this
[[k+n-x-1 if ((k-1)/n)%2 else k+x for x in range(n)] for k in range(1,n*n+1,n)]
which gives
[[1, 2, 3], [6, 5, 4], [7, 8, 9]]
I'm actually having a hard time explaining this problem. The title ask for this:
limit = 100
l = [1, 2, 4, 9, 33, 77, 85, 100, 151, 304, 405, 407, 499]
do_something(l, limit)
[
[1,2,4,9,33,77,85,100],
[151],
[304],
[405, 407, 499]
]
But that was just one step in my thought process. Truth is, I need this result:
limit = 100
l = [1, 2, 4, 9, 33, 77, 85, 100, 151, 304, 405, 407, 499]
do_something(l, limit)
[
range(1,101),
range(151,152),
range(304,305),
range(405,500)
]
Notice range never exceeds the limit of 100, and its creating a list out of ints from l that fall within the limit (starting from l[0] and resetting any time limit is exceeded). I am brainstorming ideas over here, but everything I can think of iterates over my list several times. If that's necessary so be it, but I'd like to be a bit more efficient than that.
Thanks for any help
Loop through the sorted list. Each time you start a group, save the first value of the range in one variable. Then as you step through the list, update the last value of the range to the current element. Whenever the element exceeds first+limit, you start a new range.
def do_something(l, limit):
if l == []:
return []
end = l[0] + limit
result = []
first = last = l[0]
for i in sorted(l):
if i < end:
last = i
else:
result.append(range(first, last + 1))
first = last = i
# reset the limit
end = i + limit
# append the last sublist
result.append(range(first, last+1))
return result
you also can emplement such logic:
def do_something(l,limit):
res = []
x = y = 0
for y in range(len(l)):
if y==len(l)-1 or l[x] + limit <= l[y+1]:
res.append(range(l[x], l[y]+1))
x = y + 1
return res
do_something(l,limit)
>>> out
'''
[range(1, 101), range(151, 152), range(304, 305), range(405, 500)]
Why dont you try dictionaries to store list of values within limit.
In this implementation, list is not necessarily to be sorted.
limit = 100
ranged_list = {}
for col in l:
lst = ranged_list.get((col-1) // limit, [])
lst.append(col)
ranged_list[(col-1) // limit] = lst
print(list(ranged_list.values()))
>>> [
[1, 2, 4, 9, 33, 77, 85,100],
[151],
[304],
[405, 407, 499]]
I am looking for a fast method to traverse a list of sets, and to expand each set by finding its union with any other element of the list with which it shares at least one element.
For example, suppose that I have four rows of data, where each row corresponds to a set of unique elements
0, 5, 101
8, 9, 19, 21
78, 79
5, 7, 63, 64
The first and the last rows have the intersecting element 5 and so after performing my operation I want to have the unions
0, 5, 7, 63, 64, 101
8, 9, 19, 21
78, 79
0, 5, 7, 63, 64, 101
Right now, I can nearly do this with two loops:
def consolidate_list(arr):
"""
arr (list) : A list of lists, where the inner lists correspond to sets of unique integers
"""
arr_out = list()
for item1 in arr:
item_additional = list() # a list containing all overlapping elements
for item2 in arr:
if len(np.intersect1d(item1, item2)) > 0:
item_additional.append(np.copy(item2))
out_val = np.unique(np.hstack([np.copy(item1)] + item_additional)) # find union of all lists
arr_out.append(out_val)
return arr_out
The issue with this approach is that it needs to be run multiple times, until the output stops changing. Since the input might be jagged (ie, different numbers of elements per set), I can't see a way to vectorize this function.
This problem is about creating disjoint sets and so I would use union-find methods.
Now Python is not particularly known for being fast, but for the sake of showing the algorithm, here is an implementation of a DisjointSet class without libraries:
class DisjointSet:
class Element:
def __init__(self):
self.parent = self
self.rank = 0
def __init__(self):
self.elements = {}
def find(self, key):
el = self.elements.get(key, None)
if not el:
el = self.Element()
self.elements[key] = el
else: # Path splitting algorithm
while el.parent != el:
el, el.parent = el.parent, el.parent.parent
return el
def union(self, key=None, *otherkeys):
if key is not None:
root = self.find(key)
for otherkey in otherkeys:
el = self.find(otherkey)
if el != root:
# Union by rank
if root.rank < el.rank:
root, el = el, root
el.parent = root
if root.rank == el.rank:
root.rank += 1
def groups(self):
result = { el: [] for el in self.elements.values()
if el.parent == el }
for key in self.elements:
result[self.find(key)].append(key)
return result
Here is how you could use it for this particular problem:
def solve(lists):
disjoint = DisjointSet()
for lst in lists:
disjoint.union(*lst)
groups = disjoint.groups()
return [lst and groups[disjoint.find(lst[0])] for lst in lists]
Example call:
data = [
[0, 5, 101],
[8, 9, 19, 21],
[],
[78, 79],
[5, 7, 63, 64]
]
result = solve(data)
The result will be:
[[0, 5, 101, 7, 63, 64], [8, 9, 19, 21], [], [78, 79], [0, 5, 101, 7, 63, 64]]
Note that I added an empty list in the input list, so to illustrate that this boundary case remains unaltered.
NB: There are libraries out there that provide union-find/disjoint set functionality, each with a slightly different API, but I suppose that using one of those can give a better performance.
Do you mean by?:
from itertools import combinations
l1 = [0, 5, 7, 63, 64, 101]
l2 = [8, 9, 19]
l3 = [78, 79]
l4 = [5, 4, 34]
print([v for x, y in combinations([l1, l2, l3, l4], 2) for v in {*x} & {*y}])
Output:
[5]
I'm stuck on iterating several nested lists in order to calculate Call options by using a Python module, Mibian.
If I use mibian to calculate made up European call options.
import mibian as mb
mb.BS([stock price, strike price, interest rate, days to maturity], volatility)
my_list = [[20, 25, 30, 35, 40, 45],
[50, 52, 54, 56, 58, 60, 77, 98, 101],
[30, 40, 50, 60]]
For calculating multiple call options, first, I create a range.
If I select, say the first nested list, my_list[0], and run a for-loop. I get all the call options for the stock.
range_list = list(range(len(my_list)))
range_list
# [0, 1, 2]
data = dict()
for x in range_list:
data[x] = option2 = []
for i in my_list[0]:
c = mb.BS([120, i, 1, 20 ], 10)
option2.append(c.callPrice)
option2
This gives the 6 call prices of the first nested list from my_list.
Output:
[100.01095590221843,
95.013694877773034,
90.016433853327641,
85.019172828882233,
80.021911804436854,
75.024650779991447]
What I'm trying to figure out, is how I can iterate all the nested lists in one go, and get a new list of nested lists that contain the call option prices for my_list[0], my_list[1], and my_list[2].
I'd like this output in one go for all three nested lists.
Output:
[[100.01095590221843, [70.027389755546068, [90.016433853327641,
95.013694877773034, 68.028485345767905, 80.021911804436854,
90.016433853327641, 66.029580935989742, 80.021911804436854,
85.019172828882233, 64.030676526211579, 70.027389755546068,
80.021911804436854, 62.03177211643343, ]]
75.024650779991447] 60.032867706655267,
43.042180223540925,
22.05368392087027,
19.055327306203068]
Can anyone help? I'm sure it's something very simple that I'm missing.
Many thanks.
P.S. I can't get the indentation right when editing my code on here.
Let's start with your current approach:
range_list = list(range(len(my_list)))
data = dict()
for x in range_list:
data[x] = option2 = []
for i in my_list[0]:
c = mb.BS([120, i, 1, 20 ], 10)
option2.append(c.callPrice)
The first thing you should note is that there is enumerate to get the index and the part at the same time, so you can omit the range_list variable:
data = dict()
for x, sublist in enumerate(my_list):
data[x] = option2 = []
for i in my_list[0]:
c = mb.BS([120, i, 1, 20 ], 10)
option2.append(c.callPrice)
This also takes care of the problem with the "dynamic indexing" because you can just iterate over the sublist:
data = dict()
for x, sublist in enumerate(my_list):
data[x] = option2 = []
for i in sublist:
c = mb.BS([120, i, 1, 20 ], 10)
option2.append(c.callPrice)
Then you can use a list comprehension to replace the inner loop:
data = dict()
for x, sublist in enumerate(my_list):
data[x] = [mb.BS([120, i, 1, 20 ], 10).callPrice for i in sublist]
and if you feel like you want this shorter (not recommended but some like it) then use a dict comprehension instead of the outer loop:
data = {x: [mb.BS([120, i, 1, 20 ], 10).callPrice for i in sublist]
for x, sublist in enumerate(my_list)}
provided that
my_nested_list = [[1,2,3], [4,5,6,7], [8,9]]
[i for i in my_nested_list]
returns
[[1, 2, 3], [4, 5, 6, 7], [8, 9]]
something along
my_list = [[20, 25, 30, 35, 40, 45], [50, 52, 54, 56, 58, 60, 77, 98, 101],
[30, 40, 50, 60]]
[mb.BS([120, i, 1, 20 ], 10) for i in my_list]
shall return what you expect?