Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrence #28

Merged
merged 27 commits into from
Jul 26, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 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
1fef87a
fix outdated default selection
dwhswenson May 1, 2018
7275242
Correct typo in residue contact concurrence
dwhswenson May 29, 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
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
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
52 changes: 46 additions & 6 deletions contact_map/concurrence.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import mdtraj as md
import numpy as np

import contact_map

try:
import matplotlib.pyplot as plt
except ImportError:
Expand All @@ -22,7 +24,7 @@ class Concurrence(object):
----------
values : list of list of bool
the whether a contact is present for each contact pair at each
point in time; outer list is length number of frames, inner list
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
Expand Down Expand Up @@ -63,6 +65,41 @@ def __getitem__(self, label):

# 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, contact_map.ContactFrequency):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be isinstance(contact_input, contact_map.ContactObject) ? As this statement would be False for a ContactMap or ContactDifference

if atom_or_res in ["atom", "atoms"]:
contact_input = contact_input.atom_contacts.most_common()
elif atom_or_res in ["residue", "residues", "res"]:
contact_input = contact_input.residue_contacts.most_common()
else:
raise RuntimeError("Bad value for atom_or_res: " +
str(atom_or_res))
elif isinstance(contact_input, contact_map.ContactCount):
contact_input = contact_input.most_common()

return contact_input


class AtomContactConcurrence(Concurrence):
"""Contact concurrences for atom contacts.
Expand All @@ -77,13 +114,15 @@ class AtomContactConcurrence(Concurrence):
cutoff, in nm. Should be the same as used in the contact map.
"""
def __init__(self, trajectory, atom_contacts, cutoff=0.45):
# TODO: the use of atom_contacts as input from most_common is weird
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)
values = list(map(list, zip(*vector_f(distances))))
# 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)

Expand All @@ -105,7 +144,8 @@ class ResidueContactConcurrence(Concurrence):
"""
def __init__(self, trajectory, residue_contacts, cutoff=0.45,
select="and symbol != 'H'"):
# TODO: the use of residue_contacts as input from most_common is weird
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]
Expand All @@ -120,7 +160,7 @@ def __init__(self, trajectory, residue_contacts, cutoff=0.45,
distances = md.compute_distances(trajectory,
atom_pairs=atom_pairs)
min_dists = [min(dists) for dists in distances]
values.append(list(map(lambda d: d < cutoff, min_dists)))
values.append([d < cutoff for d in min_dists])

super(ResidueContactConcurrence, self).__init__(values=values,
labels=labels)
Expand All @@ -144,7 +184,7 @@ class ConcurrencePlotter(object):
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
self._x_values = x_values

@staticmethod
def get_concurrence_labels(concurrence, labels=None):
Expand Down
23 changes: 23 additions & 0 deletions contact_map/tests/test_concurrence.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@ def setup_module():
n_neighbors_ignored=0)


@pytest.mark.parametrize("contact_type", ('atoms', 'residues'))
def test_regularize_contact_input(contact_type):
from contact_map.concurrence import _regularize_contact_input \
as regularize
most_common = {
'atoms': contacts.atom_contacts.most_common(),
'residues': contacts.residue_contacts.most_common()
}[contact_type]
contact_count = {
'atoms': contacts.atom_contacts,
'residues': contacts.residue_contacts
}[contact_type]
assert regularize(most_common, contact_type) == most_common
assert regularize(contact_count, contact_type) == most_common
assert regularize(contacts, contact_type) == most_common

def test_regularize_contact_input_bad_type():
from contact_map.concurrence import _regularize_contact_input \
as regularize
with pytest.raises(RuntimeError):
regularize(contacts, "foo")


class ContactConcurrenceTester(object):
def _test_default_labels(self, concurrence):
assert len(concurrence.labels) == len(self.labels) / 2
Expand Down