Source code for cytoflow.views.parallel_coords

#!/usr/bin/env python3.4
# coding: latin-1

# (c) Massachusetts Institute of Technology 2015-2018
# (c) Brian Teague 2018-2021
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
cytoflow.views.parallel_coords
------------------------------
"""

from traits.api import provides, Constant

import matplotlib.pyplot as plt
import matplotlib.collections

import pandas as pd
import numpy as np

import cytoflow.utility as util
from .i_view import IView

from .base_views import BaseNDView

[docs]@provides(IView) class ParallelCoordinatesView(BaseNDView): """ Plots a parallel coordinates plot. PC plots are good for multivariate data; each vertical line represents one attribute, and one set of connected line segments represents one data point. Attributes ---------- Examples -------- Make a little data set. .. plot:: :context: close-figs >>> import cytoflow as flow >>> import_op = flow.ImportOp() >>> import_op.tubes = [flow.Tube(file = "Plate01/RFP_Well_A3.fcs", ... conditions = {'Dox' : 10.0}), ... flow.Tube(file = "Plate01/CFP_Well_A4.fcs", ... conditions = {'Dox' : 1.0})] >>> import_op.conditions = {'Dox' : 'float'} >>> ex = import_op.apply() Plot the PC plot. .. plot:: :context: close-figs >>> flow.ParallelCoordinatesView(channels = ['B1-A', 'V2-A', 'Y2-A', 'FSC-A'], ... scale = {'Y2-A' : 'log', ... 'V2-A' : 'log', ... 'B1-A' : 'log', ... 'FSC-A' : 'log'}, ... huefacet = 'Dox').plot(ex) """ id = Constant('edu.mit.synbio.cytoflow.view.parallel_coords') friend_id = Constant("Parallel Coordinates Plot")
[docs] def plot(self, experiment, **kwargs): """ Plot a faceted parallel coordinates plot Parameters ---------- alpha : float (default = 0.02) The alpha blending value, between 0 (transparent) and 1 (opaque). axvlines_kwds : dict A dictionary of parameters to pass to `ax.axvline <https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.axvline.html>`_ Notes ----- This uses a low-level API for speed; there are far fewer visual options that with other views. """ if len(self.channels) < 3: raise util.CytoflowViewError('channels', "Must have at least 3 channels") super().plot(experiment, **kwargs) # clean up the plot for ax in plt.gcf().get_axes(): ax.set_xlabel("") ax.set_ylabel("") ax.get_yaxis().set_ticks([])
def _grid_plot(self, experiment, grid, **kwargs): # xlim and ylim, xscale and yscale are the limits and scale of the # plane onto which we are projecting. the kwargs 'scale' and 'lim' # are the data scale and limits, respectively scale = kwargs.pop('scale') lim = kwargs.pop('lim') # TODO - some way to optimize attribute order # TODO - allow changing attribute spacing # memo to track if we've put annotations on an axes yet ax_annotations = {} grid.map(_parallel_coords_plot, *self.channels, ax_annotations = ax_annotations, scale = scale, lim = lim, **kwargs) return {}
def _parallel_coords_plot(*channels, ax_annotations, scale, lim, **kwargs): color = kwargs.pop('color') alpha = kwargs.pop('alpha', 0.02) aa = kwargs.pop('antialiased', True) color = tuple(list(color) + [alpha]) label = kwargs.pop('label', None) df = pd.DataFrame() for c in channels: vmin = lim[c.name][0] vmax = lim[c.name][1] c_scaled = pd.Series(data = scale[c.name].norm(vmin = vmin, vmax = vmax)(c.values), index = c.index, name = c.name) c_scaled[(c < vmin) | (c > vmax)] = np.nan df[c.name] = c_scaled df.dropna(axis = 0, how = 'any', inplace = True) # adapted from pandas.plotting._misc axvlines_kwds = kwargs.pop('axvlines_kwds', {'linewidth' : 1, 'color' : 'black'}) ax = plt.gca() # we're creating a LineCollection manually because it's much much much # faster than the higher-level plotting routines out = pd.Series() for i in range(len(df.columns) - 1): new_series = df.apply( lambda x: [(i, x[i]), (i + 1, x[i + 1])], axis = 1) out = out.append(new_series) lc = matplotlib.collections.LineCollection(out.values, colors = color, antialiaseds = aa) lc.set_label(label) ax.add_collection(lc) # have we already annotated these axes? if ax in ax_annotations: return ax_annotations[ax] = True x = np.arange(len(df.columns)) for i in x: ax.axvline(i, **axvlines_kwds) ax.set_xticks(x) ax.set_xticklabels(df.columns) ax.set_xlim(x[0], x[-1]) ax.grid() util.expand_class_attributes(ParallelCoordinatesView) util.expand_method_parameters(ParallelCoordinatesView, ParallelCoordinatesView.plot)