Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
049f147
Add concurrence stuff (still needs improvements)
dwhswenson Mar 2, 2017
c3d5a0f
Merge branch 'master' into concurrence
dwhswenson Nov 5, 2017
c0a278c
Fix mixed-in tabs (I think)
dwhswenson Nov 5, 2017
d215016
refactor select_residue as lambda fcn
dwhswenson Nov 5, 2017
16bb06b
Refactor plot_concurrence into object + function
dwhswenson Nov 5, 2017
5d9a440
Python 3 import fix
dwhswenson Nov 6, 2017
1ab3d51
fix stupid error
dwhswenson Nov 6, 2017
bac1610
Fixes to concurrence plotting
dwhswenson Nov 24, 2017
42f904a
Merge branch 'master' into concurrence
dwhswenson Jan 24, 2018
c758fe9
Merge branch 'master' into concurrence
dwhswenson Apr 14, 2018
bba923f
Merge branch 'concurrence' of github.com:dwhswenson/contact_map into …
dwhswenson Apr 23, 2018
17d949d
added atom slice code
sroet Apr 25, 2018
ea177ee
added at least 1 test
sroet Apr 25, 2018
1fef87a
fix outdated default selection
dwhswenson May 1, 2018
1f934c8
atom slice does not alter the result, but is 10 times as slow
sroet May 15, 2018
caf225e
updated some pep8
sroet May 15, 2018
bdb4375
fixed pandas future warn about as_matrix
sroet May 21, 2018
698d4e5
Added documentation
sroet May 23, 2018
f440526
Added use_atom_slice to dask runner
sroet May 23, 2018
4753c00
Added tests for use_atom_slice
sroet May 23, 2018
57d4129
reworked code to fix code climate issues
sroet May 24, 2018
9b484ca
Refactor to make _use_atom_slice a class variable
sroet May 28, 2018
7275242
Correct typo in residue contact concurrence
dwhswenson May 29, 2018
b6661be
Bump to 0.4.0.dev0
dwhswenson May 30, 2018
0173f86
Merge pull request #39 from dwhswenson/0.4.0.dev0
dwhswenson May 30, 2018
277289a
Merge pull request #37 from sroet/fix_pandas_future_warn
dwhswenson May 30, 2018
10107cd
changed class variable name and all atom tuple to sorted list
sroet May 31, 2018
88bd67f
reworked to do everything in sliced space
sroet May 31, 2018
ff5f20c
finished up contact map optimisation
sroet May 31, 2018
52bd026
Mocked out slow md.atom_slice
sroet May 31, 2018
0fb5d0a
Merge branch 'master' into use_atom_slice
sroet May 31, 2018
e874747
Start tests for concurrence
dwhswenson Jul 4, 2018
fa47838
Tests on concurrences
dwhswenson Jul 4, 2018
30ecd46
tests for concurrence plotting
dwhswenson Jul 4, 2018
f31130e
minor test fixes
dwhswenson Jul 4, 2018
9c9987b
docstrings; add in __init__
dwhswenson Jul 4, 2018
5d8184d
add a couple to-dos
dwhswenson Jul 4, 2018
8983fe5
moved back to tuple int to int mapping and fixed some pep8 stuff
sroet Jul 9, 2018
5f7e96f
added test for 100% coverage
sroet Jul 9, 2018
4a76b47
try setting a timeout
sroet Jul 10, 2018
d930eef
fix by only spin up 4 workers instead of 31
sroet Jul 10, 2018
74e4713
Merge pull request #40 from sroet/fix_dask_travis_error
dwhswenson Jul 10, 2018
046c7bb
Merge branch 'master' into use_atom_slice
sroet Jul 10, 2018
9276bec
Updated the example and added an example on how to shut off the optim…
sroet Jul 10, 2018
b84283e
Update after Sander's review
dwhswenson Jul 10, 2018
cdfd277
Merge branch 'master' of github.com:dwhswenson/contact_map into concu…
dwhswenson Jul 11, 2018
a41bbb1
support multiple input types with concurrences
dwhswenson Jul 11, 2018
1d59768
Tests for input types; clearer code
dwhswenson Jul 11, 2018
64d3deb
Add ContactsDict and .contacts attrib
dwhswenson Jul 17, 2018
626456a
clean up repetitive code
dwhswenson Jul 17, 2018
91e3092
Fix ContactObject imports?
dwhswenson Jul 17, 2018
24f9928
Add extra blank line for readability
dwhswenson Jul 26, 2018
0868e2b
Merge pull request #28 from dwhswenson/concurrence
dwhswenson Jul 26, 2018
6112be8
add haystack_residues, query_residues
dwhswenson Nov 10, 2018
e9e353f
add haystack_residue_range & query_residue_range
dwhswenson Nov 10, 2018
7a5271d
Added some text about MDTraj resid vs resSeq
dwhswenson Nov 13, 2018
11faa66
Switch to properties; don't use idx for residue
dwhswenson Nov 13, 2018
50b1c84
docstrings; cleanup for codeclimate
dwhswenson Nov 13, 2018
4b665bb
Handle dask TimeoutError exception in tests
dwhswenson Nov 13, 2018
0aa03dd
Merge pull request #43 from dwhswenson/residues_for_atoms
sroet Nov 13, 2018
57e3128
clarity updates in test_dask_runner
dwhswenson Nov 15, 2018
28f5f6e
Merge pull request #44 from dwhswenson/fix_dask_tests
sroet Nov 16, 2018
4d882d9
merge upstream/master
Nov 17, 2018
1d584f9
fixed residue to ignore atoms when using atom_slice
Nov 17, 2018
263e7c4
work through comments David
Nov 17, 2018
383af3f
search for why pytest isn't installing
dwhswenson Jan 15, 2019
9d55671
try requesting upgrade when installing testing
dwhswenson Jan 15, 2019
0225f39
Merge pull request #45 from dwhswenson/fix_pytest_cov_master
dwhswenson Jan 15, 2019
d73b325
Add example for concurrences
dwhswenson Jan 29, 2019
4bb09ae
Update concurrence plotter to allow customization
dwhswenson Jan 29, 2019
19cd1f6
updates to docs
dwhswenson Jan 29, 2019
c9100c2
fix typos in example text
dwhswenson Jan 30, 2019
5cdc85c
fix typo in docs
dwhswenson Jan 30, 2019
5965805
Merge pull request #47 from dwhswenson/concurrences_example
dwhswenson Jan 30, 2019
6526d28
final style issue
sroet Feb 11, 2019
9ed6977
Merge pull request #35 from sroet/use_atom_slice
dwhswenson Feb 13, 2019
3aa7ad0
allow kwarg passthrough
sroet Mar 30, 2019
80af29c
plot at least 1 pixel
sroet Mar 30, 2019
0da7389
grab dpi and figsize from figure instead
sroet Mar 30, 2019
e7855c2
actually 'get' the dpi
sroet Mar 30, 2019
0e200a1
revert to old behavior, but raise warning
sroet Mar 30, 2019
c4294db
changed vmin and vmax paramters into a single vrange=(vmin, vmax)
sroet Apr 6, 2019
e9bb4ee
split out some code complexity
sroet Apr 6, 2019
1626d36
added warning tests
sroet Apr 6, 2019
897b038
does this fix duplication issue?
sroet Apr 6, 2019
6e4a01f
use a working mplbackend
sroet Apr 6, 2019
bd4eb33
try just removing the omnia channel?
dwhswenson Apr 12, 2019
9f28303
revert vrange to vmin/vmax and some misc typo's
sroet Apr 12, 2019
6c2d1eb
set matplotlib to pdf instead of png
sroet Apr 12, 2019
a092040
new rtd.yml with pip installs (rtd yml v2)
dwhswenson Apr 12, 2019
46d56bd
add cython to requirements.txt?
dwhswenson Apr 12, 2019
0011b4c
don't use system packages?
dwhswenson Apr 12, 2019
794f0d3
try an even more simplified conda?
dwhswenson Apr 12, 2019
2857e90
Merge pull request #50 from sroet/always_1_pixel
dwhswenson Apr 12, 2019
65bbb7e
Try to force cython and numpy installations
dwhswenson Apr 12, 2019
83bd2e2
pin scientific requirements
dwhswenson Apr 12, 2019
c57f0c9
pin everything
dwhswenson Apr 12, 2019
785afa0
reorder to get conda used?
dwhswenson Apr 12, 2019
afdc67a
switch to setuptools install
dwhswenson Apr 12, 2019
3380010
only pin scientific things (no pin on docs stuff)
dwhswenson Apr 12, 2019
41da486
Merge pull request #52 from dwhswenson/docs
dwhswenson Apr 14, 2019
e7ca829
Do the convert at only one place
sroet May 22, 2019
8c6d823
Remove commented out old code
sroet May 22, 2019
5fac01f
Merge pull request #54 from sroet/fix_dask
dwhswenson May 29, 2019
83a5e21
Release 0.4.0
dwhswenson Jun 3, 2019
575ce6e
Merge branch 'stable' into release-0.4.0
dwhswenson Jun 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ omit =
*/_version.py
exclude_lines =
pragma: no cover
-no-cov-
def __repr__
raise NotImplementedError
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
dask-worker-space
# netcdf outputs
*nc
.pytest_cache

