Skip to content

Commit 2c8d437

Browse files
authored
Merge pull request #85 from dwhswenson/release-0.6.0
Release 0.6.0
2 parents 62007fd + dce1ec4 commit 2c8d437

17 files changed

+1495
-139
lines changed

ci/conda-recipe/meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package:
22
name: contact_map
33
# add ".dev0" for unreleased versions
4-
version: "0.5.1"
4+
version: "0.6.0"
55

66
source:
77
path: ../../
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pytest
22
pytest-cov
3-
python-coveralls
3+
coveralls
44
codacy-coverage
55
autorelease

contact_map/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from .contact_count import ContactCount
1313

14+
from .contact_trajectory import ContactTrajectory, RollingContactFrequency
15+
1416
from .min_dist import NearestAtoms, MinimumDistanceCounter
1517

1618
from .concurrence import (

contact_map/contact_count.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,22 @@
1616
# pandas 0.25 not available on py27; can drop this when we drop py27
1717
_PD_VERSION = tuple(int(x) for x in pd.__version__.split('.')[:2])
1818

19-
def _colorbar(with_colorbar, cmap_f, norm, min_val):
19+
20+
def _colorbar(with_colorbar, cmap_f, norm, min_val, ax=None):
2021
if with_colorbar is False:
2122
return None
2223
elif with_colorbar is True:
2324
cbmin = np.floor(min_val) # [-1.0..0.0] => -1; [0.0..1.0] => 0
2425
cbmax = 1.0
25-
cb = ranged_colorbar(cmap_f, norm, cbmin, cbmax)
26+
cb = ranged_colorbar(cmap_f, norm, cbmin, cbmax, ax=ax)
2627
# leave open other inputs to be parsed later (like tuples)
2728
return cb
2829

2930

3031
# TODO: remove following: this is a monkeypatch for a bug in pandas
3132
# see: https://github.com/pandas-dev/pandas/issues/29814
3233
from pandas._libs.sparse import BlockIndex, IntIndex, SparseIndex
33-
def _patch_from_spmatrix(cls, data):
34+
def _patch_from_spmatrix(cls, data): # -no-cov-
3435
length, ncol = data.shape
3536

3637
if ncol != 1:
@@ -129,7 +130,7 @@ def df(self):
129130
index = list(range(self.n_x))
130131
columns = list(range(self.n_y))
131132

132-
if _PD_VERSION < (0, 25): # py27 only
133+
if _PD_VERSION < (0, 25): # py27 only -no-cov-
133134
mtx = mtx.tocoo()
134135
return pd.SparseDataFrame(mtx, index=index, columns=columns)
135136

@@ -198,18 +199,39 @@ def plot(self, cmap='seismic', vmin=-1.0, vmax=1.0, with_colorbar=True,
198199
"""
199200
if not HAS_MATPLOTLIB: # pragma: no cover
200201
raise RuntimeError("Error importing matplotlib")
202+
fig, ax = plt.subplots(**kwargs)
203+
204+
# Check the number of pixels of the figure
205+
self._check_number_of_pixels(fig)
206+
self.plot_axes(ax=ax, cmap=cmap, vmin=vmin, vmax=vmax)
207+
208+
return (fig, ax)
209+
210+
def plot_axes(self, ax, cmap='seismic', vmin=-1.0, vmax=1.0,
211+
with_colorbar=True):
212+
"""
213+
Plot contact matrix on a matplotlib.axes
214+
215+
Parameters
216+
----------
217+
ax : matplotlib.axes
218+
axes to plot the contact matrix on
219+
cmap : str
220+
color map name, default 'seismic'
221+
vmin : float
222+
minimum value for color map interpolation; default -1.0
223+
vmax : float
224+
maximum value for color map interpolation; default 1.0
225+
with_colorbar : bool
226+
If a colorbar is added to the axes
227+
"""
228+
201229
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
202230
cmap_f = plt.get_cmap(cmap)
203-
204-
fig, ax = plt.subplots(**kwargs)
205231
ax.axis([0, self.n_x, 0, self.n_y])
206232
ax.set_facecolor(cmap_f(norm(0.0)))
207233

208234
min_val = 0.0
209-
210-
# Check the number of pixels of the figure
211-
self._check_number_of_pixels(fig)
212-
213235
for (pair, value) in self.counter.items():
214236
if value < min_val:
215237
min_val = value
@@ -227,9 +249,7 @@ def plot(self, cmap='seismic', vmin=-1.0, vmax=1.0, with_colorbar=True,
227249
ax.add_patch(patch_0)
228250
ax.add_patch(patch_1)
229251

230-
_colorbar(with_colorbar, cmap_f, norm, min_val)
231-
232-
return (fig, ax)
252+
_colorbar(with_colorbar, cmap_f, norm, min_val, ax=ax)
233253

234254
def most_common(self, obj=None):
235255
"""

contact_map/contact_map.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import itertools
88
import pickle
99
import json
10+
11+
import warnings
12+
1013
import numpy as np
1114
import pandas as pd
1215
import mdtraj as md
@@ -171,6 +174,24 @@ def __init__(self, topology, query, haystack, cutoff, n_neighbors_ignored):
171174
}
172175
self._atom_idx_to_residue_idx = self._set_atom_idx_to_residue_idx()
173176

177+
@classmethod
178+
def from_contacts(cls, atom_contacts, residue_contacts, topology,
179+
query=None, haystack=None, cutoff=0.45,
180+
n_neighbors_ignored=2):
181+
obj = cls.__new__(cls)
182+
super(cls, obj).__init__(topology, query, haystack, cutoff,
183+
n_neighbors_ignored)
184+
185+
def get_contact_counter(contact):
186+
if isinstance(contact, ContactCount):
187+
return contact.counter
188+
else:
189+
return contact
190+
191+
obj._atom_contacts = get_contact_counter(atom_contacts)
192+
obj._residue_contacts = get_contact_counter(residue_contacts)
193+
return obj
194+
174195
def _set_atom_slice(self):
175196
""" Set atom slice logic """
176197
if (self._class_use_atom_slice is None and
@@ -675,13 +696,25 @@ def residue_contacts(self):
675696
class ContactMap(ContactObject):
676697
"""
677698
Contact map (atomic and residue) for a single frame.
699+
700+
.. deprecated:: 0.6.0
701+
``ContactMap`` will be removed in Contact Map Explorer 0.7.0 because
702+
it is redundant with ``ContactFrequency``. For more, see
703+
https://github.com/dwhswenson/contact_map/issues/82.
704+
678705
"""
679706
# Default for use_atom_slice, None tries to be smart
680707
_class_use_atom_slice = None
681708

709+
_deprecation_message=(
710+
"The ContactMap class will be removed in Contact Map Explorer 0.7. "
711+
+ "Use ContactFrequency instead. For more, see: "
712+
+ "https://github.com/dwhswenson/contact_map/issues/82."
713+
)
714+
682715
def __init__(self, frame, query=None, haystack=None, cutoff=0.45,
683716
n_neighbors_ignored=2):
684-
717+
warnings.warn(self._deprecation_message, FutureWarning)
685718
self._frame = frame # TODO: remove this?
686719
super(ContactMap, self).__init__(frame.topology, query, haystack,
687720
cutoff, n_neighbors_ignored)
@@ -692,6 +725,19 @@ def __init__(self, frame, query=None, haystack=None, cutoff=0.45,
692725
(atom_contacts, self._residue_contacts) = contact_maps
693726
self._atom_contacts = self.convert_atom_contacts(atom_contacts)
694727

728+
@classmethod
729+
def from_dict(cls, dct):
730+
warnings.warn(cls._deprecation_message, FutureWarning)
731+
return super(ContactMap, cls).from_dict(dct)
732+
733+
# don't need to add deprecation in from_json because it uses from_dict
734+
735+
@classmethod
736+
def from_file(cls, filename):
737+
warnings.warn(cls._deprecation_message, FutureWarning)
738+
return super(ContactMap, cls).from_file(filename)
739+
740+
695741
def __hash__(self):
696742
return hash((super(ContactMap, self).__hash__(),
697743
tuple(self._atom_contacts.items()),
@@ -746,6 +792,17 @@ def __init__(self, trajectory, query=None, haystack=None, cutoff=0.45,
746792
contacts = self._build_contact_map(trajectory)
747793
(self._atom_contacts, self._residue_contacts) = contacts
748794

795+
@classmethod
796+
def from_contacts(cls, atom_contacts, residue_contacts, n_frames,
797+
topology, query=None, haystack=None, cutoff=0.45,
798+
n_neighbors_ignored=2):
799+
obj = super(ContactFrequency, cls).from_contacts(
800+
atom_contacts, residue_contacts, topology, query, haystack,
801+
cutoff, n_neighbors_ignored
802+
)
803+
obj._n_frames = n_frames
804+
return obj
805+
749806
def __hash__(self):
750807
return hash((super(ContactFrequency, self).__hash__(),
751808
tuple(self._atom_contacts.items()),
@@ -923,6 +980,10 @@ def __sub__(self, other):
923980
def contact_map(self, *args, **kwargs): #pylint: disable=W0221
924981
raise NotImplementedError
925982

983+
@classmethod
984+
def from_contacts(self, *args, **kwargs): #pylint: disable=W0221
985+
raise NotImplementedError
986+
926987
@property
927988
def atom_contacts(self):
928989
n_x = self.topology.n_atoms

0 commit comments

Comments
 (0)