import numpy as np import matplotlib.pyplot as plt from thunderfish.tracker_v2 import include_progress_bar from IPython import embed import scipy.stats as scp from tqdm import tqdm def estimate_error(a_error, f_error, t_error, a_error_distribution, f_error_distribution, min_f_weight=0.4, max_f_weight=0.9, t_of_max_f_weight=5.): """ Cost function estimating the error between two fish signals at two different times using realative frequency difference and relative signal amplitude difference on n electrodes (relative because the values are compared to a given distribution of these values). With increasing time difference between the signals the impact of frequency error increases and the influence of amplitude error decreases due to potential changes caused by fish movement. Parameters ---------- a_error: float MSE of amplitude difference of two electric signals recorded with n electodes. f_error: float absolute frequency difference between two electric signals. t_error: float temporal difference between two measured signals in s. a_error_distribution: list distribution of possible MSE of the amplitudes between random data points in the dataset. f_error_distribution: list distribution of possible frequency differences between random data points in the dataset. min_f_weight: float minimum proportion of the frequency impact to the error value. max_f_weight: float maximum proportion of the frequency impact to the error value. t_of_max_f_weight: float error value between two electric signals at two time points. Returns ------- float error value between two electric signals at two time points """ def boltzmann(t, alpha= 0.25, beta = 0.0, x0 = 4, dx = 0.85): """ Calulates a boltzmann function. Parameters ---------- t: array time vector. alpha: float max value of the boltzmann function. beta: float min value of the boltzmann function. x0: float time where the turning point of the boltzmann function occurs. dx: float slope of the boltzman function. Returns ------- array boltzmann function of the given time array base on the other parameters given. """ boltz = (alpha - beta) / (1. + np.exp(- (t - x0 ) / dx) ) + beta return boltz a_error_distribution = np.array(a_error_distribution) f_error_distribution = np.array(f_error_distribution) if t_error >= 2.: f_weight = max_f_weight else: f_weight = 1. * (max_f_weight - min_f_weight) / t_of_max_f_weight * t_error + min_f_weight a_weight = 1. - f_weight a_e = a_weight * len(a_error_distribution[a_error_distribution < a_error]) / len(a_error_distribution) f_e = f_weight * len(f_error_distribution[f_error_distribution < f_error]) / len(f_error_distribution) t_e = boltzmann(t_error) # t_e = 0.5 * (1. * t_error / max_t_error) ** (1. / 3) # when weight is 0.1 I end up in an endless loop somewhere return a_e + f_e + t_e def freq_tracking_v3(fund_v, idx_v, sign_v, times, freq_tolerance, n_channels, return_tmp_idenities=False, ioi_fti=False, a_error_distribution=False, f_error_distribution=False, freq_lims=(400, 1200)): """ Sorting algorithm which sorts fundamental EOD frequnecies detected in consecutive powespectra of single or multielectrode recordings using frequency difference and frequnency-power amplitude difference on the electodes. Signal tracking and identity assiginment is accomplished in four steps: 1) Extracting possible frequency and amplitude difference distributions. 2) Esitmate relative error between possible datapoint connections (relative amplitude and frequency error based on frequency and amplitude error distribution). 3) For a data window covering the EOD frequencies detected 10 seconds before the accual datapoint to assigne identify temporal identities based on overall error between two datapoints from smalles to largest. 4) Form tight connections between datapoints where one datapoint is in the timestep that is currently of interest. Repeat these steps until the end of the recording. The temporal identities are only updated when the timestep of current interest reaches the middle (5 sec.) of the temporal identities. This is because no tight connection shall be made without checking the temporal identities. The temnporal identities are used to check if the potential connection from the timestep of interest to a certain datsapoint is the possibly best or if a connection in the futur will be better. If a future connection is better the thight connection is not made. Parameters ---------- fundamentals: 2d-arraylike / list list of arrays of fundemantal EOD frequnecies. For each timestep/powerspectrum contains one list with the respectivly detected fundamental EOD frequnecies. signatures: 3d-arraylike / list same as fundamentals but for each value in fundamentals contains a list of powers of the respective frequency detected of n electrodes used. times: array respective time vector. freq_tolerance: float maximum frequency difference between two datapoints to be connected in Hz. n_channels: int number of channels/electodes used in the analysis., return_tmp_idenities: bool only returne temporal identities at a certain timestep. Dependent on ioi_fti and only used to check algorithm. ioi_fti: int Index Of Interest For Temporal Identities: respective index in fund_v to calculate the temporal identities for. a_error_distribution: array possible amplitude error distributions for the dataset. f_error_distribution: array possible frequency error distribution for the dataset. fig: mpl.figure figure to plot the tracking progress life. ax: mpl.axis axis to plot the tracking progress life. freq_lims: double minimum/maximum frequency to be tracked. Returns ------- fund_v: array flattened fundamtantals array containing all detected EOD frequencies in the recording. ident_v: array respective assigned identites throughout the tracking progress. idx_v: array respective index vectro impliing the time of the detected frequency. sign_v: 2d-array for each fundamental frequency the power of this frequency on the used electodes. a_error_distribution: array possible amplitude error distributions for the dataset. f_error_distribution: array possible frequency error distribution for the dataset. idx_of_origin_v: array for each assigned identity the index of the datapoint on which basis the assignement was made. """ def clean_up(fund_v, ident_v, idx_v, times): """ deletes/replaces with np.nan those identities only consisting from little data points and thus are tracking artefacts. Identities get deleted when the proportion of the trace (slope, ratio of detected datapoints, etc.) does not fit a real fish. Parameters ---------- fund_v: array flattened fundamtantals array containing all detected EOD frequencies in the recording. ident_v: array respective assigned identites throughout the tracking progress. idx_v: array respective index vectro impliing the time of the detected frequency. times: array respective time vector. Returns ------- ident_v: array cleaned up identities vector. """ # print('clean up') for ident in np.unique(ident_v[~np.isnan(ident_v)]): if np.median(np.abs(np.diff(fund_v[ident_v == ident]))) >= 0.25: ident_v[ident_v == ident] = np.nan continue if len(ident_v[ident_v == ident]) <= 10: ident_v[ident_v == ident] = np.nan continue return ident_v def get_a_and_f_error_dist(fund_v, idx_v, sign_v, ): """ get the distribution of possible frequency and amplitude errors for the tracking. Parameters ---------- fund_v: array flattened fundamtantals array containing all detected EOD frequencies in the recording. idx_v: array respective index vectro impliing the time of the detected frequency. sign_v: 2d-array for each fundamental frequency the power of this frequency on the used electodes. Returns ------- f_error_distribution: array possible frequency error distribution for the dataset. a_error_distribution: array possible amplitude error distributions for the dataset. """ # get f and amp signature distribution ############### BOOT ####################### a_error_distribution = np.zeros(20000) # distribution of amplitude errors f_error_distribution = np.zeros(20000) # distribution of frequency errors idx_of_distribution = np.zeros(20000) # corresponding indices b = 0 # loop varialble next_message = 0. # feedback while b < 20000: next_message = include_progress_bar(b, 20000, 'get f and sign dist', next_message) # feedback while True: # finding compare indices to create initial amp and freq distribution # r_idx0 = np.random.randint(np.max(idx_v[~np.isnan(idx_v)])) r_idx0 = np.random.randint(np.max(idx_v[~np.isnan(idx_v)])) r_idx1 = r_idx0 + 1 if len(sign_v[idx_v == r_idx0]) != 0 and len(sign_v[idx_v == r_idx1]) != 0: break r_idx00 = np.random.randint(len(sign_v[idx_v == r_idx0])) r_idx11 = np.random.randint(len(sign_v[idx_v == r_idx1])) s0 = sign_v[idx_v == r_idx0][r_idx00] # amplitude signatures s1 = sign_v[idx_v == r_idx1][r_idx11] f0 = fund_v[idx_v == r_idx0][r_idx00] # fundamentals f1 = fund_v[idx_v == r_idx1][r_idx11] # if np.abs(f0 - f1) > freq_tolerance: # frequency threshold if np.abs(f0 - f1) > 10.: # frequency threshold continue idx_of_distribution[b] = r_idx0 a_error_distribution[b] = np.sqrt(np.sum([(s0[k] - s1[k]) ** 2 for k in range(len(s0))])) f_error_distribution[b] = np.abs(f0 - f1) b += 1 return f_error_distribution, a_error_distribution def get_tmp_identities(i0_m, i1_m, error_cube, fund_v, idx_v, i, ioi_fti, dps, idx_comp_range): """ extract temporal identities for a datasnippted of 2*index compare range of the original tracking algorithm. for each data point in the data window finds the best connection within index compare range and, thus connects the datapoints based on their minimal error value until no connections are left or possible anymore. Parameters ---------- i0_m: 2d-array for consecutive timestamps contains for each the indices of the origin EOD frequencies. i1_m: 2d-array respectively contains the indices of the targen EOD frequencies, laying within index compare range. error_cube: 3d-array error values for each combination from i0_m and the respective indices in i1_m. fund_v: array flattened fundamtantals array containing all detected EOD frequencies in the recording. idx_v: array respective index vectro impliing the time of the detected frequency. i: int loop variable and current index of interest for the assignment of tight connections. ioi_fti: int index of interest for temporal identities. dps: float detections per second. 1. / 'temporal resolution of the tracking' idx_comp_range: int index compare range for the assignment of two data points to each other. Returns ------- tmp_ident_v: array for each EOD frequencies within the index compare range for the current time step of interest contains the temporal identity. errors_to_v: array for each assigned temporal identity contains the error value based on which this connection was made. """ next_tmp_identity = 0 # mask_cube = [np.ones(np.shape(error_cube[n]), dtype=bool) for n in range(len(error_cube))] max_shape = np.max([np.shape(layer) for layer in error_cube[1:]], axis=0) cp_error_cube = np.full((len(error_cube)-1, max_shape[0], max_shape[1]), np.nan) for enu, layer in enumerate(error_cube[1:]): cp_error_cube[enu, :np.shape(error_cube[enu+1])[0], :np.shape(error_cube[enu+1])[1]] = layer try: tmp_ident_v = np.full(len(fund_v), np.nan) errors_to_v = np.full(len(fund_v), np.nan) except: tmp_ident_v = np.zeros(len(fund_v)) / 0. errors_to_v = np.zeros(len(fund_v)) / 0. layers, idx0s, idx1s = np.unravel_index(np.argsort(cp_error_cube, axis=None), np.shape(cp_error_cube)) layers = layers+1 # embed() # quit() for layer, idx0, idx1 in zip(layers, idx0s, idx1s): # embed() # quit() if np.isnan(cp_error_cube[layer-1, idx0, idx1]): break # _____ some control functions _____ ### if not ioi_fti: if idx_v[i1_m[layer][idx1]] - i > idx_comp_range*2: continue else: if idx_v[i1_m[layer][idx1]] - idx_v[ioi_fti] > idx_comp_range*2: continue if fund_v[i0_m[layer][idx0]] > fund_v[i1_m[layer][idx1]]: if 1. * np.abs(fund_v[i0_m[layer][idx0]] - fund_v[i1_m[layer][idx1]]) / ((idx_v[i1_m[layer][idx1]] - idx_v[i0_m[layer][idx0]]) / dps) > 2.: continue else: if 1. * np.abs(fund_v[i0_m[layer][idx0]] - fund_v[i1_m[layer][idx1]]) / ((idx_v[i1_m[layer][idx1]] - idx_v[i0_m[layer][idx0]]) / dps) > 2.: continue if np.isnan(tmp_ident_v[i0_m[layer][idx0]]): if np.isnan(tmp_ident_v[i1_m[layer][idx1]]): tmp_ident_v[i0_m[layer][idx0]] = next_tmp_identity tmp_ident_v[i1_m[layer][idx1]] = next_tmp_identity errors_to_v[i1_m[layer][idx1]] = cp_error_cube[layer-1][idx0, idx1] # errors_to_v[i0_m[layer][idx0]] = error_cube[layer][idx0, idx1] # errors_to_v[(tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]) & (np.isnan(errors_to_v))] = error_cube[layer][idx0, idx1] next_tmp_identity += 1 else: if idx_v[i0_m[layer][idx0]] in idx_v[tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]]: continue tmp_ident_v[i0_m[layer][idx0]] = tmp_ident_v[i1_m[layer][idx1]] errors_to_v[i1_m[layer][idx1]] = cp_error_cube[layer-1][idx0, idx1] # errors_to_v[(tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]) & (np.isnan(errors_to_v))] = error_cube[layer][idx0, idx1] # errors_to_v[tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]][0] = np.nan else: if np.isnan(tmp_ident_v[i1_m[layer][idx1]]): if idx_v[i1_m[layer][idx1]] in idx_v[tmp_ident_v == tmp_ident_v[i0_m[layer][idx0]]]: continue tmp_ident_v[i1_m[layer][idx1]] = tmp_ident_v[i0_m[layer][idx0]] errors_to_v[i1_m[layer][idx1]] = cp_error_cube[layer-1][idx0, idx1] # errors_to_v[(tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]) & (np.isnan(errors_to_v))] = error_cube[layer][idx0, idx1] # errors_to_v[tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]][0] = np.nan else: if tmp_ident_v[i0_m[layer][idx0]] == tmp_ident_v[i1_m[layer][idx1]]: if np.isnan(errors_to_v[i1_m[layer][idx1]]): errors_to_v[i1_m[layer][idx1]] = cp_error_cube[layer-1][idx0, idx1] continue idxs_i0 = idx_v[tmp_ident_v == tmp_ident_v[i0_m[layer][idx0]]] idxs_i1 = idx_v[tmp_ident_v == tmp_ident_v[i1_m[layer][idx1]]] if np.any(np.diff(sorted(np.concatenate((idxs_i0, idxs_i1)))) == 0): continue tmp_ident_v[tmp_ident_v == tmp_ident_v[i0_m[layer][idx0]]] = tmp_ident_v[i1_m[layer][idx1]] if np.isnan(errors_to_v[i1_m[layer][idx1]]): errors_to_v[i1_m[layer][idx1]] = cp_error_cube[layer-1][idx0, idx1] return tmp_ident_v, errors_to_v # _____ parameters and vectors _____ ### detection_time_diff = times[1] - times[0] dps = 1. / detection_time_diff try: ident_v = np.full(len(fund_v), np.nan) # fish identities of frequencies idx_of_origin_v = np.full(len(fund_v), np.nan) except: ident_v = np.zeros(len(fund_v)) / 0. # fish identities of frequencies idx_of_origin_v = np.zeros(len(fund_v)) / 0. idx_comp_range = int(np.floor(dps * 5.)) # maximum compare range backwards for amplitude signature comparison # idx_comp_range = int(np.floor(dps * 15.)) # maximum compare range backwards for amplitude signature comparison low_freq_th = 400. # min. frequency tracked high_freq_th = 1050. # max. frequency tracked # _____ get amp and freq error distribution # if hasattr(a_error_distribution, '__len__') and hasattr(f_error_distribution, '__len__'): # pass # else: # f_error_distribution, a_error_distribution = get_a_and_f_error_dist(fund_v, idx_v, sign_v) # _____ create initial error cube _____ ### error_cube = [] # [fundamental_list_idx, freqs_to_assign, target_freqs] i0_m = [] i1_m = [] next_message = 0. start_idx = idx_v[0] f_error_distribution = [] a_error_distribution = [] idx_of_error = [] fig1, ax1 = plt.subplots() ax1.plot(times[idx_v], fund_v, 'o', color='grey', alpha=.1) tmp_ident_handle = [] ident_handles = [] plt.show(block=False) # # start_idx = 0 if not ioi_fti else idx_v[ioi_fti] # Index Of Interest for temporal identities # distributions NEW for i in range(start_idx, int(start_idx + idx_comp_range*2)): next_message = include_progress_bar(i - start_idx, int(idx_comp_range*2), 'error dist init', next_message) i0_v = np.arange(len(idx_v))[(idx_v == i) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of fundamtenals to assign i1_v = np.arange(len(idx_v))[(idx_v > i) & (idx_v <= (i + int(idx_comp_range))) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of possible targets # i0_m.append(i0_v) # i1_m.append(i1_v) if len(i0_v) == 0 or len(i1_v) == 0: # if nothing to assign or no targets continue continue for enu0 in range(len(fund_v[i0_v])): if fund_v[i0_v[enu0]] < low_freq_th or fund_v[i0_v[enu0]] > high_freq_th: continue for enu1 in range(len(fund_v[i1_v])): if fund_v[i1_v[enu1]] < low_freq_th or fund_v[i1_v[enu1]] > high_freq_th: continue if np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]]) >= freq_tolerance: # freq difference to high continue a_error_distribution.append(np.sqrt(np.sum([(sign_v[i0_v[enu0]][k] - sign_v[i1_v[enu1]][k]) ** 2 for k in range(len(sign_v[i0_v[enu0]]))]))) f_error_distribution.append(np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]])) idx_of_error.append(idx_v[i1_v[enu1]]) # Initial error cube for i in range(start_idx, int(start_idx + idx_comp_range*2)): next_message = include_progress_bar(i - start_idx, int(idx_comp_range*2), 'initial error cube', next_message) i0_v = np.arange(len(idx_v))[(idx_v == i) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of fundamtenals to assign i1_v = np.arange(len(idx_v))[(idx_v > i) & (idx_v <= (i + int(idx_comp_range))) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of possible targets i0_m.append(i0_v) i1_m.append(i1_v) if len(i0_v) == 0 or len(i1_v) == 0: # if nothing to assign or no targets continue error_cube.append(np.array([[]])) continue try: error_matrix = np.full((len(i0_v), len(i1_v)), np.nan) except: error_matrix = np.zeros((len(i0_v), len(i1_v))) / 0. for enu0 in range(len(fund_v[i0_v])): if fund_v[i0_v[enu0]] < low_freq_th or fund_v[i0_v[enu0]] > high_freq_th: # freq to assigne out of tracking range continue for enu1 in range(len(fund_v[i1_v])): if fund_v[i1_v[enu1]] < low_freq_th or fund_v[i1_v[enu1]] > high_freq_th: # target freq out of tracking range continue if np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]]) >= freq_tolerance: # freq difference to high continue a_error = np.sqrt(np.sum([(sign_v[i0_v[enu0]][j] - sign_v[i1_v[enu1]][j]) ** 2 for j in range(n_channels)])) f_error = np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]]) t_error = 1. * np.abs(idx_v[i0_v[enu0]] - idx_v[i1_v[enu1]]) / dps error_matrix[enu0, enu1] = estimate_error(a_error, f_error, t_error, a_error_distribution, f_error_distribution) error_cube.append(error_matrix) cube_app_idx = idx_v[0] + len(error_cube) # _____ accual tracking _____ ### next_identity = 0 next_message = 0.00 # for enu, i in enumerate(np.arange(len(fundamentals))): for enu, i in enumerate(np.unique(idx_v)): if enu == 0: fig, ax = plt.subplots(1, 2) n, h = np.histogram(f_error_distribution, 1000) # a0, = ax[0].plot(h[1:], np.cumsum(n) / np.sum(n), color='cornflowerblue', linewidth=2) ax[0].plot(h[:-1], np.cumsum(n) / np.sum(n), color='k', linewidth=2) n, h = np.histogram(a_error_distribution, 1000) # a1, = ax[1].plot(h[1:], np.cumsum(n) / np.sum(n), color='cornflowerblue', linewidth=2) ax[1].plot(h[:-1], np.cumsum(n) / np.sum(n), color='k', linewidth=2) plt.show(block=False) else: if len(f_error_distribution) >= 5000: f_error_distribution = f_error_distribution[-4000:] a_error_distribution = a_error_distribution[-4000:] idx_of_error = idx_of_error[-4000:] n, h = np.histogram(f_error_distribution, 1000) # a0.set_data(h[1:], np.cumsum(n) / np.sum(n)) c = np.random.rand(3) ax[0].plot(h[:-1], np.cumsum(n) / np.sum(n), color=c) n, h = np.histogram(a_error_distribution, 1000) ax[1].plot(h[:-1], np.cumsum(n) / np.sum(n), color=c) # a1.set_data(h[1:], np.cumsum(n) / np.sum(n)) fig.canvas.draw() # print(i, idx_v[-1]) if i != 0 and (i % int(idx_comp_range * 120)) == 0: # clean up every 10 minutes ident_v = clean_up(fund_v, ident_v, idx_v, times) if not return_tmp_idenities: next_message = include_progress_bar(enu, len(np.unique(idx_v)), 'tracking', next_message) # feedback if enu % idx_comp_range == 0: # t0 = time.time() tmp_ident_v, errors_to_v = get_tmp_identities(i0_m, i1_m, error_cube, fund_v, idx_v, i, ioi_fti, dps, idx_comp_range) tmp_ident_regress = {} for ident in np.unique(tmp_ident_v[~np.isnan(tmp_ident_v)]): slope, intercept, _, _, _ = scp.linregress(idx_v[tmp_ident_v == ident], fund_v[tmp_ident_v == ident]) tmp_ident_regress.update({'%.0f' %ident: [slope, intercept]}) # embed() # quit() # for hand in tmp_ident_handle: # hand.remove() # tmp_ident_handle = [] # for ident in np.unique(tmp_ident_v[~np.isnan(tmp_ident_v)]): # handle, = ax1.plot(times[idx_v][tmp_ident_v == ident], fund_v[tmp_ident_v == ident], color='red') # tmp_ident_handle.append(handle) # fig1.canvas.draw() idx0s, idx1s = np.unravel_index(np.argsort(error_cube[0], axis=None), np.shape(error_cube[0])) for idx0, idx1 in zip(idx0s, idx1s): if np.isnan(error_cube[0][idx0, idx1]): break if freq_lims: if fund_v[i0_m[0][idx0]] > freq_lims[1] or fund_v[i0_m[0][idx0]] < freq_lims[0]: continue if fund_v[i1_m[0][idx1]] > freq_lims[1] or fund_v[i1_m[0][idx1]] < freq_lims[0]: continue if not np.isnan(ident_v[i1_m[0][idx1]]): continue if not np.isnan(errors_to_v[i1_m[0][idx1]]): if errors_to_v[i1_m[0][idx1]] < error_cube[0][idx0, idx1]: continue if np.isnan(ident_v[i0_m[0][idx0]]): # i0 doesnt have identity if 1. * np.abs(fund_v[i0_m[0][idx0]] - fund_v[i1_m[0][idx1]]) / ((idx_v[i1_m[0][idx1]] - idx_v[i0_m[0][idx0]]) / dps) > 2.: continue if np.isnan(ident_v[i1_m[0][idx1]]): # i1 doesnt have identity ident_v[i0_m[0][idx0]] = next_identity ident_v[i1_m[0][idx1]] = next_identity next_identity += 1 a_error_distribution.append(np.sqrt(np.sum([(sign_v[i0_m[0][idx0]][k] - sign_v[i1_m[0][idx1]][k]) ** 2 for k in range(len(sign_v[i0_m[0][idx0]]))]))) f_error_distribution.append(np.abs(fund_v[i0_m[0][idx0]] - fund_v[i1_m[0][idx1]])) idx_of_error.append(idx_v[i1_m[0][idx1]]) else: # i1 does have identity continue else: # i0 does have identity if np.isnan(ident_v[i1_m[0][idx1]]): # i1 doesnt have identity if idx_v[i1_m[0][idx1]] in idx_v[ident_v == ident_v[i0_m[0][idx0]]]: continue # _____ if either idx0-idx1 is not a direct connection or ... # _____ idx1 is not the new last point of ident[idx0] check ... if not idx_v[i0_m[0][idx0]] == idx_v[ident_v == ident_v[i0_m[0][idx0]]][-1]: # if i0 is not the last ... if len(ident_v[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v > idx_v[i0_m[0][idx0]]) & (idx_v < idx_v[i1_m[0][idx1]])]) == 0: # zwischen i0 und i1 keiner next_idx_after_new = np.arange(len(ident_v))[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v > idx_v[i1_m[0][idx1]])][0] if tmp_ident_v[next_idx_after_new] != tmp_ident_v[i1_m[0][idx1]]: continue elif len(ident_v[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v > idx_v[i1_m[0][idx1]])]) == 0: # keiner nach i1 last_idx_before_new = np.arange(len(ident_v))[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v < idx_v[i1_m[0][idx1]])][-1] if tmp_ident_v[last_idx_before_new] != tmp_ident_v[i1_m[0][idx1]]: continue else: # sowohl als auch next_idx_after_new = np.arange(len(ident_v))[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v > idx_v[i1_m[0][idx1]])][0] last_idx_before_new = np.arange(len(ident_v))[(ident_v == ident_v[i0_m[0][idx0]]) & (idx_v < idx_v[i1_m[0][idx1]])][-1] if tmp_ident_v[last_idx_before_new] != tmp_ident_v[i1_m[0][idx1]] or tmp_ident_v[next_idx_after_new] != tmp_ident_v[i1_m[0][idx1]]: continue ident_v[i1_m[0][idx1]] = ident_v[i0_m[0][idx0]] a_error_distribution.append(np.sqrt(np.sum([(sign_v[i0_m[0][idx0]][k] - sign_v[i1_m[0][idx1]][k]) ** 2 for k in range(len(sign_v[i0_m[0][idx0]]))]))) f_error_distribution.append(np.abs(fund_v[i0_m[0][idx0]] - fund_v[i1_m[0][idx1]])) idx_of_error.append(idx_v[i1_m[0][idx1]]) else: continue idx_of_origin_v[i1_m[0][idx1]] = i0_m[0][idx0] # # for hand in ident_handles: # hand.remove() # ident_handles = [] # for ident in np.unique(ident_v[~np.isnan(ident_v)]): # hand, = ax1.plot(times[idx_v][ident_v == ident], fund_v[ident_v == ident], color='green', lw=2) # ident_handles.append(hand) # # ax1.set_xlim([times[idx_v[~np.isnan(tmp_ident_v)][0]] - 10, times[idx_v[~np.isnan(tmp_ident_v)][-1]] + 10 ]) # fig1.canvas.draw() # plt.waitforbuttonpress() # sort_time += time.time()-t0 i0_m.pop(0) i1_m.pop(0) error_cube.pop(0) i0_v = np.arange(len(idx_v))[(idx_v == cube_app_idx) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of fundamtenals to assign i1_v = np.arange(len(idx_v))[(idx_v > cube_app_idx) & (idx_v <= (cube_app_idx + idx_comp_range)) & (fund_v >= freq_lims[0]) & (fund_v <= freq_lims[1])] # indices of possible targets i0_m.append(i0_v) i1_m.append(i1_v) # embed() # quit() if len(i0_v) == 0 or len(i1_v) == 0: # if nothing to assign or no targets continue error_cube.append(np.array([[]])) else: try: error_matrix = np.full((len(i0_v), len(i1_v)), np.nan) except: error_matrix = np.zeros((len(i0_v), len(i1_v))) / 0. for enu0 in range(len(fund_v[i0_v])): if fund_v[i0_v[enu0]] < low_freq_th or fund_v[i0_v[enu0]] > high_freq_th: # freq to assigne out of tracking range continue for enu1 in range(len(fund_v[i1_v])): if fund_v[i1_v[enu1]] < low_freq_th or fund_v[i1_v[enu1]] > high_freq_th: # target freq out of tracking range continue if np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]]) >= freq_tolerance: # freq difference to high continue a_error = np.sqrt( np.sum([(sign_v[i0_v[enu0]][j] - sign_v[i1_v[enu1]][j]) ** 2 for j in range(n_channels)])) if not np.isnan(tmp_ident_v[i0_v[enu0]]): a = tmp_ident_regress['%.0f' % tmp_ident_v[i0_v[enu0]]][0] b = tmp_ident_regress['%.0f' % tmp_ident_v[i0_v[enu0]]][1] f_error = np.abs( (a*idx_v[i1_v[enu1]]+b) - fund_v[i1_v[enu1]]) else: f_error = np.abs(fund_v[i0_v[enu0]] - fund_v[i1_v[enu1]]) t_error = 1. * np.abs(idx_v[i0_v[enu0]] - idx_v[i1_v[enu1]]) / dps error_matrix[enu0, enu1] = estimate_error(a_error, f_error, t_error, a_error_distribution, f_error_distribution) error_cube.append(error_matrix) cube_app_idx += 1 ident_v = clean_up(fund_v, ident_v, idx_v, times) return fund_v, ident_v, idx_v, sign_v, a_error_distribution, f_error_distribution, idx_of_origin_v if __name__ == '__main__': fund_v = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/fund_v.npy') idx_v = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/idx_v.npy') times = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/times.npy') sign_v = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/sign_v.npy') # ident_v = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/ident_v.npy') ident_v = np.load('/home/raab/paper_create/raab2018_tracking_without_tagging/bsp_data/2016-04-10-22:14/ident_v2.npy') # embed() # quit() # fund_v, ident_v, idx_v, sign_v, a_error_distribution, f_error_distribution, idx_of_origin_v = freq_tracking_v3(fund_v, idx_v, sign_v, times, freq_tolerance=40., n_channels=np.shape(sign_v)[1]) a_errors_true = [] a_errors_false = [] # 2 sek f_errors_true = [] # 5 sek f_errors_false = [] # freq tollerance = 2 for i in tqdm(range(len(fund_v))): for j in range(i, len(fund_v)): if idx_v[i] == idx_v[j] or times[idx_v[j]] - times[idx_v[i]] > .5: continue if np.abs(fund_v[i] - fund_v[j]) >2.: continue if ident_v[i] == ident_v[j]: f_errors_true.append(np.abs(fund_v[i] - fund_v[j])) a_errors_true.append(np.sqrt(np.sum([(sign_v[i][a] - sign_v[j][a]) ** 2 for a in range(len(sign_v[i]))]))) else: f_errors_false.append(np.abs(fund_v[i] - fund_v[j])) a_errors_false.append(np.sqrt(np.sum([(sign_v[i][a] - sign_v[j][a]) ** 2 for a in range(len(sign_v[i]))]))) fig, ax = plt.subplots() ax.plot(times[idx_v], fund_v, 'o', color='grey', alpha=.5) for ident in np.unique(ident_v[~np.isnan(ident_v)]): ax.plot(times[idx_v[ident_v == ident]], fund_v[ident_v == ident], color = np.random.rand(3), marker ='.') plt.show() embed() quit()