def test_custom_dist_func(self): # Several 2D random walkers N = 5 length = 5 step_size = 2 search_range = 3 steps = (np.random.random((2, length, N)) - 0.5) * step_size x, y = np.cumsum(steps, axis=2) f = DataFrame(dict(x=x.ravel(), y=y.ravel(), frame=np.repeat(np.arange(length), N))) # link in normal (2D Euclidean) coordinates expected = self.link(f, search_range) # compute radial coordinates f_radial = f.copy() f_radial['angle'] = np.arctan2(f_radial['y'], f_radial['x']) f_radial['r'] = np.sqrt(f_radial['y']**2 + f_radial['x']**2) # leave x, y for the comparison at the end def dist_func(a, b): x1 = a[0] * np.cos(a[1]) y1 = a[0] * np.sin(a[1]) x2 = b[0] * np.cos(b[1]) y2 = b[0] * np.sin(b[1]) return np.sqrt((x1 - x2)**2 + (y1 - y2)**2) # link using a custom distance function actual = self.link(f_radial, search_range, pos_columns=['r', 'angle'], dist_func=dist_func) assert_traj_equal(actual, expected)
def test_isolated_continuous_random_walks(self): # Two 2D random walks np.random.seed(0) N = 30 Y = 250 M = 20 # margin, because negative values raise OutOfHash a = DataFrame({'x': M + random_walk(N), 'y': M + random_walk(N), 'frame': np.arange(N)}) b = DataFrame({'x': M + random_walk(N - 1), 'y': M + Y + random_walk(N - 1), 'frame': np.arange(1, N)}) f = pd.concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.zeros(N), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) # Many 2D random walks np.random.seed(0) initial_positions = [(100, 100), (200, 100), (100, 200), (200, 200)] import itertools c = itertools.count() def walk(x, y): i = next(c) return DataFrame({'x': x + random_walk(N - i), 'y': y + random_walk(N - i), 'frame': np.arange(i, N)}) f = pd.concat([walk(*pos) for pos in initial_positions]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([i*np.ones(N - i) for i in range(len(initial_positions))]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected)
def test_two_isolated(self): shape = (32, 32) expected = DataFrame({'x': [8, 16, 24, 16], 'y': [8, 8, 24, 24], 'frame': [0, 1, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_quadrature_distances(self): """A simple test to check whether the subnet linker adds orthogonal coordinates in quadrature (as in Pythagoras). We have two possible linking results: 1. A->C and B->D, cost (linear) = 16, cost (quadrature) = 200 2. A->D and B->C, cost (linear) = 28, cost (quadrature) = 200 """ def subnet_test(epsilon): """Returns 2 features in 2 frames, which represent a special case when the subnet linker adds distances in quadrature. With epsilon=0, subnet linking is degenerate. Therefore linking should differ for positive and negative epsilon.""" return pd.DataFrame([(0, 6, 0), #A (0, 14 + epsilon, 8), #B (1, 8, 0), #C (1, 0, 8)], #D columns=['frame', 'x', 'y']) trpos = self.link(subnet_test(1), 20) expected = subnet_test(1) expected['particle'] = np.array([0, 1, 1, 0]) assert_traj_equal(trpos, expected) trneg = self.link(subnet_test(-1), 20) expected = subnet_test(-1) expected['particle'] = np.array([0, 1, 0, 1]) assert_traj_equal(trneg, expected)
def test_custom_to_eucl(self): # Several 2D random walkers N = 5 length = 5 step_size = 2 search_range = 3 steps = (np.random.random((2, length, N)) - 0.5) * step_size x, y = np.cumsum(steps, axis=2) f = DataFrame(dict(x=x.ravel(), y=y.ravel(), frame=np.repeat(np.arange(length), N))) # link in normal (2D Euclidean) coordinates expected = self.link(f, search_range) # compute radial coordinates f_radial = f.copy() f_radial['angle'] = np.arctan2(f_radial['y'], f_radial['x']) f_radial['r'] = np.sqrt(f_radial['y'] ** 2 + f_radial['x'] ** 2) # leave x, y for the comparison at the end def to_eucl(arr): r, angle = arr.T x = r * np.cos(angle) y = r * np.sin(angle) return np.array([x, y]).T # link using a custom distance function actual = self.link(f_radial, search_range, pos_columns=['r', 'angle'], to_eucl=to_eucl) assert_traj_equal(actual, expected)
def test_one(self): expected = DataFrame({'x': [8, 16], 'y': [16, 16], 'frame': [0, 1], 'particle': [0, 0]}) actual = self.link(expected, shape=(24, 24), remove=[0]) assert_traj_equal(actual, expected) actual = self.link(expected, shape=(24, 24), search_range=7, remove=[0]) assert_equal(len(actual), 1)
def test_link_memory(self): expected = pd.DataFrame(self.coords_link, columns=self.pos_columns + ['frame']) expected['frame'] = expected['frame'].astype(np.int) actual = tp.link(expected, memory=self.memory, **self.link_params) expected['particle'] = self.expected_link_memory assert_traj_equal(actual, expected)
def test_one_trivial_stepper(self): # One 1D stepper N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) expected = f.copy() expected['particle'] = np.zeros(N) actual = self.link_df(f, 5) assert_traj_equal(actual, expected)
def test_blank_frame_no_memory(self): # One 1D stepper N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': [0, 1, 2, 4, 5], 'particle': [0, 0, 0, 1, 1]}) expected = f.copy() actual = self.link_df(f, 5) assert_traj_equal(actual, expected)
def test_two_full_overlap(self): shape = (24, 24) # a --> b # c --> d # a-c, b-c, b-d, a-d overlap expected = DataFrame({'x': [8, 15, 8, 15], 'y': [8, 8, 16, 16], 'frame': [0, 1, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_start_at_frame_other_than_zero(self): # One 1D stepper N = 5 FIRST_FRAME = 3 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': FIRST_FRAME + np.arange(N)}) expected = f.copy() expected['particle'] = np.zeros(N) actual = self.link_df(f, 5) assert_traj_equal(actual, expected)
def test_splitting_subnets(self): shape = (24, 48) expected = pd.DataFrame({'x': [8, 12, 16, 20, 32, 28, 40, 36], 'y': [8, 16, 8, 16, 8, 16, 8, 16], 'frame': [1, 0, 1, 0, 1, 0, 1, 0], 'particle': [0, 0, 1, 1, 2, 2, 3, 3]}) for n in range(5): for remove in itertools.combinations(range(4), n): actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_args_dtype(self): """Check whether find_link accepts float typed arguments""" # One 1D stepper N = 5 f = DataFrame( {'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) expected = f.copy() expected['particle'] = np.zeros(N) # Should not raise actual = self.link_df(f, 5.2, separation=9.5, diameter=15.2) assert_traj_equal(actual, expected)
def test_copy(self): """Check inplace/copy behavior of link_df """ # One 1D stepper N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) expected = f.copy() expected['particle'] = np.zeros(N) # Should copy actual = self.link_df(f, 5) assert_traj_equal(actual, expected) assert 'particle' not in f.columns
def test_shifting_string(self): shape = (24, 48) shift = 5 expected = pd.DataFrame({'x': [8, 8+shift, 16, 16+shift, 24, 24+shift, 32, 32+shift], 'y': [8, 16, 8, 16, 8, 16, 8, 16], 'frame': [0, 1, 0, 1, 0, 1, 0, 1], 'particle': [0, 0, 1, 1, 2, 2, 3, 3]}) for n in range(5): for remove in itertools.combinations(range(4), n): actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_copy(self): """Check inplace/copy behavior of link_df, link_df_iter""" # One 1D stepper N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) f_inplace = f.copy() expected = f.copy() expected['particle'] = np.zeros(N) # Should add particle column in-place # UNLESS diagnostics are enabled (or input dataframe is not writeable) actual = self.link_df(f_inplace, 5) assert_traj_equal(actual, expected) if self.do_diagnostics: assert 'particle' not in f_inplace.columns else: assert_traj_equal(actual, f_inplace) # Should copy actual = self.link_df(f, 5, copy_features=True) assert_traj_equal(actual, expected) assert 'particle' not in f.columns # Should copy actual_iter = self.link_df_iter(f, 5, hash_size=(10, 2)) assert_traj_equal(actual_iter, expected) assert 'particle' not in f.columns
def test_multiple_lost_simple(self): shape = (32, 32) # b a, b, c, d in frame 0 # a e c e in frame 1, disappears, should be linked to correct one # d # left expected = pd.DataFrame({'x': [8, 16, 24, 16, 15], 'y': [16, 8, 16, 24, 16], 'frame': [0, 0, 0, 0, 1], 'particle': [0, 1, 2, 3, 0]}) actual = self.link(expected, shape=shape, remove=[0]) assert_traj_equal(actual, expected) # top expected = pd.DataFrame({'x': [8, 16, 24, 16, 16], 'y': [16, 8, 16, 24, 15], 'frame': [0, 0, 0, 0, 1], 'particle': [0, 1, 2, 3, 1]}) actual = self.link(expected, shape=shape, remove=[0]) assert_traj_equal(actual, expected) # right expected = pd.DataFrame({'x': [8, 16, 24, 16, 17], 'y': [16, 8, 16, 24, 16], 'frame': [0, 0, 0, 0, 1], 'particle': [0, 1, 2, 3, 2]}) actual = self.link(expected, shape=shape, remove=[0]) assert_traj_equal(actual, expected) # bottom expected = pd.DataFrame({'x': [8, 16, 24, 16, 16], 'y': [16, 8, 16, 24, 17], 'frame': [0, 0, 0, 0, 1], 'particle': [0, 1, 2, 3, 3]}) actual = self.link(expected, shape=shape, remove=[0]) assert_traj_equal(actual, expected)
def test_two_double_overlap(self): shape = (24, 32) # (a b) c --> d # a-c and b-c overlap expected = DataFrame({'x': [8, 8, 16, 24], 'y': [8, 16, 12, 12], 'frame': [0, 1, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # (a b) d <-- c # a-d and b-d overlap expected = DataFrame({'x': [8, 8, 16, 24], 'y': [8, 16, 12, 12], 'frame': [0, 1, 1, 0], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_blank_frame_no_memory(self): N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': [0, 1, 2, 4, 5]}) expected = f.copy() # Using link_df, the particle will be given a new ID after the gap. expected['particle'] = np.array([0, 0, 0, 1, 1], dtype=np.float64) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) # link_df_iter will (in this test suite) iterate over only the frames # present in the dataframe, so the gap will be ignored. expected['particle'] = 0.0 actual = self.link_df_iter(f, 5, hash_size=(10, 10)) assert_traj_equal(actual, expected)
def test_one_trivial_stepper(self): # One 1D stepper N = 5 f = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) expected = f.copy() expected['particle'] = np.zeros(N) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f, 5, hash_size=(10, 2)) assert_traj_equal(actual_iter, expected) if self.do_diagnostics: assert 'diag_search_range' in self.diag.columns # Except for first frame, all particles should have been labeled # with a search_range assert not any(self.diag['diag_search_range'][ actual_iter.frame > 0].isnull())
def test_drop_link(self): # One 1D stepper. A new particle appears in frame 2. # The resulting subnet causes the trajectory to be broken # when link_strategy is 'drop' and search_range is large enough. N = 2 f_1particle = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) f = f_1particle.append(DataFrame( {'x': [3], 'y': [1], 'frame': [1]}), ignore_index=True) f_expected_without_subnet = f.copy() f_expected_without_subnet['particle'] = [0, 0, 1] # The linker assigns new particle IDs in arbitrary order. So # comparing with expected values is tricky. # We just check for the creation of 2 new trajectories. without_subnet = self.link_df(f, 1.5, retain_index=True) assert_traj_equal(without_subnet, f_expected_without_subnet) with_subnet = self.link_df(f, 5, retain_index=True) assert set(with_subnet.particle) == set((0, 1, 2))
def test_quadrature_sum(self): """A simple test to check whether the subnet linker adds distances in quadrature (as in Crocker-Grier).""" def subnet_test(epsilon): """Returns 2 features in 2 frames, which represent a special case when the subnet linker adds distances in quadrature. With epsilon=0, subnet linking is degenerate. Therefore linking should differ for positive and negative epsilon.""" return pd.DataFrame([(0, 10, 30), (0, 10, 0), (1, 0, 20), (1, 30, 20 + epsilon)], columns=['frame', 'x', 'y']) trpos = self.link(subnet_test(1), 30) expected = subnet_test(1) expected['particle'] = np.array([0, 1, 1, 0]) assert_traj_equal(trpos, expected) trneg = self.link(subnet_test(-1), 30) expected = subnet_test(-1) expected['particle'] = np.array([0, 1, 0, 1]) assert_traj_equal(trneg, expected)
def test_multiple_lost_subnet(self): shape = (24, 48) # (subnet 1, a-c) g (subnet 2, d-f) # left expected = pd.DataFrame({'x': [8, 10, 16, 32, 40, 38, 23], 'y': [8, 16, 8, 8, 8, 16, 8], 'frame': [0, 1, 0, 0, 0, 1, 1], 'particle': [0, 0, 1, 2, 3, 3, 1]}) for n in range(3): for remove in itertools.combinations(range(3), n): actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # right expected = pd.DataFrame({'x': [8, 10, 16, 32, 40, 38, 25], 'y': [8, 16, 8, 8, 8, 16, 8], 'frame': [0, 1, 0, 0, 0, 1, 1], 'particle': [0, 0, 1, 2, 3, 3, 2]}) for n in range(3): for remove in itertools.combinations(range(3), n): actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_memory_on_one_gap(self): N = 5 Y = 2 # Begin second feature one frame later than the first, so the particle labeling (0, 1) is # established and not arbitrary. a = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) b = DataFrame({'x': np.arange(1, N), 'y': Y + np.ones(N - 1), 'frame': np.arange(1, N)}) a = a.drop(3).reset_index(drop=True) f = pd.concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.array([0, 0, 0, 0]), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) expected.reset_index(drop=True, inplace=True) actual = self.link_df(f, 5, memory=1) assert_traj_equal(actual, expected) # Sort rows by frame (normal use) actual = self.link_df(pandas_sort(f, 'frame'), 5, memory=1) assert_traj_equal(actual, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5, memory=1) assert_traj_equal(actual, expected)
def test_two_isolated_steppers_one_gapped(self): N = 5 Y = 25 # Begin second feature one frame later than the first, # so the particle labeling (0, 1) is established and not arbitrary. a = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) a = a.drop(3).reset_index(drop=True) b = DataFrame({'x': np.arange(1, N), 'y': Y + np.ones(N - 1), 'frame': np.arange(1, N)}) f = pd.concat([a, b]) expected = f.copy() expected['particle'] = np.concatenate([np.array([0, 0, 0, 2]), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) expected.reset_index(drop=True, inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) # link_df_iter() tests not performed, because hash_size is # not knowable from the first frame alone. # Sort rows by frame (normal use) actual = self.link_df(pandas_sort(f, 'frame'), 5) assert_traj_equal(actual, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected)
def test_two_single_overlap(self): shape = (16, 40) # a --> b c --> d : b-c overlap expected = DataFrame({'x': [8, 16, 24, 32], 'y': [8, 8, 8, 8], 'frame': [0, 1, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # a --> b d <-- c : b-d overlap expected = DataFrame({'x': [8, 16, 24, 32], 'y': [8, 8, 8, 8], 'frame': [0, 1, 1, 0], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # b <-- a d --> c : a-d overlap expected = DataFrame({'x': [8, 16, 24, 32], 'y': [8, 8, 8, 8], 'frame': [1, 0, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_two_triple_overlap(self): shape = (24, 32) # a --> b # c --> d # a-c, b-c, and b-d overlap expected = DataFrame({'x': [8, 16, 16, 24], 'y': [8, 8, 16, 16], 'frame': [0, 1, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # a --> b # d <-- c # a-d, b-d, and b-c overlap expected = DataFrame({'x': [8, 16, 16, 24], 'y': [8, 8, 16, 16], 'frame': [0, 1, 1, 0], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected) # b <-- a # c -- > d # b-c, a-c, and a-d overlap expected = DataFrame({'x': [8, 16, 16, 24], 'y': [8, 8, 16, 16], 'frame': [1, 0, 0, 1], 'particle': [0, 0, 1, 1]}) for remove in [[], [0], [1], [0, 1]]: actual = self.link(expected, shape=shape, remove=remove) assert_traj_equal(actual, expected)
def test_nearby_continuous_random_walks(self): # Two 2D random walks np.random.seed(0) N = 30 Y = 250 M = 20 # margin, because negative values raise OutOfHash a = DataFrame({'x': M + random_walk(N), 'y': M + random_walk(N), 'frame': np.arange(N)}) b = DataFrame({'x': M + random_walk(N - 1), 'y': M + Y + random_walk(N - 1), 'frame': np.arange(1, N)}) f = pandas_concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.zeros(N), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f, 5, hash_size=(2*M, 2*M + Y)) assert_traj_equal(actual, expected) # Several 2D random walks np.random.seed(0) initial_positions = [(10, 11), (10, 18), (14, 15), (20, 21), (13, 13), (10, 10), (17, 19)] import itertools c = itertools.count() def walk(x, y): i = next(c) return DataFrame({'x': x + random_walk(N - i), 'y': y + random_walk(N - i), 'frame': np.arange(i, N)}) f = pandas_concat([walk(*pos) for pos in initial_positions]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([i*np.ones(N - i) for i in range(len(initial_positions))]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f, 5, hash_size=(2*M, 2*M)) assert_traj_equal(actual, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f1, 5, hash_size=(2*M, 2*M)) assert_traj_equal(actual, expected)
def test_two_nearby_steppers_one_gapped(self): N = 5 Y = 2 # Begin second feature one frame later than the first, so the particle labeling (0, 1) is # established and not arbitrary. a = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) b = DataFrame({'x': np.arange(1, N), 'y': Y + np.ones(N - 1), 'frame': np.arange(1, N)}) a = a.drop(3).reset_index(drop=True) f = pandas_concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.array([0, 0, 0, 2]), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) expected.reset_index(drop=True, inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Sort rows by frame (normal use) actual = self.link_df(pandas_sort(f, 'frame'), 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(pandas_sort(f, 'frame'), 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f1, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected)
def test_two_nearby_steppers(self): N = 5 Y = 2 # Begin second feature one frame later than the first, so the particle labeling (0, 1) is # established and not arbitrary. a = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) b = DataFrame({'x': np.arange(1, N), 'y': Y + np.ones(N - 1), 'frame': np.arange(1, N)}) f = pandas_concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.zeros(N), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Sort rows by frame (normal use) actual = self.link_df(pandas_sort(f, 'frame'), 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(pandas_sort(f, 'frame'), 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f1, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) if self.do_diagnostics: assert 'diag_subnet' in self.diag.columns assert 'diag_subnet_size' in self.diag.columns # Except for frame in which they appear, all particles should have # been labeled with a search_range assert not any(self.diag['diag_search_range'][ actual_iter.frame > 1].isnull()) # The number of loop iterations is reported by the numba linker only if self.linker_opts['link_strategy'] == 'numba': assert 'diag_subnet_iterations' in self.diag.columns
def test_two_isolated_steppers(self): N = 5 Y = 25 # Begin second feature one frame later than the first, so the particle labeling (0, 1) is # established and not arbitrary. a = DataFrame({'x': np.arange(N), 'y': np.ones(N), 'frame': np.arange(N)}) b = DataFrame({'x': np.arange(1, N), 'y': Y + np.ones(N - 1), 'frame': np.arange(1, N)}) f = pandas_concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.zeros(N), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Sort rows by frame (normal use) actual = self.link_df(pandas_sort(f, 'frame'), 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(pandas_sort(f, 'frame'), 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected) actual_iter = self.link_df_iter(f1, 5, hash_size=(50, 50)) assert_traj_equal(actual_iter, expected)
def test_nearby_continuous_random_walks(self): # Two 2D random walks np.random.seed(0) N = 30 Y = 250 M = 20 # margin, because negative values raise OutOfHash a = DataFrame({ 'x': M + random_walk(N), 'y': M + random_walk(N), 'frame': np.arange(N) }) b = DataFrame({ 'x': M + random_walk(N - 1), 'y': M + Y + random_walk(N - 1), 'frame': np.arange(1, N) }) f = pandas_concat([a, b]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate([np.zeros(N), np.ones(N - 1)]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f, 5, hash_size=(2 * M, 2 * M + Y)) assert_traj_equal(actual, expected) # Several 2D random walks np.random.seed(0) initial_positions = [(10, 11), (10, 18), (14, 15), (20, 21), (13, 13), (10, 10), (17, 19)] import itertools c = itertools.count() def walk(x, y): i = next(c) return DataFrame({ 'x': x + random_walk(N - i), 'y': y + random_walk(N - i), 'frame': np.arange(i, N) }) f = pandas_concat([walk(*pos) for pos in initial_positions]) expected = f.copy().reset_index(drop=True) expected['particle'] = np.concatenate( [i * np.ones(N - i) for i in range(len(initial_positions))]) pandas_sort(expected, ['particle', 'frame'], inplace=True) actual = self.link_df(f, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f, 5, hash_size=(2 * M, 2 * M)) assert_traj_equal(actual, expected) # Shuffle rows (crazy!) np.random.seed(0) f1 = f.reset_index(drop=True) f1.reindex(np.random.permutation(f1.index)) actual = self.link_df(f1, 5) assert_traj_equal(actual, expected) actual = self.link_df_iter(f1, 5, hash_size=(2 * M, 2 * M)) assert_traj_equal(actual, expected)