def exclude_zigZag(self, vlimit, angle=None, returnBlocks=None): ''' CAUSED: by tracking errors Reasoning why it must be an error: -Fish can have at certain speeds a maximal turning ability. -Of course, a startle can exceed this maneuvrability but only for 1 frame -For 2 frames followed it is not realistic IDENTIFIED: (i) the direction changes stronger as an threshold angle twice (ii) all direction changes happen velocities faster than the limiting speed TODO: actually it is not checked if the second direction change goes in the opposite direction as the first ''' if returnBlocks is None: returnBlocks = False if angle is None: angle = np.pi / 2 vel = np.diff(self.dat, axis=0) v = np.sqrt(np.sum(vel**2, axis=2)) # check case (i) case1 = np.array([ gen.angle_between(vel[:-1, i], vel[1:, i]) for i in range(self.N) ]).T case1 = (case1[:-1] > angle) & (case1[1:] > angle) # check case (ii) case2 = (v[:-2] > vlimit) & (v[1:-1] > vlimit) & (v[2:] > vlimit) zigZag = case1 & case2 block = gen.find_blocks_large(zigZag.astype(int), 0.5, 1, 1) if len(block) > 0: # block refers to acceleration: positions 2 frames later needs inclusion block[:, 1] += 3 self.exclude_blocks(block) if returnBlocks: return block
def exclude_jumps2(self, returnBlocks=None): ''' CAUSED: by missing frames or id-switch IDENTIFIED: (i) by an positive acceleration directly followed by a similar negative acceleration -> acc = [80, -80] (ii) the acceleration should have doubled the speed ''' if returnBlocks is None: returnBlocks = False vel = np.diff(self.dat, axis=0) v = np.sqrt(np.sum(vel**2, axis=2)) acc = np.diff(v, axis=0) case1 = -acc[:-1] / acc[1:] case1 = (0.8 < case1) & (case1 < 1.2) & (acc[:-1] > 0) # check case (ii) case2 = (acc[:-1] / v[:-2]) > 2 jump = case1 & case2 block = gen.find_blocks_large(jump.astype(int), 0.5, 1, 1) if len(block) > 0: # block refers to acceleration: positions 2 frames later needs inclusion block[:, 1] += 3 self.exclude_blocks(block) if returnBlocks: return block
def get_update_rate(acc_s): ''' returns the update events, length and the "update" (marked by 1st. positive acceleration) estimated in window of size "win" INPUT: acc_s.shape(time, N) change of speed NOT of velocity-vector OR CLASS with attr. acc_s win float OUTPUT: update.shape(time, N) ''' if hasattr(acc_s, 'acc_s'): acc_s = acc_s.acc_s assert len(acc_s.shape) == 2, 'len(acc_s.shape) != 2' time, N = acc_s.shape update = np.zeros((time, N), dtype='float') upLen = [] upAcc = [] for i in range(N): blocks = gen.find_blocks_large(acc_s[:, i], 0, 1, 1) if len(blocks) > 0: update[blocks[:, 0], i] = 1 # = update time series temp = list(blocks[:, 1] + 1 - blocks[:, 0]) upLen.append(temp) temp = [acc_s[b[0]:b[1] + 1].mean() for b in blocks] upAcc.append(temp) return update, upLen, upAcc
def get_burst_behavior(burstPlus_coastMinus, acc_s, acc_v, acc_phi, s, d_phi): ''' INPUT: burstPlus_coastMinus shape=(time, N) timeseries which marks a burst with +1 and coast with -1 acc_s shape=(time, N) acceleration s shape=(time, N) speed OUTPUT: coast_acc list accelerations at coasting coast_s list speed at coasting ''' assert len(acc_s.shape) == 2, 'len(acc_s.shape) != 2' time, N = acc_s.shape burst_acc_s = [] burst_acc_v = [] burst_acc_phi = [] burst_s = [] burst_d_phi = [] for i in range(N): thereLarge = gen.find_blocks_large(burstPlus_coastMinus[:, i], 0, 1, 1, noBlocks=True) if len(thereLarge) > 0: burst_acc_s.append(list(acc_s[thereLarge, i])) burst_acc_v.append(list(acc_v[thereLarge, i])) burst_acc_phi.append(list(acc_phi[thereLarge, i])) burst_s.append(list(s[thereLarge, i])) burst_d_phi.append(list(d_phi[thereLarge, i])) return burst_acc_s, burst_acc_v, burst_acc_phi, burst_s, burst_d_phi
def get_burst_blockDetails(burstPlus_coastMinus): ''' in contrast to get_burst_behavior it just returns the start-, end-time and the ID of the bursting agent This allows in combination with the other measures to estimate the social forces INPUT: burstPlus_coastMinus shape=(time, N) timeseries which marks a burst with +1 and coast with -1 OUTPUT: burstBlockDetails list [[t_start, t_end, IDburst], ...] list of block-detail-lists where the latter defines start and end time and ID of burster ''' assert len( burstPlus_coastMinus.shape) == 2, 'Invalid Shape: len(shape) != 2' time, N = burstPlus_coastMinus.shape burstStart = [] burstEnd = [] bursterID = [] for i in range(N): blocksLarge = gen.find_blocks_large(burstPlus_coastMinus[:, i], 0, 1, 1, noBlocks=False) if len(blocksLarge) > 0: blocksLarge = np.array(blocksLarge).T burstStart += list(blocksLarge[0]) burstEnd += list(blocksLarge[1]) bursterID += len(blocksLarge[0]) * [i] return burstStart, burstEnd, bursterID
def exclude_jumps(self, speed_max, speed_mean, returnBlocks=None): ''' CAUSED: by missing frames or id-switch IDENTIFIED: by an extreme positive acceleration directly followed by extreme negative acceleration -> acc = [80, -80] extreme acceleration= speed_max - speed_mean ''' if returnBlocks is None: returnBlocks = False vel = np.diff(self.dat, axis=0) v = np.sqrt(np.sum(vel**2, axis=2)) acc = np.diff(v, axis=0) jump = -1 * np.diff(acc, axis=0) crazy_jump = (speed_max - speed_mean) * 2 block = gen.find_blocks_large(jump, crazy_jump, 1, 1) if len(block) > 0: # block refers to acceleration: positions 2 frames later needs inclusion block[:, 1] += 2 self.exclude_blocks(block) if returnBlocks: return block
def make_v_block(self): ''' computes from excluded blocks the valid blocks: e.g.: ex_block = [[0, 10], [100, 140], [end-3, end]] -> v_block = [[11, 99], [141, end-4]] ''' if hasattr(self, 'ex_block'): # if whole block is excluded if self.ex_block[0, 0] == 0 and self.ex_block[0, 1] == self.time - 1: self.v_block = np.zeros((0, 2), dtype='int') self.v_time = np.array([0]) self.v_N = len(self.v_block) return valid = np.ones(self.time) for ex in self.ex_block: valid[ex[0]:ex[1] + 1] = 0 self.v_block = gen.find_blocks_large(valid, 0.5, 1, 1) else: self.v_block = np.array([[0, self.time - 1]]) self.v_time = (np.diff(self.v_block, axis=1) + 1).flatten() self.v_N = len(self.v_block)