def crop(self, r, df): n_space_cols = len([col for col in 'xyz' if col in df.columns]) for regions in self.unit_region.values(): if len(regions) < r: r -= len(regions) else: if isinstance(regions[r], (pt.Polytope, pt.Region)): raise NotImplementedError else: _min, _max = regions[r] if n_space_cols < _min.size: assert _min.size == n_space_cols + 1 df = df[(_min[-1] <= df['t']) & (df['t'] <= _max[-1])] df = crop(df, np.r_[_min[:-1], _max[:-1] - _min[:-1]]) else: df = crop(df, np.r_[_min, _max - _min]) return df
def crop(self, r, df): n_space_cols = len([col for col in 'xyz' if col in df.columns]) loc_indices = set() df_r = None for u in self.group[r]: if isinstance(self.unit_region[u], (pt.Polytope, pt.Region)): raise NotImplementedError else: _min, _max = self.unit_region[u] if n_space_cols < _min.size: assert _min.size == n_space_cols + 1 df_u = df[(_min[-1] <= df['t']) & (df['t'] <= _max[-1])] df_u = crop(df_u, np.r_[_min[:-1], _max[:-1] - _min[:-1]]) else: df_u = crop(df, np.r_[_min, _max - _min]) if df_r is None: df_r = df_u else: df_r = pd.merge(df_r, df_u, how='outer') return df_r
def crop(self, r, df, **kwargs): kwargs["preserve_index"] = True n_space_cols = len([col for col in "xyz" if col in df.columns]) loc_indices = set() df_r = None for u in self.group[r]: if isinstance(self.unit_region[u], (pt.Polytope, pt.Region)): raise NotImplementedError else: _min, _max = self.unit_region[u] if n_space_cols < _min.size: assert _min.size == n_space_cols + 1 df_u = df[(_min[-1] <= df["t"]) & (df["t"] <= _max[-1])] df_u = crop(df_u, np.r_[_min[:-1], _max[:-1] - _min[:-1]], **kwargs) else: df_u = crop(df, np.r_[_min, _max - _min], **kwargs) if df_r is None: df_r = df_u else: df_r = pd.merge(df_r, df_u, how="outer") return reindex_trajectories(df_r.sort_values(by=["n", "t"]))
def cropping_utility(): import argparse parser = argparse.ArgumentParser(prog="crop", description="crop 2D trajectories") parser.add_argument( "--columns", help="input trajectory file column names (comma-separated list)") parser.add_argument("-q", "--quiet", action="store_true", help="do not ask to overwrite the file") parser.add_argument("input_file", help="path to input trajectory file") parser.add_argument("bounding_box", help="bounding box as left,bottom,right,top") parser.add_argument("output_file", nargs="?", help="path to output trajectory file") args = parser.parse_args() bounding_box = np.array([float(x) for x in args.bounding_box.split(",")]) dim = int(round(float(bounding_box.size) * 0.5)) bounding_box[dim:] -= bounding_box[:dim] load_kwargs = {} if args.columns: load_kwargs["columns"] = args.columns.split(",") trajectories = load_xyt(args.input_file, **load_kwargs) cropped_trajectories = crop(trajectories, bounding_box.tolist()) if args.output_file: output_file = args.output_file else: output_file = args.input_file if not args.quiet: invite = "overwrite file '{}': [N/y] ".format(output_file) try: answer = raw_input(invite) # Py2 except NameError: answer = input(invite) if not (answer and answer[0].lower() == "y"): return cropped_trajectories.to_csv(output_file, sep="\t", index=False)
def cropping_utility(): import argparse parser = argparse.ArgumentParser(prog='crop', description='crop 2D trajectories') parser.add_argument( '--columns', help="input trajectory file column names (comma-separated list)") parser.add_argument('-q', '--quiet', action='store_true', help="do not ask to overwrite the file") parser.add_argument('input_file', help='path to input trajectory file') parser.add_argument('bounding_box', help='bounding box as left,bottom,right,top') parser.add_argument('output_file', nargs='?', help='path to output trajectory file') args = parser.parse_args() bounding_box = np.array([float(x) for x in args.bounding_box.split(',')]) dim = int(round(float(bounding_box.size) * .5)) bounding_box[dim:] -= bounding_box[:dim] load_kwargs = {} if args.columns: load_kwargs['columns'] = args.columns.split(',') trajectories = load_xyt(args.input_file, **load_kwargs) cropped_trajectories = crop(trajectories, bounding_box.tolist()) if args.output_file: output_file = args.output_file else: output_file = args.input_file if not args.quiet: invite = "overwrite file '{}': [N/y] ".format(output_file) try: answer = raw_input(invite) # Py2 except NameError: answer = input(invite) if not (answer and answer[0].lower() == 'y'): return cropped_trajectories.to_csv(output_file, sep='\t', index=False)
def random_walk(diffusivity=None, force=None, viscosity=None, drift=None, trajectory_mean_count=100, trajectory_count_sd=0, turnover=None, initial_trajectory_count=None, new_trajectory_count=None, lifetime_tau=None, lifetime=None, single=False, box=(0., 0., 1., 1.), duration=10., time_step=.05, minor_step_count=99, reflect=False, full=False, count_outside_trajectories=None): """ Generate random walks. Consider also the alternative generator :func:`~tramway.helper.simulation.categoricaltrap.random_walk_2d`. The `trajectory_mean_count` and `trajectory_count_sd` arguments are not compatible with the `initial_trajectory_count` and `new_trajectory_count` arguments. Use either pair of arguments. Arguments: diffusivity (callable or float): if callable, takes a coordinate vector (:class:`~numpy.ndarray`) and time (float), and returns the local diffusivity (float) in :math:`\mu m^2.s^{-1}` force (callable): takes a coordinate vector (:class:`~numpy.ndarray`) and time (float) and returns the local force vector (:class:`~numpy.ndarray`) viscosity (callable or float): if callable, takes a coordinate vector (:class:`~numpy.ndarray`) and time (float), and returns the local viscosity (float) that divides the local force; the default viscosity ensures equilibrium conditions drift (callable): takes a coordinate vector (:class:`~numpy.ndarray`) and time (float) if `force` is not defined, or the same two arguments plus the diffusivity (float) and force (:class:`~numpy.ndarray`) otherwise, and returns the local drift; this lets `viscosity` unevaluated trajectory_mean_count (int or float): average number of active trajectories at any time trajectory_count_sd (int or float): standard deviation of the number of active trajectories turnover (float): fraction of trajectories that will end at each time step; **deprecated** initial_trajectory_count (int): initial number of active trajectories new_trajectory_count (int or callable): number of newly generated trajectories; if `new_trajectory_count` is ``callable``, then it takes the time of a step as input and returns the count for this step as an ``int`` lifetime_tau (float): trajectory lifetime constant in seconds lifetime (callable or float): trajectory lifetime in seconds; if `lifetime` is a ``float``, then it acts like `lifetime_tau`; if `lifetime` is ``callable``, then it takes as input the initial time of the trajectory single (bool): allow single-point trajectories box (array-like): origin and size of the space bounding box duration (float): duration of the simulation in seconds time_step (float): duration between two consecutive observations minor_step_count (int): number of intermediate unobserved steps reflect (bool): reflect the minor steps that would otherwise leave the bounding box full (bool): include locations that are outside the bounding box and times that are posterior to `duration` count_outside_trajectories (bool): include trajectories that have left the bounding box in determining the number of trajectories at each observation step; **deprecated** Returns: pandas.DataFrame: simulated trajectories with 'n' the trajectory index, 't' the time and with other columns for location coordinates """ if turnover is not None: warnings.warn('`turnover` is deprecated', DeprecationWarning) if count_outside_trajectories is False: warnings.warn('`count_outside_trajectories` is deprecated', DeprecationWarning) _box = np.asarray(box) dim = int(_box.size / 2) support_lower_bound = _box[:dim] support_size = _box[dim:] support_upper_bound = support_lower_bound + support_size # default diffusivity and drift maps #def null_scalar_map(xy, t): # return 0. #def null_vector_map(xy, t): # return np.zeros((dim,)) if callable(diffusivity): pass elif np.isscalar(diffusivity) and 0 < diffusivity: _D = diffusivity diffusivity = lambda x, t: _D else: raise ValueError('`diffusivity` must be callable or a positive float') if force and not callable(force): raise TypeError('`force` must be callable') if drift: _drift = drift if force: drift = lambda x, t, D: _drift(x, t, D, force(x, t)) else: drift = lambda x, t, D: _drift(x, t) elif force: if viscosity is None: drift = lambda x, t, D: D * force(x, t) elif callable(viscosity): drift = lambda x, t, D: force(x, t) / viscosity(x, t) elif np.isscalar(viscosity) and 0 < viscosity: drift = lambda x, t, D: force(x, t) / viscosity else: raise ValueError( '`viscosity` must be callable or a positive float') else: drift = lambda x, t, D: 0 # N = int(round(float(duration) / time_step)) # number of observed steps min_step_count = 1 if single else 2 # minimum number of observed steps per trajectory if callable(lifetime): lifetime_tau = None elif lifetime: lifetime_tau, lifetime = lifetime, None if lifetime_tau: trajectory_count_sd = None elif trajectory_count_sd: lifetime_tau = float(trajectory_count_sd) * time_step else: lifetime_tau = 4. * time_step # check if lifetime: assert callable(lifetime) else: assert bool(lifetime_tau) # number of trajectories at each time step K = None if new_trajectory_count is None: if trajectory_count_sd: K = np.rint( np.random.randn(N) * trajectory_count_sd + trajectory_mean_count).astype(int) else: K = np.full(N, trajectory_mean_count, dtype=int) elif initial_trajectory_count is None: warnings.warn( 'in combination with `new_trajectory_count`, use `initial_trajectory_count` instead of `trajectory_mean_count`' ) initial_trajectory_count = trajectory_mean_count k = np.zeros(N, dtype=int) # starting time and duration of the trajectories time_support = [] t = 0. for i in range(N): t += time_step if K is None: if i == 0: k_new = initial_trajectory_count elif callable(new_trajectory_count): k_new = new_trajectory_count(t) else: k_new = new_trajectory_count else: k_new = K[i] - k[i] if k_new <= 0: if k_new != 0: print('{:d} excess trajectories at step {:d}'.format( -k_new, i)) continue if lifetime: _lifetime = np.array([lifetime(t) for j in range(k_new)]) else: _lifetime = -np.log(1 - np.random.rand(k_new)) * lifetime_tau _lifetime = np.rint(_lifetime / time_step).astype(int) + 1 _lifetime = _lifetime[min_step_count <= _lifetime] time_support.append((t, _lifetime)) _lifetimes, _count = np.unique(_lifetime, return_counts=True) _lifetime = np.zeros(_lifetimes.max(), dtype=_count.dtype) _lifetime[_lifetimes - 1] = _count if K is not None: _count = np.flipud(np.cumsum(np.flipud(_lifetime))) k[i:min(i + _count.size, k.size )] += _count[:min(k.size - i, _count.size)] # actual_time_step = time_step / float(minor_step_count + 1) total_location_count = sum( [np.sum(_lifetime) for _, _lifetime in time_support]) # generate the trajectories N = np.empty((total_location_count, ), dtype=int) # trajectory index X = np.empty((total_location_count, dim), dtype=_box.dtype) # spatial coordinates T = np.empty((total_location_count, ), dtype=float) # time i, n = 0, 0 for t0, lifetimes in time_support: k = lifetimes.size # number of new trajectories at time t X0 = np.random.rand( k, dim) * support_size + support_lower_bound # initial coordinates for x0, _lifetime in zip(X0, lifetimes): # for each new trajectory n += 1 N[i] = n X[i] = x = x0 T[i] = t = t0 i += 1 # from here the main code (``not reflect``) is duplicated to reduce the number of if-tests if reflect: # duplicated code for j in range(1, _lifetime): for _ in range(minor_step_count + 1): D = diffusivity(x, t) A = drift(x, t, D) dx = actual_time_step * A + \ np.sqrt(actual_time_step * 2. * D) * np.random.randn(dim) x = x + dx # additional code above = support_upper_bound < x x[above] = 2 * support_upper_bound[above] - x[above] below = x < support_lower_bound if np.any(below & above): raise NotImplementedError( 'the jump is so large that its reflection also exceeds the opposite bound' ) x[below] = 2 * support_lower_bound[below] - x[below] # t = t + actual_time_step t = t0 + j * time_step # moderate numerical precision errors N[i] = n X[i] = x T[i] = t i += 1 else: # reference code for j in range(1, _lifetime): for _ in range(minor_step_count + 1): D = diffusivity(x, t) A = drift(x, t, D) dx = actual_time_step * A + \ np.sqrt(actual_time_step * 2. * D) * np.random.randn(dim) x = x + dx t = t + actual_time_step t = t0 + j * time_step # moderate numerical precision errors N[i] = n X[i] = x T[i] = t i += 1 # format the data as a dataframe columns = 'xyz' if dim <= 3: xcols = [d for d in columns[:dim]] else: xcols = ['x' + str(i) for i in range(dim)] points = pd.DataFrame(N, columns=['n']).join(pd.DataFrame( X, columns=xcols)).join(pd.DataFrame(T, columns=['t'])) # post-process if not full: points = crop(points, _box) points = points.loc[points['t'] <= duration + time_step * .1] if not single: n, count = np.unique(points['n'].values, return_counts=True) n = n[count == 1] if n.size: points = points.loc[~points['n'].isin(n)] points.index = np.arange(points.shape[0]) return points
def crop(self, df=None): _min, _max = self._bounding_box if df is None: df = self._spt_data.dataframe return crop(df, np.r_[_min, _max - _min])