lifetimes

mdtools.dtrj.lifetimes(dtrj, axis=-1, uncensored=False, discard_neg_start=False, discard_all_neg=False, return_states=False, return_cmp_ix=False)[source]

Calculate the state lifetimes for each compound.

Parameters:
  • dtrj (array_like) – Array containing the discrete trajectory.

  • axis (int, optional) – The axis along which to search for state transitions. For ordinary discrete trajectories with shape (n, f) or (f,), where n is the number of compounds and f is the number of frames, set axis to -1. If you parse a transposed discrete trajectory of shape (f, n), set axis to 0.

  • uncensored (bool, optional) – If True only take into account uncensored states, i.e. states whose start and end lie within the trajectory. In other words, discard the truncated (censored) states at the beginning and end of the trajectory. For these states the start/end time is unknown.

  • discard_neg_start (bool, optional) – If True, discard the lifetimes of all negative states (see notes). This is equivalent to discarding all transitions starting from a negative state when calculating transition rates with mdtools.dtrj.trans_rate() or remain probabilities with mdtools.dtrj.remain_prob(). Has no effect if discard_all_neg is True.

  • discard_all_neg (bool, optional) – If True, discard the lifetimes of all negative states and of all states that are followed by a negative state (see notes). This is equivalent to discarding all transitions starting from or ending in a negative state when calculating transition rates with mdtools.dtrj.trans_rate() or remain probabilities with mdtools.dtrj.remain_prob().

  • return_states (bool, optional) – If True, return the state indices associated with the returned lifetimes.

  • return_cmp_ix (bool, optional) – If True, return the compound indices associated with the returned lifetimes.

Returns:

  • lt (numpy.ndarray) – 1-dimensional array containing the lifetimes for all compounds in all states.

  • states (numpy.ndarray) – 1-dimensional array of the same shape as lt containing the corresponding states indices. Only returned if return_states is True.

  • cmp_ix (numpy.ndarray) – 1-dimensional array of the same shape as lt containing the corresponding compound indices. Only returned if return_cmp_ix is True.

See also

mdtools.dtrj.lifetimes_per_state()

Calculate the state lifetimes for each state

mdtools.dtrj.trans_rate()

Calculate the transition rate for each compound averaged over all states

mdtools.dtrj.kaplan_meier()

Estimate the state survival function using the Kaplan-Meier estimator

mdtools.dtrj.remain_prob()

Calculate the probability that a compound is still (or again) in the same state as at time \(t_0\) after a lag time \(\Delta t\)

Notes

State lifetimes are calculated by simply counting the number of frames a given compound stays in a given state. Note that lifetimes calculated in this way are usually biased to lower values because of the limited length of the trajectory and because of truncation/censoring at the trajectory edges: At the beginning and end of the trajectory it is impossible to say how long a compound has already been it’s initial state or how long it will stay in it’s final state. For better estimates of the average state lifetime use mdtools.dtrj.kaplan_meier(), mdtools.dtrj.trans_rate(), or mdtools.dtrj.remain_prob().

If uncensored is True, the truncated states at the trajectory edges are ignored. Thus, lifetimes calculated in this way can at maximum be as long as the trajectory minus the first and last frame. Depending on the length of the trajectory and the average state lifetime, uncensored counting might waste a significant amount of the trajectory.

Valid and Invalid States

By default, all states in the given discrete trajectory are valid. However, in some cases you might want to treat certain states as invalid, e.g. because you only want to consider specific states. This can be achieved with the arguments discard_neg_start or discard_all_neg which treat negative states (i.e. states with a negative state number/index) as invalid states.

Note

If you want to treat states with index zero as invalid, too, simply subtract one from dtrj. If you want to treat all states below a certain cutoff as invalid, subtract this cutoff from dtrj. If you want to treat certain states as invalid, make these states negative, e.g. by multiplying these states with minus one.

The arguments discard_neg_start and discard_all_neg affect the counting of frames in which a given compound resides in a given state.

If both, discard_neg_start and discard_all_neg, are False (the default), all states are valid, i.e. all frames are counted.

If discard_neg_start is True, negative states are not counted, i.e. the lifetimes of negative states are discarded. Thus, the resulting average lifetime is the average lifetime of all positive states.