contact_map/version.py
cover
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ install:
- pip list

script:
- export MPLBACKEND=PS
- export MPLBACKEND=SVG
- python -c "import contact_map"
- python autorelease_check.py --branch ${TRAVIS_BRANCH} --event ${TRAVIS_EVENT_TYPE} --allow-patch-skip #TODO remove allow-patch-skip
- py.test -vv --cov=contact_map --cov-report xml:cov.xml
Expand Down
2 changes: 1 addition & 1 deletion ci/conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package:
name: contact_map
# add ".dev0" for unreleased versions
version: "0.3.3"
version: "0.4.0"

source:
path: ../../
Expand Down
7 changes: 5 additions & 2 deletions contact_map/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

from .min_dist import NearestAtoms, MinimumDistanceCounter

from .concurrence import (
Concurrence, AtomContactConcurrence, ResidueContactConcurrence,
ConcurrencePlotter, plot_concurrence
)

from .dask_runner import DaskContactFrequency

from . import plot_utils

# import concurrence
292 changes: 292 additions & 0 deletions contact_map/concurrence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
import itertools
import mdtraj as md
import numpy as np

import contact_map
from .contact_map import ContactObject

try:
import matplotlib.pyplot as plt
except ImportError:
HAS_MATPLOTLIB = False
else:
HAS_MATPLOTLIB = True


