Skip to content

Commit

Permalink
Merge pull request #96 from dwhswenson/release-0.7
Browse files Browse the repository at this point in the history
Release 0.7
  • Loading branch information
dwhswenson committed Oct 28, 2020
2 parents 2c8d437 + 5b94518 commit 2ddd590
Show file tree
Hide file tree
Showing 19 changed files with 14,130 additions and 351 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -7,7 +7,7 @@

[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f7f3cf53698e4655ac8895f13fa5dea6)](https://www.codacy.com/app/dwhswenson/contact_map?utm_source=github.com&utm_medium=referral&utm_content=dwhswenson/contact_map&utm_campaign=Badge_Grade)
[![Maintainability](https://api.codeclimate.com/v1/badges/84768756d594176d8da6/maintainability)](https://codeclimate.com/github/dwhswenson/contact_map/maintainability)

[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/dwhswenson/contact_map/master)
# Contact Map Explorer

This package provides tools for analyzing and exploring contacts
Expand Down Expand Up @@ -38,6 +38,8 @@ representing the fraction of trajectory time that the contact was present.

Full documentation is at http://contact-map.readthedocs.io/.

Try it out online: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/dwhswenson/contact_map/master?filepath=%2Fexamples) (Note: the performance of the online servers can vary widely.)

## Installation

The easiest way to install is with `conda`. Conda is a powerful package and
Expand Down
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.6.0"
version: "0.7.0"

source:
path: ../../
Expand Down
4 changes: 3 additions & 1 deletion contact_map/__init__.py
Expand Up @@ -6,7 +6,9 @@
__version__ = version.version

from .contact_map import (
ContactMap, ContactFrequency, ContactDifference
ContactMap, ContactFrequency, ContactDifference,
AtomMismatchedContactDifference, ResidueMismatchedContactDifference,
OverrideTopologyContactDifference
)

from .contact_count import ContactCount
Expand Down
106 changes: 106 additions & 0 deletions contact_map/atom_indexer.py
@@ -0,0 +1,106 @@
import collections
import numpy as np
import mdtraj as md

def _atom_slice(traj, indices):
"""Mock MDTraj.atom_slice without rebuilding topology"""
xyz = np.array(traj.xyz[:, indices], order='C')
topology = traj.topology.copy()
if traj._have_unitcell:
unitcell_lengths = traj._unitcell_lengths.copy()
unitcell_angles = traj._unitcell_angles.copy()
else:
unitcell_lengths = None
unitcell_angles = None
time = traj._time.copy()

# Hackish to make the smart slicing work
topology._atoms = indices
topology._numAtoms = len(indices)
return md.Trajectory(xyz=xyz, topology=topology, time=time,
unitcell_lengths=unitcell_lengths,
unitcell_angles=unitcell_angles)

def residue_query_atom_idxs(sliced_query, atom_idx_to_residue_idx):
residue_query_atom_idxs = collections.defaultdict(list)
for sliced_idx in sliced_query:
residue_idx = atom_idx_to_residue_idx[sliced_idx]
residue_query_atom_idxs[residue_idx].append(sliced_idx)
return residue_query_atom_idxs


class AtomSlicedIndexer(object):
"""Indexer when using atom slicing.
"""
def __init__(self, topology, real_query, real_haystack, all_atoms):
self.all_atoms = all_atoms
self.sliced_idx = {
real_idx : sliced_idx
for sliced_idx, real_idx in enumerate(all_atoms)
}
self.real_idx = {
sliced_idx: real_idx
for real_idx, sliced_idx in self.sliced_idx.items()
}
self.query = set([self.sliced_idx[q] for q in real_query])
self.haystack = set([self.sliced_idx[h] for h in real_haystack])

# atom_idx_to_residue_idx
self.real_atom_idx_to_residue_idx = {atom.index: atom.residue.index
for atom in topology.atoms}
self.atom_idx_to_residue_idx = {
sliced_idx: self.real_atom_idx_to_residue_idx[real_idx]
for sliced_idx, real_idx in enumerate(all_atoms)
}
self.residue_query_atom_idxs = residue_query_atom_idxs(
self.query, self.atom_idx_to_residue_idx
)

def ignore_atom_idx(self, atoms, all_atoms_set):
result = set(atom.index for atom in atoms)
result &= all_atoms_set
result = set(self.sliced_idx[a] for a in result)
return result

def convert_atom_contacts(self, atom_contacts):
result = {frozenset(map(self.real_idx.__getitem__, pair)): value
for pair, value in atom_contacts.items()}
return collections.Counter(result)

def slice_trajectory(self, trajectory):
# Prevent (memory) expensive atom slicing if not needed.
# This check is also needed here because ContactFrequency slices the
# whole trajectory before calling this function.
if len(self.all_atoms) < trajectory.topology.n_atoms:
sliced = _atom_slice(trajectory, self.all_atoms)
else:
sliced = trajectory
return sliced


class IdentityIndexer(object):
"""Indexer when not using atom slicing.
"""
def __init__(self, topology, real_query, real_haystack, all_atoms):
self.all_atoms = all_atoms
self.topology = topology
identity_mapping = {a: a for a in range(topology.n_atoms)}
self.sliced_idx = identity_mapping
self.real_idx = identity_mapping
self.query = set(real_query)
self.haystack = set(real_haystack)
self.real_atom_idx_to_residue_idx = {atom.index: atom.residue.index
for atom in topology.atoms}
self.atom_idx_to_residue_idx = self.real_atom_idx_to_residue_idx
self.residue_query_atom_idxs = residue_query_atom_idxs(
self.query, self.atom_idx_to_residue_idx
)

def ignore_atom_idx(self, atoms, all_atoms_set):
return set(atom.index for atom in atoms)

def convert_atom_contacts(self, atom_contacts):
return atom_contacts

def slice_trajectory(self, trajectory):
return trajectory
13 changes: 13 additions & 0 deletions contact_map/contact_count.py
@@ -1,3 +1,4 @@
import collections
import scipy
import numpy as np
import pandas as pd
Expand Down Expand Up @@ -310,3 +311,15 @@ def most_common_idx(self):
most_common : same thing, using objects as key
"""
return self._counter.most_common()

def filter(self, idx):
"""New ContactCount filtered to idx.
Returns a new ContactCount with the only the counter keys/values
where both the keys are in idx
"""
dct = {k: v for k, v in self._counter.items()
if all([i in idx for i in k])}
new_count = collections.Counter()
new_count.update(dct)
return ContactCount(new_count, self._object_f, self.n_x, self.n_y)

0 comments on commit 2ddd590

Please sign in to comment.