If discard_all_neg is True, negative states and states that are followed by a negative state are not counted. This means transitions from or to negative states are completely discarded. Thus, the resulting average lifetime is the average lifetime of positive states that transition to another positive state.

Examples

>>> dtrj = np.array([[1, 2, 2, 3, 3, 3],
...                  [2, 2, 3, 3, 3, 1],
...                  [6, 6, 6, 1, 4, 4],
...                  [1, 6, 6, 6, 4, 4]])
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 3, 2])
>>> states
array([1, 2, 3, 2, 3, 1, 6, 1, 4, 1, 6, 4])
>>> cmp_ix
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
>>> mdt.nph.group_by(states, lt)
[array([1, 1, 1, 1]), array([2, 2]), array([3, 3]), array([2, 2]), array([3, 3])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([3, 1, 2]), array([1, 3, 2])]
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, axis=0, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2])
>>> states
array([1, 2, 6, 1, 2, 6, 2, 3, 6, 3, 1, 6, 3, 4, 3, 1, 4])
>>> cmp_ix
array([0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5])
>>> mdt.nph.group_by(states, lt)
[array([1, 1, 1, 1]), array([1, 2, 1]), array([1, 2, 2, 1]), array([2, 2]), array([1, 2, 2, 1])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 1, 1, 1]), array([2, 2]), array([1, 1, 2]), array([2, 1, 1]), array([2, 2]), array([1, 1, 2])]
>>> dtrj = np.array([[1, 2, 2, 3, 3, 3],
...                  [2, 2, 3, 3, 3, 1],
...                  [3, 3, 3, 1, 2, 2],
...                  [1, 3, 3, 3, 2, 2]])
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 3, 2])
>>> states
array([1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 3, 2])
>>> cmp_ix
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
>>> mdt.nph.group_by(states, lt)
[array([1, 1, 1, 1]), array([2, 2, 2, 2]), array([3, 3, 3, 3])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([3, 1, 2]), array([1, 3, 2])]
>>> np.mean(lt)
2.0
>>> np.std(lt, ddof=1)
0.8528028654224418
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_unc
array([2, 3, 1, 3])
>>> states_unc
array([2, 3, 1, 3])
>>> cmp_ix_unc
array([0, 1, 2, 3])
>>> ax = 0
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, axis=ax, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 1, 1, 1, 2, 2, 1, 3, 2, 1, 1, 2, 2, 1, 1, 2])
>>> states
array([1, 2, 3, 1, 2, 3, 2, 3, 3, 1, 3, 3, 2, 3, 1, 2])
>>> cmp_ix
array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5])
>>> mdt.nph.group_by(states, lt)
[array([1, 1, 1, 1]), array([1, 2, 1, 2, 2]), array([1, 2, 3, 2, 1, 2, 1])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 1, 1, 1]), array([2, 2]), array([1, 3]), array([2, 1, 1]), array([2, 2]), array([1, 1, 2])]
>>> np.mean(lt)
1.5
>>> np.std(lt, ddof=1)
0.6324555320336759
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_unc
array([1, 1, 1, 1])
>>> states_unc
array([2, 3, 1, 1])
>>> cmp_ix_unc
array([0, 0, 3, 5])
>>> dtrj = np.array([[ 1,  2,  2,  1,  1,  1],
...                  [ 2,  2,  3,  3,  3,  2],
...                  [-3, -3, -3, -1,  2,  2],
...                  [ 2,  3,  3,  3, -2, -2]])
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 3, 2])
>>> states
array([ 1,  2,  1,  2,  3,  2, -3, -1,  2,  2,  3, -2])
>>> cmp_ix
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3])
>>> mdt.nph.group_by(states, lt)
[array([3]), array([2]), array([1]), array([1, 3]), array([2, 2, 1, 2, 1]), array([3, 3])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([3, 1, 2]), array([1, 3, 2])]
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_unc
array([2, 3, 1, 3])
>>> states_unc
array([ 2,  3, -1,  3])
>>> cmp_ix_unc
array([0, 1, 2, 3])
>>> lt_start, states_start, cmp_ix_start = mdt.dtrj.lifetimes(
...     dtrj,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start
array([1, 2, 3, 2, 3, 1, 2, 1, 3])
>>> states_start
array([1, 2, 1, 2, 3, 2, 2, 2, 3])
>>> cmp_ix_start
array([0, 0, 0, 1, 1, 1, 2, 3, 3])
>>> mdt.nph.group_by(states_start, lt_start)
[array([1, 3]), array([2, 2, 1, 2, 1]), array([3, 3])]
>>> mdt.nph.group_by(cmp_ix_start, lt_start, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([2]), array([1, 3])]
>>> lt_start_unc, states_start_unc, cmp_ix_start_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_start_unc
array([2, 3, 3])
>>> states_start_unc
array([2, 3, 3])
>>> cmp_ix_start_unc
array([0, 1, 3])
>>> lt_all, states_all, cmp_ix_all = mdt.dtrj.lifetimes(
...     dtrj,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all
array([1, 2, 3, 2, 3, 1, 2, 1])
>>> states_all
array([1, 2, 1, 2, 3, 2, 2, 2])
>>> cmp_ix_all
array([0, 0, 0, 1, 1, 1, 2, 3])
>>> mdt.nph.group_by(states_all, lt_all)
[array([1, 3]), array([2, 2, 1, 2, 1]), array([3])]
>>> mdt.nph.group_by(cmp_ix_all, lt_all, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([2]), array([1])]
>>> lt_all_unc, states_all_unc, cmp_ix_all_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_all_unc
array([2, 3])
>>> states_all_unc
array([2, 3])
>>> cmp_ix_all_unc
array([0, 1])
>>> ax = 0
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, axis=ax, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1])
>>> states
array([ 1,  2, -3,  2,  2, -3,  3,  2,  3, -3,  3,  1,  3, -1,  3,  1,  3,
        2, -2,  1,  2, -2])