class Concurrence(object):
"""Superclass for contact concurrence objects.

Contact concurrences measure what contacts occur simultaneously in a
trajectory. When defining states, one usually wants to characterize
based on multiple contacts that are made simultaneously; contact
concurrences makes it easier to identify those.

Parameters
----------
values : list of list of bool
the whether a contact is present for each contact pair at each
point in time; inner list is length number of frames, outer list
in length number of (included) contacts
labels : list of string
labels for each contact pair
"""
def __init__(self, values, labels=None):
self.values = values
self.labels = labels

# @property
# def lifetimes(self):
# pass

def set_labels(self, labels):
"""Set the contact labels

Parameters
----------
labels : list of string
labels for each contact pair
"""
self.labels = labels

def __getitem__(self, label):
idx = self.labels.index(label)
return self.values[idx]

# temporarily removed until we find a good metric here; this metric did
# not seem optimal and I stopped using it, so remove from code before
# release (can add back in later)
# def coincidence(self, label_list):
# this_list = np.asarray(self[label_list[0]])
# coincidence_list = this_list
# norm_sq = sum(this_list)
# for label in label_list[1:]:
# this_list = np.asarray(self[label])
# coincidence_list &= this_list
# norm_sq *= sum(this_list)

# return sum(coincidence_list) / np.sqrt(norm_sq)

def _regularize_contact_input(contact_input, atom_or_res):
"""Clean input for concurrence objects.

The allowed inputs are the :class:`.ContactFrequency`, or the
:class:`.ContactObject` coming from the ``.atom_contacts`` or
``.residue_contacts`` attribute of the contact frequency, or the list
coming from the ``.most_common()`` method for the
:class:`.ContactObject`.

Parameters
----------
contact_input : many possible types; see method description
input to the contact concurrences
atom_or_res : string
whether to treat this as an atom-based or residue-based contact;
allowed values are "atom", "res", and "residue"

Returns
-------
list :
list in the format of ``ContactCount.most_common()``
"""
if isinstance(contact_input, ContactObject):
contact_input = contact_input.contacts[atom_or_res]

if isinstance(contact_input, contact_map.ContactCount):
contact_input = contact_input.most_common()

return contact_input


class AtomContactConcurrence(Concurrence):
"""Contact concurrences for atom contacts.

Parameters
----------
trajectory : :class:`mdtraj.Trajectory`
the trajectory to analyze
atom_contacts : list
output from ``contact_map.atom_contacts.most_common()``
cutoff : float
cutoff, in nm. Should be the same as used in the contact map.
"""
def __init__(self, trajectory, atom_contacts, cutoff=0.45):
atom_contacts = _regularize_contact_input(atom_contacts, "atom")
atom_pairs = [[contact[0][0].index, contact[0][1].index]
for contact in atom_contacts]
labels = [str(contact[0]) for contact in atom_contacts]
distances = md.compute_distances(trajectory, atom_pairs=atom_pairs)
vector_f = np.vectorize(lambda d: d < cutoff)
# transpose because distances is ndarray shape (n_frames,
# n_contacts); values should be list shape (n_contacts, n_frames)
values = vector_f(distances).T.tolist()
super(AtomContactConcurrence, self).__init__(values=values,
labels=labels)


