Python: Unearthing Data Trends - Local Maxima and Minima in NumPy
Conceptual Approach
- Local maxima (peaks) are points where the data value is greater than both its neighbors on either side.
Implementation with NumPy
- Compare with neighbors: We can achieve this by calculating the difference between consecutive elements in the array using
np.diff(data)
. - Identify direction change: A local maximum (minimum) corresponds to a change in direction from positive (negative) difference to negative (positive) difference.
- Find extrema indices: We can use boolean indexing to find these transitions and then add 1 to account for the initial shift caused by
np.diff
.
Here's the code that implements this logic:
import numpy as np
def find_local_extrema(data):
"""
Finds local maxima and minima in a 1D NumPy array.
Args:
data: A 1D NumPy array.
Returns:
A tuple containing two lists:
- local_maxima: A list of indices of local maxima.
- local_minima: A list of indices of local minima.
"""
# Get boolean arrays for where the data is greater/less than its neighbors
is_greater_than_neighbors = np.diff(data) > 0
is_less_than_neighbors = np.diff(data) < 0
# Find the indices where the direction changes (i.e., local extrema)
local_maxima = np.where((is_greater_than_neighbors[:-1] & ~is_greater_than_neighbors[1:]) |
(is_less_than_neighbors[:-1] & ~is_less_than_neighbors[1:]))[0] + 1
local_minima = np.where((~is_greater_than_neighbors[:-1] & is_greater_than_neighbors[1:]) |
(~is_less_than_neighbors[:-1] & is_less_than_neighbors[1:]))[0] + 1
return local_maxima, local_minima
# Sample data
data = np.array([2, 3, 5, 7, 4, 2, 1, 6])
# Find local maxima and minima
local_maxima, local_minima = find_local_extrema(data)
# Print the results
print("Local maxima:", data[local_maxima])
print("Local minima:", data[local_minima])
This code effectively finds local maxima and minima in the data array.
Imports:
import numpy as np
This line imports the NumPy library and assigns it the alias np
. NumPy is essential for working with arrays in Python.
Function Definition:
def find_local_extrema(data):
"""
Finds local maxima and minima in a 1D NumPy array.
Args:
data: A 1D NumPy array.
Returns:
A tuple containing two lists:
- local_maxima: A list of indices of local maxima.
- local_minima: A list of indices of local minima.
"""
This defines a function called find_local_extrema
that takes a 1D NumPy array (data
) as input and returns two lists: local_maxima
and local_minima
containing the indices of local maxima and minima respectively.
Finding Differences:
# Get boolean arrays for where the data is greater/less than its neighbors
is_greater_than_neighbors = np.diff(data) > 0
is_less_than_neighbors = np.diff(data) < 0
np.diff(data)
calculates the difference between consecutive elements in thedata
array.- The results are stored in two boolean arrays:
is_greater_than_neighbors
: True for elements greater than their neighbors.
Identifying Direction Changes:
# Find the indices where the direction changes (i.e., local extrema)
local_maxima = np.where((is_greater_than_neighbors[:-1] & ~is_greater_than_neighbors[1:]) |
(is_less_than_neighbors[:-1] & ~is_less_than_neighbors[1:]))[0] + 1
local_minima = np.where((~is_greater_than_neighbors[:-1] & is_greater_than_neighbors[1:]) |
(~is_less_than_neighbors[:-1] & is_less_than_neighbors[1:]))[0] + 1
This part is the core logic for finding extrema:
[:-1]
and[1:]
slice the boolean arrays, excluding the last and first element respectively (to avoid out-of-bounds errors).&
(bitwise AND) and~
(bitwise NOT) are used to find positions where the direction changes.- For maxima: A transition from positive difference to negative difference.
np.where
finds the indices where these conditions are True.+ 1
accounts for the initial shift caused bynp.diff
.
Returning Results:
return local_maxima, local_minima
The function returns a tuple containing two lists: local_maxima
and local_minima
.
Sample Data and Usage:
# Sample data
data = np.array([2, 3, 5, 7, 4, 2, 1, 6])
# Find local maxima and minima
local_maxima, local_minima = find_local_extrema(data)
# Print the results
print("Local maxima:", data[local_maxima])
print("Local minima:", data[local_minima])
This section creates a sample array (data
) and calls the find_local_extrema
function to find local maxima and minima. Finally, it prints the values at the identified extrema indices.
Using derivative sign changes:
This approach leverages the fact that local extrema occur at points where the derivative changes sign.
import numpy as np
def find_extrema_with_derivative(data):
"""
Finds local maxima and minima using derivative sign changes.
Args:
data: A 1D NumPy array.
Returns:
A tuple containing two lists:
- local_maxima: A list of indices of local maxima.
- local_minima: A list of indices of local minima.
"""
# Calculate the derivative (difference)
derivative = np.diff(data)
# Find sign changes in the derivative
sign_changes = np.diff(np.sign(derivative)) # sign(x) returns +1 for positive, -1 for negative
# Identify local maxima and minima based on sign changes
local_maxima = np.where(sign_changes > 0)[0] + 1 # +1 for initial shift
local_minima = np.where(sign_changes < 0)[0] + 1
return local_maxima, local_minima
This method calculates the derivative using np.diff
and then finds the sign changes in the derivative with np.diff(np.sign(derivative))
. Positive sign changes indicate local minima, and negative sign changes indicate local maxima. Finally, it finds the indices of these sign changes and adjusts for the initial shift.
Using SciPy's find_peaks function (if available):
If you have the SciPy library installed, you can leverage its find_peaks
function specifically designed for this purpose.
from scipy.signal import find_peaks
def find_extrema_with_scipy(data, prominence=None):
"""
Finds local maxima and minima using SciPy's find_peaks function.
Args:
data: A 1D NumPy array.
prominence (optional): Minimum prominence of peaks (defaults to None).
Returns:
A tuple containing two lists:
- peaks: A list of indices of both maxima and minima.
- properties: A dictionary containing peak properties (if prominence is provided).
"""
# Find all peaks (including both maxima and minima)
peaks, _ = find_peaks(data, prominence=prominence)
# You can further classify peaks as maxima/minima based on data values
return peaks, None # You can return properties from _ if needed
# Example usage with prominence (optional)
peaks, _ = find_extrema_with_scipy(data, prominence=2)
This method uses find_peaks
from SciPy to find all peaks (both maxima and minima) based on a user-defined prominence threshold (optional). You can then analyze the data values at the peak indices to differentiate between maxima and minima.
Remember to install SciPy (pip install scipy
) if you want to use the second method.
python numpy