>>> cmp_ix
array([0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5])
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_unc
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 2])
>>> states_unc
array([ 2, -3, -3,  3, -3,  3, -1,  3,  2,  2])
>>> cmp_ix_unc
array([0, 0, 1, 2, 2, 3, 3, 4, 4, 5])
>>> lt_start, states_start, cmp_ix_start = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start
array([1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2])
>>> states_start
array([1, 2, 2, 2, 3, 2, 3, 3, 1, 3, 3, 1, 3, 2, 1, 2])
>>> cmp_ix_start
array([0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5])
>>> lt_start_unc, states_start_unc, cmp_ix_start_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_start_unc
array([1, 1, 1, 1, 1, 2])
>>> states_start_unc
array([2, 3, 3, 3, 2, 2])
>>> cmp_ix_start_unc
array([0, 2, 3, 4, 4, 5])
>>> lt_all, states_all, cmp_ix_all = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
>>> states_all
array([1, 2, 3, 2, 3, 1, 3, 1, 3, 1])
>>> cmp_ix_all
array([0, 0, 1, 2, 2, 3, 3, 4, 4, 5])
>>> lt_all_unc, states_all_unc, cmp_ix_all_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_all_unc
array([1])
>>> states_all_unc
array([3])
>>> cmp_ix_all_unc
array([4])
>>> dtrj = np.array([[ 1, -2, -2,  3,  3,  3],
...                  [-2, -2,  3,  3,  3,  1],
...                  [ 3,  3,  3,  1, -2, -2],
...                  [ 1,  3,  3,  3, -2, -2],
...                  [ 1,  4,  4,  4,  4, -1]])
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 2, 3, 2, 3, 1, 3, 1, 2, 1, 3, 2, 1, 4, 1])
>>> states
array([ 1, -2,  3, -2,  3,  1,  3,  1, -2,  1,  3, -2,  1,  4, -1])
>>> cmp_ix
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])
>>> mdt.nph.group_by(states, lt)
[array([2, 2, 2, 2]), array([1]), array([1, 1, 1, 1, 1]), array([3, 3, 3, 3]), array([4])]
>>> mdt.nph.group_by(cmp_ix, lt, assume_sorted=True)
[array([1, 2, 3]), array([2, 3, 1]), array([3, 1, 2]), array([1, 3, 2]), array([1, 4, 1])]
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_unc
array([2, 3, 1, 3, 4])
>>> states_unc
array([-2,  3,  1,  3,  4])
>>> cmp_ix_unc
array([0, 1, 2, 3, 4])
>>> lt_start, states_start, cmp_ix_start = mdt.dtrj.lifetimes(
...     dtrj,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start
array([1, 3, 3, 1, 3, 1, 1, 3, 1, 4])
>>> states_start
array([1, 3, 3, 1, 3, 1, 1, 3, 1, 4])
>>> cmp_ix_start
array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4])
>>> mdt.nph.group_by(states_start, lt_start)
[array([1, 1, 1, 1, 1]), array([3, 3, 3, 3]), array([4])]
>>> mdt.nph.group_by(cmp_ix_start, lt_start, assume_sorted=True)
[array([1, 3]), array([3, 1]), array([3, 1]), array([1, 3]), array([1, 4])]
>>> lt_start_unc, states_start_unc, cmp_ix_start_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start_unc
array([3, 1, 3, 4])
>>> states_start_unc
array([3, 1, 3, 4])
>>> cmp_ix_start_unc
array([1, 2, 3, 4])
>>> lt_all, states_all, cmp_ix_all = mdt.dtrj.lifetimes(
...     dtrj,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all
array([3, 3, 1, 3, 1, 1])
>>> states_all
array([3, 3, 1, 3, 1, 1])
>>> cmp_ix_all
array([0, 1, 1, 2, 3, 4])
>>> mdt.nph.group_by(states_all, lt_all)
[array([1, 1, 1]), array([3, 3, 3])]
>>> mdt.nph.group_by(cmp_ix_all, lt_all, assume_sorted=True)
[array([3]), array([3, 1]), array([3]), array([1]), array([1])]
>>> lt_all_unc, states_all_unc, cmp_ix_all_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all_unc
array([3])
>>> states_all_unc
array([3])
>>> cmp_ix_all_unc
array([1])
>>> ax = 0
>>> lt, states, cmp_ix = mdt.dtrj.lifetimes(
...     dtrj, axis=ax, return_states=True, return_cmp_ix=True
... )
>>> lt
array([1, 1, 1, 2, 2, 2, 1, 1, 3, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1])
>>> states
array([ 1, -2,  3,  1, -2,  3,  4, -2,  3,  4,  3,  1,  3,  4,  3, -2,  4,
        3,  1, -2, -1])
>>> cmp_ix
array([0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5])
>>> lt_start, states_start, cmp_ix_start = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start
array([1, 1, 2, 2, 1, 3, 1, 2, 1, 1, 1, 2, 1, 1, 1])
>>> states_start
array([1, 3, 1, 3, 4, 3, 4, 3, 1, 3, 4, 3, 4, 3, 1])
>>> cmp_ix_start
array([0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5])
>>> lt_start_unc, states_start_unc, cmp_ix_start_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     discard_neg_start=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_start_unc
array([1, 2, 3, 1, 1, 1])
>>> states_start_unc
array([3, 3, 3, 1, 3, 1])
>>> cmp_ix_start_unc
array([0, 1, 2, 3, 3, 5])
>>> lt_all, states_all, cmp_ix_all = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all
array([1, 2, 2, 1, 3, 1, 2, 1, 1, 1, 1, 1])
>>> states_all
array([3, 1, 3, 4, 3, 4, 3, 1, 3, 4, 4, 3])
>>> cmp_ix_all
array([0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 5])
>>> lt_all_unc, states_all_unc, cmp_ix_all_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=ax,
...     uncensored=True,
...     discard_all_neg=True,
...     return_states=True,
...     return_cmp_ix=True,
... )
>>> lt_all_unc
array([1, 2, 3, 1, 1])
>>> states_all_unc
array([3, 3, 3, 1, 3])
>>> cmp_ix_all_unc
array([0, 1, 2, 3, 3])
>>> dtrj = np.array([[1, 2, 2],
...                  [3, 3, 3]])
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_unc
array([], dtype=int64)
>>> states_unc
array([], dtype=int64)
>>> cmp_ix_unc
array([], dtype=int64)
>>> lt_unc, states_unc, cmp_ix_unc = mdt.dtrj.lifetimes(
...     dtrj,
...     axis=0,
...     uncensored=True,
...     return_states=True,
...     return_cmp_ix=True
... )
>>> lt_unc
array([], dtype=int64)
>>> states_unc
array([], dtype=int64)
>>> cmp_ix_unc
array([], dtype=int64)