Source code for prody.dynamics.heatmapper

# -*- coding: utf-8 -*-
"""This module defines functions for supporting VMD plugin `Heatmapper`_
format files.

.. _Heatmapper: http://www.ks.uiuc.edu/Research/vmd/plugins/heatmapper/"""

__author__ = 'Ahmet Bakan, Anindita Dutta'

from numpy import arange, fromstring, array

from prody.utilities import openFile, intorfloat, startswith, addext

from prody import LOGGER


__all__ = ['parseHeatmap', 'writeHeatmap', 'showHeatmap']

HMTYPES = {
    'max': float,
    'min': float,
    'numbering': lambda line: line.split(':'),
    'title': str,
    'xlabel': str,
    'ylabel': str,
    'xorigin': intorfloat,
    'xstep': intorfloat,
}


[docs]def showHeatmap(heatmap, *args, **kwargs): """Show *heatmap*, which can be an two dimensional array or a Heat Mapper :file:`.hm` file. Heatmap is plotted using :func:`~matplotlib.pyplot.imshow` function. Default values passed to this function are ``interpolation='nearest'``, ``aspect='auto'``, and ``origin='lower'``.""" try: ndim, shape = heatmap.ndim, heatmap.shape except AttributeError: heatmap, headers = parseHeatmap(heatmap) ndim, shape = heatmap.ndim, heatmap.shape xorigin = headers.pop('xorigin', 0) xextent = headers.pop('xstep', 1) * shape[0] ylabel = kwargs.get('ylabel', '').lower() indices = None if ylabel: for key in headers.get('numbering', []): if startswith(ylabel, key.lower()): indices = headers.get(key) if indices is not None: extent = [indices[0] - .5, indices[0] + len(indices) - .5, xorigin - .5, xextent - .5] else: extent = [-.5, shape[1] * 2 - .5, xorigin - .5, xextent - .5] kwargs.setdefault('extent', extent) for key in ['numbering', 'min', 'max'] + headers.get('numbering', []): headers.pop(key, None) headers.update(kwargs) kwargs = headers if ndim != 2: raise ValueError('mutinfo must be a 2D matrix') kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('origin', 'lower') kwargs.setdefault('aspect', 'auto') xlabel = kwargs.pop('xlabel', None) ylabel = kwargs.pop('ylabel', None) title = kwargs.pop('title', None) import matplotlib.pyplot as plt show = plt.imshow(heatmap, *args, **kwargs), plt.colorbar() if xlabel is not None: plt.xlabel(xlabel) if ylabel is not None: plt.ylabel(ylabel) if title is not None: plt.title(title) return show
[docs]def parseHeatmap(heatmap, **kwargs): """Returns a two dimensional array and a dictionary with information parsed from *heatmap*, which may be an input stream or an :file:`.hm` file in VMD plugin Heat Mapper format.""" try: readline, close = heatmap.readline, lambda: None except AttributeError: heatmap = openFile(heatmap) readline, close = heatmap.readline, heatmap.close meta = {} arrs = [] line = readline() while line: if line.startswith('-'): label, data = line[1:].split(None, 1) data = data.strip() if data[0] == data[-1] == '"': data = data[1:-1] label = label.strip() try: meta[label] = HMTYPES[label](data) except KeyError: LOGGER.warn('Unrecognized label encountered: {0}' .format(repr(label))) meta[label] = HMTYPES[label](data) except TypeError: LOGGER.warn('Could not parse data with label {0}.' .format(repr(label))) else: arrs.append(line.rstrip()) line = readline() close() nnums = len(meta.get('numbering', '')) heatmap = [] numbers = [] for arr in arrs: if nnums: items = arr.split(':', nnums + 1) numbers.append(items[:nnums]) else: items = [arr] heatmap.append(fromstring(items[-1], float, sep=';')) heatmap = array(heatmap) if nnums: numbering = meta['numbering'] try: numbers = array(numbers, int) except ValueError: try: numbers = array(numbers, float) except ValueError: LOGGER.warn('Numbering for y-axis could not be parsed.') numbering = [] for i, label in enumerate(numbering): meta[label] = numbers[:, i].copy() return heatmap, meta
[docs]def writeHeatmap(filename, heatmap, **kwargs): """Returns *filename* that contains *heatmap* in Heat Mapper :file:`.hm` file (extension is automatically added when not found). *filename* may also be an output stream. :arg title: title of the heatmap :type title: str :arg xlabel: x-axis lab, default is ``'unknown'`` :type xlabel: str :arg ylabel: y-axis lab, default is ``'unknown'`` :type ylabel: str :arg xorigin: x-axis origin, default is 0 :type xorigin: float :arg xstep: x-axis step, default is 1 :type xstep: float :arg min: minimum value, default is minimum in *heatmap* :type min: float :arg max: maximum value, default is maximum in *heatmap* :type max: float :arg format: number format, default is ``'%f'`` :type format: str Other keyword arguments that are arrays with length equal to the y-axis (second dimension of heatmap) will be considered as *numbering*.""" try: ndim, shape = heatmap.ndim, heatmap.shape except: raise TypeError('heatmap must be an array object') if ndim!= 2: raise TypeError('heatmap must be a 2D array') try: write, close, stream = filename.write, lambda: None, filename except AttributeError: out = openFile(addext(filename, '.hm'), 'w') write, close, stream = out.write, out.close, out format = kwargs.pop('format', '%f') write('-min "{0}"\n'.format(kwargs.pop('min', heatmap.min()))) write('-max "{0}"\n'.format(kwargs.pop('max', heatmap.max()))) for label, default in [ ('title', 'unknown'), ('xlabel', 'unknown'), ('xorigin', 0), ('xstep', 1), ('ylabel', 'unknown'), ]: write('-{0} "{1}"\n'.format(label, kwargs.pop(label, default))) numbering = [] numlabels = [] for key, val in kwargs.items(): try: length = len(val) except TypeError: LOGGER.warn('Keyword argument {0} is not used.'.format(key)) else: if length == shape[0]: numlabels.append(key) numbering.append(val) if not numbering: numlabels.append('unknown') numbering.append(arange(1, shape[0] + 1)) write('-numbering "{0}"\n'.format(':'.join(numlabels))) for i, row in enumerate(heatmap): write(':'.join(str(nums[i]) for nums in numbering) + ':') row.tofile(stream, sep=';', format=format) write(';\n') close() return filename