def _compute_peaks_or_valleys_of_first_derivative(s, do_peaks=True):
<EOL>gradient = np.nan_to_num(np.apply_along_axis(np.gradient, <NUM_LIT:1>, s), copy=False)<EOL>threshold = np.squeeze(np.nanmean(gradient, axis=<NUM_LIT:1>) + np.nanstd(gradient, axis=<NUM_LIT:1>))<EOL>half_window = <NUM_LIT:4><EOL>if do_peaks:<EOL><INDENT>indexes = [signal.argrelextrema(gradient[i, :], np.greater, order=half_window)[<NUM_LIT:0>] for i in range(gradient.shape[<NUM_LIT:0>])]<EOL><DEDENT>else:<EOL><INDENT>indexes = [signal.argrelextrema(gradient[i, :], np.less, order=half_window)[<NUM_LIT:0>] for i in range(gradient.shape[<NUM_LIT:0>])]<EOL><DEDENT>extrema = np.zeros(s.shape)<EOL>for row_index, index_array in enumerate(indexes):<EOL><INDENT>for col_index in index_array:<EOL><INDENT>if do_peaks and (gradient[row_index, col_index] > threshold[row_index]):<EOL><INDENT>extrema[row_index, col_index] = <NUM_LIT:1><EOL><DEDENT>elif not do_peaks:<EOL><INDENT>extrema[row_index, col_index] = <NUM_LIT:1><EOL><DEDENT><DEDENT><DEDENT>return extrema, gradient<EOL>
Takes a spectrogram and returns a 2D array of the form:

0 0 0 1 0 0 1 0 0 0 1   <-- Frequency 0
0 0 1 0 0 0 0 0 0 1 0   <-- Frequency 1
0 0 0 0 0 0 1 0 1 0 0   <-- Frequency 2
*** Time axis *******

Where a 1 means that the value in that time bin in the spectrogram corresponds to
a peak/valley in the first derivative.

This function is used as part of the ASA algorithm and is not meant to be used publicly.