Skip to content

Commit

Permalink
Merge pull request #85 from dwhswenson/release-0.6.0
Browse files Browse the repository at this point in the history
Release 0.6.0
  • Loading branch information
dwhswenson committed Sep 1, 2020
2 parents 62007fd + dce1ec4 commit 2c8d437
Show file tree
Hide file tree
Showing 17 changed files with 1,495 additions and 139 deletions.
2 changes: 1 addition & 1 deletion ci/conda-recipe/meta.yaml
@@ -1,7 +1,7 @@
package:
name: contact_map
# add ".dev0" for unreleased versions
version: "0.5.1"
version: "0.6.0"

source:
path: ../../
Expand Down
2 changes: 1 addition & 1 deletion ci/pip-install/testing_requirements.txt
@@ -1,5 +1,5 @@
pytest
pytest-cov
python-coveralls
coveralls
codacy-coverage
autorelease
2 changes: 2 additions & 0 deletions contact_map/__init__.py
Expand Up @@ -11,6 +11,8 @@

from .contact_count import ContactCount

from .contact_trajectory import ContactTrajectory, RollingContactFrequency

from .min_dist import NearestAtoms, MinimumDistanceCounter

from .concurrence import (
Expand Down
46 changes: 33 additions & 13 deletions contact_map/contact_count.py
Expand Up @@ -16,21 +16,22 @@
# pandas 0.25 not available on py27; can drop this when we drop py27
_PD_VERSION = tuple(int(x) for x in pd.__version__.split('.')[:2])

def _colorbar(with_colorbar, cmap_f, norm, min_val):

def _colorbar(with_colorbar, cmap_f, norm, min_val, ax=None):
if with_colorbar is False:
return None
elif with_colorbar is True:
cbmin = np.floor(min_val) # [-1.0..0.0] => -1; [0.0..1.0] => 0
cbmax = 1.0
cb = ranged_colorbar(cmap_f, norm, cbmin, cbmax)
cb = ranged_colorbar(cmap_f, norm, cbmin, cbmax, ax=ax)
# leave open other inputs to be parsed later (like tuples)
return cb


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

if ncol != 1:
Expand Down Expand Up @@ -129,7 +130,7 @@ def df(self):
index = list(range(self.n_x))
columns = list(range(self.n_y))

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

Expand Down Expand Up @@ -198,18 +199,39 @@ def plot(self, cmap='seismic', vmin=-1.0, vmax=1.0, with_colorbar=True,
"""
if not HAS_MATPLOTLIB: # pragma: no cover
raise RuntimeError("Error importing matplotlib")
fig, ax = plt.subplots(**kwargs)

# Check the number of pixels of the figure
self._check_number_of_pixels(fig)
self.plot_axes(ax=ax, cmap=cmap, vmin=vmin, vmax=vmax)

return (fig, ax)

def plot_axes(self, ax, cmap='seismic', vmin=-1.0, vmax=1.0,
with_colorbar=True):
"""
Plot contact matrix on a matplotlib.axes
Parameters
----------
ax : matplotlib.axes
axes to plot the contact matrix on
cmap : str
color map name, default 'seismic'
vmin : float
minimum value for color map interpolation; default -1.0
vmax : float
maximum value for color map interpolation; default 1.0
with_colorbar : bool
If a colorbar is added to the axes
"""

norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
cmap_f = plt.get_cmap(cmap)

fig, ax = plt.subplots(**kwargs)
ax.axis([0, self.n_x, 0, self.n_y])
ax.set_facecolor(cmap_f(norm(0.0)))

min_val = 0.0

# Check the number of pixels of the figure
self._check_number_of_pixels(fig)

for (pair, value) in self.counter.items():
if value < min_val:
min_val = value
Expand All @@ -227,9 +249,7 @@ def plot(self, cmap='seismic', vmin=-1.0, vmax=1.0, with_colorbar=True,
ax.add_patch(patch_0)
ax.add_patch(patch_1)

_colorbar(with_colorbar, cmap_f, norm, min_val)

return (fig, ax)
_colorbar(with_colorbar, cmap_f, norm, min_val, ax=ax)

def most_common(self, obj=None):
"""
Expand Down
63 changes: 62 additions & 1 deletion contact_map/contact_map.py
Expand Up @@ -7,6 +7,9 @@
import itertools
import pickle
import json

import warnings

import numpy as np
import pandas as pd
import mdtraj as md
Expand Down Expand Up @@ -171,6 +174,24 @@ def __init__(self, topology, query, haystack, cutoff, n_neighbors_ignored):
}
self._atom_idx_to_residue_idx = self._set_atom_idx_to_residue_idx()

@classmethod
def from_contacts(cls, atom_contacts, residue_contacts, topology,
query=None, haystack=None, cutoff=0.45,
n_neighbors_ignored=2):
obj = cls.__new__(cls)
super(cls, obj).__init__(topology, query, haystack, cutoff,
n_neighbors_ignored)

def get_contact_counter(contact):
if isinstance(contact, ContactCount):
return contact.counter
else:
return contact

obj._atom_contacts = get_contact_counter(atom_contacts)
obj._residue_contacts = get_contact_counter(residue_contacts)
return obj

def _set_atom_slice(self):
""" Set atom slice logic """
if (self._class_use_atom_slice is None and
Expand Down Expand Up @@ -675,13 +696,25 @@ def residue_contacts(self):
class ContactMap(ContactObject):
"""
Contact map (atomic and residue) for a single frame.
.. deprecated:: 0.6.0
``ContactMap`` will be removed in Contact Map Explorer 0.7.0 because
it is redundant with ``ContactFrequency``. For more, see
https://github.com/dwhswenson/contact_map/issues/82.
"""
# Default for use_atom_slice, None tries to be smart
_class_use_atom_slice = None

_deprecation_message=(
"The ContactMap class will be removed in Contact Map Explorer 0.7. "
+ "Use ContactFrequency instead. For more, see: "
+ "https://github.com/dwhswenson/contact_map/issues/82."
)

def __init__(self, frame, query=None, haystack=None, cutoff=0.45,
n_neighbors_ignored=2):

warnings.warn(self._deprecation_message, FutureWarning)
self._frame = frame # TODO: remove this?
super(ContactMap, self).__init__(frame.topology, query, haystack,
cutoff, n_neighbors_ignored)
Expand All @@ -692,6 +725,19 @@ def __init__(self, frame, query=None, haystack=None, cutoff=0.45,
(atom_contacts, self._residue_contacts) = contact_maps
self._atom_contacts = self.convert_atom_contacts(atom_contacts)

@classmethod
def from_dict(cls, dct):
warnings.warn(cls._deprecation_message, FutureWarning)
return super(ContactMap, cls).from_dict(dct)

# don't need to add deprecation in from_json because it uses from_dict

@classmethod
def from_file(cls, filename):
warnings.warn(cls._deprecation_message, FutureWarning)
return super(ContactMap, cls).from_file(filename)


def __hash__(self):
return hash((super(ContactMap, self).__hash__(),
tuple(self._atom_contacts.items()),
Expand Down Expand Up @@ -746,6 +792,17 @@ def __init__(self, trajectory, query=None, haystack=None, cutoff=0.45,
contacts = self._build_contact_map(trajectory)
(self._atom_contacts, self._residue_contacts) = contacts

@classmethod
def from_contacts(cls, atom_contacts, residue_contacts, n_frames,
topology, query=None, haystack=None, cutoff=0.45,
n_neighbors_ignored=2):
obj = super(ContactFrequency, cls).from_contacts(
atom_contacts, residue_contacts, topology, query, haystack,
cutoff, n_neighbors_ignored
)
obj._n_frames = n_frames
return obj

def __hash__(self):
return hash((super(ContactFrequency, self).__hash__(),
tuple(self._atom_contacts.items()),
Expand Down Expand Up @@ -923,6 +980,10 @@ def __sub__(self, other):
def contact_map(self, *args, **kwargs): #pylint: disable=W0221
raise NotImplementedError

@classmethod
def from_contacts(self, *args, **kwargs): #pylint: disable=W0221
raise NotImplementedError

@property
def atom_contacts(self):
n_x = self.topology.n_atoms
Expand Down

0 comments on commit 2c8d437

Please sign in to comment.