class ResidueContactConcurrence(Concurrence):
"""Contact concurrences for residue contacts.

Parameters
----------
trajectory : :class:`mdtraj.Trajectory`
the trajectory to analyze
residue_contacts : list
output from ``contact_map.residue_contacts.most_common()``
cutoff : float
cutoff, in nm. Should be the same as used in the contact map.
select : string
additional atom selection string for MDTraj; defaults to "and symbol
!= 'H'"
"""
def __init__(self, trajectory, residue_contacts, cutoff=0.45,
select="and symbol != 'H'"):
residue_contacts = _regularize_contact_input(residue_contacts,
"residue")
residue_pairs = [[contact[0][0], contact[0][1]]
for contact in residue_contacts]
labels = [str(contact[0]) for contact in residue_contacts]
values = []
select_residue = lambda idx: trajectory.topology.select(
"resid " + str(idx) + " " + select
)
for res_A, res_B in residue_pairs:
atoms_A = select_residue(res_A.index)
atoms_B = select_residue(res_B.index)
atom_pairs = itertools.product(atoms_A, atoms_B)
distances = md.compute_distances(trajectory,
atom_pairs=atom_pairs)
min_dists = [min(dists) for dists in distances]
values.append([d < cutoff for d in min_dists])

super(ResidueContactConcurrence, self).__init__(values=values,
labels=labels)


class ConcurrencePlotter(object):
"""Plot manager for contact concurrences.

Parameters
----------
concurrence : :class:`.Concurrence`
concurrence to plot; default None allows to override later
labels : list of string
labels for the contact pairs, default None will use concurrence
labels if available, integers if not
x_values : list of numeric
values to use for the time axis; default None uses integers starting
at 0 (can be used to assign the actual simulation time to the
x-axis)
"""
def __init__(self, concurrence=None, labels=None, x_values=None):
self.concurrence = concurrence
self.labels = self.get_concurrence_labels(concurrence, labels)
self._x_values = x_values

@staticmethod
def get_concurrence_labels(concurrence, labels=None):
"""Extract labels for contact from a concurrence object

If ``labels`` is given, that is returned. Otherwise, the
``concurrence`` is checked for labels, and those are used. If those
are also not available, string forms of integers starting with 0 are
returned.


Parameters
----------
concurrence : :class:`.Concurrence`
concurrence, which may have label information
labels : list of string
labels to use for contacts (optional)

Returns
-------
list of string
labels to use for contacts
"""
if labels is None:
if concurrence and concurrence.labels is not None:
labels = concurrence.labels
else:
labels = [str(i) for i in range(len(concurrence.values))]
return labels

@property
def x_values(self):
"""list : values to use for the x-axis (time)"""
x_values = self._x_values
if x_values is None:
x_values = list(range(len(self.concurrence.values[0])))
return x_values

@x_values.setter
def x_values(self, x_values):
self._x_values = x_values

def plot(self, concurrence=None, **kwargs):
"""Contact concurrence plot based on matplotlib

Additional kwargs given here will be passed to the matplotlib
``Axes.plot()`` method.

Parameters
----------
concurrence : :class:`.Concurrence`
optional; default None uses ``self.concurrence``; this allows
one to override the use of ``self.concurrence``

Returns
-------
fig : :class:`.matplotlib.Figure`
ax : :class:`.matplotlib.Axes`
lgd: :class:`.matplotlib.legend.Legend`
objects for matplotlib-based plot of contact concurrences
"""
if not HAS_MATPLOTLIB: # pragma: no cover
raise ImportError("matplotlib not installed")
if concurrence is None:
concurrence = self.concurrence
labels = self.get_concurrence_labels(concurrence=concurrence)
x_values = self.x_values

fig = plt.figure(1)
ax = fig.add_subplot(111)

plot_kwargs = {'markersize': 1}
plot_kwargs.update(kwargs)

y_val = -1.0
for label, val_set in zip(labels, concurrence.values):
x_vals = [x for (x, y) in zip(x_values, val_set) if y]
ax.plot(x_vals, [y_val] * len(x_vals), '.', label=label,
**plot_kwargs)
y_val -= 1.0

ax.set_ylim(top=0.0)
ax.set_xlim(left=min(x_values), right=max(x_values))
ax.set_yticks([])
lgd = ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
return (fig, ax, lgd)


def plot_concurrence(concurrence, labels=None, x_values=None, **kwargs): # -no-cov-
"""
Convenience function for concurrence plots.

Additional kwargs given here will be passed to the matplotlib
``Axes.plot()`` method.

Parameters
----------
concurrence : :class:`.Concurrence`
concurrence to be plotted
labels: list of string
labels for contacts (optional)
x_values : list of float or list of int
values to use for the x-axis

See also
--------
:class:`.ConcurrencePlotter`
"""
return ConcurrencePlotter(concurrence, labels, x_values).plot(**kwargs)
Loading