コード例 #1
0
    def plot(self,
             channel_names,
             kind='histogram',
             gates=None,
             gate_colors=None,
             gate_lw=1,
             **kwargs):
        """Plot the flow cytometry data associated with the sample on the current axis.

        To produce the plot, follow up with a call to matplotlib's show() function.

        Parameters
        ----------
        {graph_plotFCM_pars}
        {FCMeasurement_plot_pars}
        {common_plot_ax}
        gates : [None, Gate, list of Gate]
            Gate must be of type {_gate_available_classes}.
        gate_lw: float | iterable
            line width to use when drawing gates
            if float, uses the same line width for all gates
            if iterable, then cycles between the values
        kwargs : dict
            Additional keyword arguments to be passed to graph.plotFCM

        Returns
        -------
        None : if no data is present
        plot_output : output of plot command used to draw (e.g., output of hist)

        Examples
        --------
        >>> sample.plot('Y2-A', bins=100, alpha=0.7, color='green', normed=1) # 1d histogram
        >>> sample.plot(['B1-A', 'Y2-A'], cmap=cm.Oranges, colorbar=False) # 2d histogram
        """
        ax = kwargs.get('ax')

        channel_names = to_list(channel_names)
        gates = to_list(gates)

        plot_output = graph.plotFCM(self.data,
                                    channel_names,
                                    kind=kind,
                                    **kwargs)

        if gates is not None:
            if gate_colors is None:
                gate_colors = cycle(('b', 'g', 'r', 'm', 'c', 'y'))

            if not isinstance(gate_lw, collections.Iterable):
                gate_lw = [gate_lw]

            gate_lw = cycle(gate_lw)

            for (g, c, lw) in zip(gates, gate_colors, gate_lw):
                g.plot(ax=ax, ax_channels=channel_names, color=c, lw=lw)

        return plot_output
コード例 #2
0
 def get_meta_fields(self, fields, kwargs={}):
     '''
     Return a dictionary of metadata fields
     '''
     fields = to_list(fields)
     meta = self.get_meta()
     return {field: meta.get(field) for field in fields}
コード例 #3
0
    def apply(self, func, ids=None, applyto='measurement', noneval=nan,
              setdata=False, output_format='dict', ID=None,
              **kwargs):
        '''
        Apply func to each of the specified measurements.

        Parameters
        ----------
        func : callable
            Accepts a Measurement object or a DataFrame.
        ids : hashable| iterable of hashables | None
            Keys of measurements to which func will be applied.
            If None is given apply to all measurements.
        applyto :  'measurement' | 'data'
            * 'measurement' : apply to measurements objects themselves.
            * 'data'        : apply to measurement associated data
        noneval : obj
            Value returned if applyto is 'data' but no data is available.
        setdata : bool
            Whether to set the data in the Measurement object.
            Used only if data is not already set.
        output_format : ['dict' | 'collection']
            * collection : keeps result as collection
            WARNING: For collection, func should return a copy of the measurement instance rather
            than the original measurement instance.
        Returns
        -------
        Dictionary keyed by measurement keys containing the corresponding output of func
        or returns a collection (if output_format='collection').
        '''
        if ids is None:
            ids = self.keys()
        else:
            ids = to_list(ids)
        result = dict((i, self[i].apply(func, applyto, noneval, setdata)) for i in ids)

        if output_format == 'collection':
            can_keep_as_collection = all(
                [isinstance(r, self._measurement_class) for r in result.values()])
            if not can_keep_as_collection:
                raise TypeError(
                    'Cannot turn output into a collection. The provided func must return results of type {}'.format(
                        self._measurement_class))

            new_collection = self.copy()
            # Locate IDs to remove
            ids_to_remove = [x for x in self.keys() if x not in ids]
            # Remove data for these IDs
            for ids in ids_to_remove:
                new_collection.pop(ids)
            # Update keys with new values
            for k, v in new_collection.items():
                new_collection[k] = result[k]
            if ID is not None:
                new_collection.ID = ID
            return new_collection
        else:
            # Return a dictionary
            return result
コード例 #4
0
    def add_callback(self, func):
        """ Registers a call back function """
        if func is None: return
        func_list = to_list(func)

        if not hasattr(self, 'callback_list'):
            self.callback_list = func_list
        else:
            self.callback_list.extend(func_list)
コード例 #5
0
 def filter_by_key(self, keys, ID=None):
     """
     Keep only Measurements with given keys.
     """
     keys = to_list(keys)
     fil = lambda x: x in keys
     if ID is None:
         ID = self.ID
     return self.filter(fil, applyto='keys', ID=ID)
コード例 #6
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
 def filter_by_key(self, keys, ID=None):
     """
     Keep only Measurements with given keys.
     """
     keys = to_list(keys)
     fil = lambda x: x in keys
     if ID is None:
         ID = self.ID
     return self.filter(fil, applyto='keys', ID=ID)
コード例 #7
0
    def add_callback(self, func):
        """ Registers a call back function """
        if func is None: return
        func_list = to_list(func)

        if not hasattr(self, 'callback_list'):
            self.callback_list = func_list
        else:
            self.callback_list.extend(func_list)
コード例 #8
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
 def filter_by_rows(self, rows, ID=None):
     """
     Keep only Measurements in corresponding rows.
     """
     rows = to_list(rows)
     fil = lambda x: x in rows
     applyto = {k: self._positions[k][0] for k in self.keys()}
     if ID is None:
         ID = self.ID
     return self.filter(fil, applyto=applyto, ID=ID)
コード例 #9
0
 def filter_by_cols(self, cols, ID=None):
     """
     Keep only Measurements in corresponding columns.
     """
     rows = to_list(cols)
     fil = lambda x: x in rows
     applyto = {k: self._positions[k][1] for k in self.keys()}
     if ID is None:
         ID = self.ID + '.filtered_by_cols'
     return self.filter(fil, applyto=applyto, ID=ID)
コード例 #10
0
 def filter_by_rows(self, rows, ID=None):
     """
     Keep only Measurements in corresponding rows.
     """
     rows = to_list(rows)
     fil = lambda x: x in rows
     applyto = {k: self._positions[k][0] for k in self.keys()}
     if ID is None:
         ID = self.ID
     return self.filter(fil, applyto=applyto, ID=ID)
コード例 #11
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
 def filter_by_cols(self, cols, ID=None):
     """
     Keep only Measurements in corresponding columns.
     """
     rows = to_list(cols)
     fil = lambda x: x in rows
     applyto = {k: self._positions[k][1] for k in self.keys()}
     if ID is None:
         ID = self.ID + '.filtered_by_cols'
     return self.filter(fil, applyto=applyto, ID=ID)
コード例 #12
0
    def __init__(self, vert, channels, region, name=None):
        self.vert = vert
        channels = to_list(channels)
        self.channels = channels

        if name == None:
            self.name = "Unnamed Gate {0}".format(Gate.unnamed_gate_num)
            Gate.unnamed_gate_num += 1
        else:
            self.name = name

        self.region = region
        self.validate_input()
コード例 #13
0
ファイル: gates.py プロジェクト: eyurtsev/FlowCytometryTools
    def __init__(self, vert, channels, region, name=None):
        self.vert = vert
        channels = to_list(channels)
        self.channels = channels

        if name == None:
            self.name = "Unnamed Gate {0}".format(Gate.unnamed_gate_num)
            Gate.unnamed_gate_num += 1
        else:
            self.name = name

        self.region = region
        self.validate_input()
コード例 #14
0
    def get_measurement_metadata(self,
                                 fields,
                                 ids=None,
                                 noneval=nan,
                                 output_format='DataFrame'):
        """
        Get the metadata fields of specified measurements (all if None given).

        Parameters
        ----------
        fields : str | iterable of str
            Names of metadata fields to be returned.
        ids : hashable| iterable of hashables | None
            Keys of measurements for which metadata will be returned.
            If None is given return metadata of all measurements.
        noneval : obj
            Value returned if applyto is 'data' but no data is available.
        output_format :  'DataFrame' | 'dict'
            'DataFrame' : return DataFrame,
            'dict'      : return dictionary.

        Returns
        -------
        Measurement metadata in specified output_format.
        """
        fields = to_list(fields)
        func = lambda x: x.get_meta_fields(fields)
        meta_d = self.apply(func,
                            ids=ids,
                            applyto='measurement',
                            noneval=noneval,
                            output_format='dict')
        if output_format == 'dict':
            return meta_d
        elif output_format == 'DataFrame':
            from pandas import DataFrame as DF
            meta_df = DF(meta_d, index=fields)
            return meta_df
        else:
            msg = ("The output_format must be either 'dict' or 'DataFrame'. " +
                   "Encountered unsupported value %s." % repr(output_format))
            raise Exception(msg)
コード例 #15
0
    def set_positions(self, positions=None, position_mapper='name', ids=None):
        """
        checks for position validity & collisions,
        but not that all measurements are assigned.

        Parameters
        -----------
        positions : is dict-like of measurement_key:(row,col)
        parser :
            callable - gets key and returns position
            mapping  - key:pos
            'name'   - parses things like 'A1', 'G12'
            'number' - converts number to positions, going over rows first.
        ids :
            parser will be applied to specified ids only.
            If None is given, parser will be applied to all measurements.

        TODO: output a more informative message for position collisions
        """
        if positions is None:
            if ids is None:
                ids = self.keys()
            else:
                ids = to_list(ids)
            mapper = self._get_ID2position_mapper(position_mapper)
            positions = dict((ID, mapper(ID)) for ID in ids)
        else:
            pass
        # check that resulting assignment is unique (one measurement per position)
        temp = self._positions.copy()
        temp.update(positions)
        if not len(temp.values()) == len(set(temp.values())):
            msg = 'A position can only be occupied by a single measurement'
            raise Exception(msg)

        for k, pos in positions.items():
            if not self._is_valid_position(pos):
                msg = 'Position {} is not supported for this collection'.format(
                    pos)
                raise ValueError(msg)
            self._positions[k] = pos
            self[k]._set_position(self.ID, pos)
コード例 #16
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
    def set_positions(self, positions=None, position_mapper='name', ids=None):
        '''
        checks for position validity & collisions,
        but not that all measurements are assigned.

        Parameters
        -----------
        positions : is dict-like of measurement_key:(row,col)
        parser :
            callable - gets key and returns position
            mapping  - key:pos
            'name'   - parses things like 'A1', 'G12'
            'number' - converts number to positions, going over rows first.
        ids :
            parser will be applied to specified ids only.
            If None is given, parser will be applied to all measurements.

        TODO: output a more informative message for position collisions
        '''
        if positions is None:
            if ids is None:
                ids = self.keys()
            else:
                ids = to_list(ids)
            mapper = self._get_ID2position_mapper(position_mapper)
            positions = dict((ID, mapper(ID)) for ID in ids)
        else:
            pass
        # check that resulting assignment is unique (one measurement per position)
        temp = self._positions.copy()
        temp.update(positions)
        if not len(temp.values()) == len(set(temp.values())):
            msg = 'A position can only be occupied by a single measurement'
            raise Exception(msg)

        for k, pos in positions.items():
            if not self._is_valid_position(pos):
                msg = 'Position {} is not supported for this collection'.format(pos)
                raise ValueError(msg)
            self._positions[k] = pos
            self[k]._set_position(self.ID, pos)
コード例 #17
0
ファイル: gates.py プロジェクト: eyurtsev/FlowCytometryTools
 def _find_orientation(self, ax_channels):
     ax_channels = to_list(ax_channels)
     c = self.channels[0]
     if ax_channels is not None:
         try:
             i = ax_channels.index(c)
             if i == 0:
                 flip = False
             else:
                 flip = True
         except ValueError:
             raise Exception("""Trying to plot gate that is defined on channel {0},
                             but figure axis correspond to channels {1}""".format(c,
                                                                                  ax_channels))
     if len(self.channels) == 2:
         c = self.channels[1]
         if c not in ax_channels:
             raise Exception("""Trying to plot gate that is defined on channel {0},
                             but figure axis correspond to channels {1}""".format(c,
                                                                                  ax_channels))
     return flip
コード例 #18
0
def transform_frame(frame, transform, columns=None, direction='forward',
                    return_all=True, args=(), **kwargs):
    """
    Apply transform to specified columns.

    direction: 'forward' | 'inverse'
    return_all: bool
        True -  return all columns, with specified ones transformed.
        False - return only specified columns.

    .. warning:: deprecated
    """
    tfun, tname = parse_transform(transform, direction)
    columns = to_list(columns)
    if columns is None:
        columns = frame.columns
    if return_all:
        transformed = frame.copy()
        for c in columns:
            transformed[c] = tfun(frame[c], *args, **kwargs)
    else:
        transformed = frame.filter(columns).apply(tfun, *args, **kwargs)
    return transformed
コード例 #19
0
 def _find_orientation(self, ax_channels):
     ax_channels = to_list(ax_channels)
     c = self.channels[0]
     if ax_channels is not None:
         try:
             i = ax_channels.index(c)
             if i == 0:
                 flip = False
             else:
                 flip = True
         except ValueError:
             raise Exception(
                 """Trying to plot gate that is defined on channel {0},
                             but figure axis correspond to channels {1}""".
                 format(c, ax_channels))
     if len(self.channels) == 2:
         c = self.channels[1]
         if c not in ax_channels:
             raise Exception(
                 """Trying to plot gate that is defined on channel {0},
                             but figure axis correspond to channels {1}""".
                 format(c, ax_channels))
     return flip
コード例 #20
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
    def get_measurement_metadata(self, fields, ids=None, noneval=nan,
                                 output_format='DataFrame'):
        """
        Get the metadata fields of specified measurements (all if None given).

        Parameters
        ----------
        fields : str | iterable of str
            Names of metadata fields to be returned.
        ids : hashable| iterable of hashables | None
            Keys of measurements for which metadata will be returned.
            If None is given return metadata of all measurements.
        noneval : obj
            Value returned if applyto is 'data' but no data is available.
        output_format :  'DataFrame' | 'dict'
            'DataFrame' : return DataFrame,
            'dict'      : return dictionary.

        Returns
        -------
        Measurement metadata in specified output_format.
        """
        fields = to_list(fields)
        func = lambda x: x.get_meta_fields(fields)
        meta_d = self.apply(func, ids=ids, applyto='measurement',
                            noneval=noneval, output_format='dict')
        if output_format is 'dict':
            return meta_d
        elif output_format is 'DataFrame':
            from pandas import DataFrame as DF
            meta_df = DF(meta_d, index=fields)
            return meta_df
        else:
            msg = ("The output_format must be either 'dict' or 'DataFrame'. " +
                   "Encountered unsupported value %s." % repr(output_format))
            raise Exception(msg)
コード例 #21
0
def plotFCM(data,
            channel_names,
            kind='histogram',
            ax=None,
            autolabel=True,
            xlabel_kwargs={},
            ylabel_kwargs={},
            colorbar=False,
            grid=False,
            **kwargs):
    """
    Plots the sample on the current axis.

    Follow with a call to matplotlibs show() in order to see the plot.

    Parameters
    ----------
    data : DataFrame
    {graph_plotFCM_pars}
    {common_plot_ax}

    Returns
    -------
    The output of the plot command used
    """

    if ax == None: ax = pl.gca()

    xlabel_kwargs.setdefault('size', 16)
    ylabel_kwargs.setdefault('size', 16)

    channel_names = to_list(channel_names)

    if len(channel_names) == 1:
        # 1D so histogram plot
        kwargs.setdefault('color', 'gray')
        kwargs.setdefault('histtype', 'stepfilled')
        kwargs.setdefault('bins', 200)  # Do not move above

        x = data[channel_names[0]].values
        if len(x) >= 1:
            if (len(x) == 1) and isinstance(kwargs['bins'], int):
                # Only needed for hist (not hist2d) due to hist function doing
                # excessive input checking
                warnings.warn(
                    "One of the data sets only has a single event. "
                    "This event won't be plotted unless the bin locations"
                    " are explicitly provided to the plotting function. ")
                return None
            plot_output = ax.hist(x, **kwargs)
        else:
            return None

    elif len(channel_names) == 2:
        x = data[channel_names[0]].values  # value of first channel
        y = data[channel_names[1]].values  # value of second channel

        if len(x) == 0:
            # Don't draw a plot if there's no data
            return None
        if kind == 'scatter':
            kwargs.setdefault('edgecolor', 'none')
            plot_output = ax.scatter(x, y, **kwargs)
        elif kind == 'histogram':
            kwargs.setdefault('bins', 200)  # Do not move above
            kwargs.setdefault('cmin', 1)
            kwargs.setdefault('cmap', pl.cm.copper)
            kwargs.setdefault('norm', matplotlib.colors.LogNorm())
            plot_output = ax.hist2d(x, y, **kwargs)
            mappable = plot_output[-1]

            if colorbar:
                pl.colorbar(mappable, ax=ax)
        else:
            raise ValueError(
                "Not a valid plot type. Must be 'scatter', 'histogram'")
    else:
        raise ValueError(
            'Received an unexpected number of channels: "{}"'.format(
                channel_names))

    pl.grid(grid)

    if autolabel:
        y_label_text = 'Counts' if len(
            channel_names) == 1 else channel_names[1]
        ax.set_xlabel(channel_names[0], **xlabel_kwargs)
        ax.set_ylabel(y_label_text, **ylabel_kwargs)

    return plot_output
コード例 #22
0
ファイル: bases.py プロジェクト: eyurtsev/FlowCytometryTools
    def grid_plot(self, func, applyto='measurement', ids=None,
                  row_labels=None, col_labels=None,
                  xlim='auto', ylim='auto',
                  xlabel=None, ylabel=None,
                  colorbar=True,
                  row_label_xoffset=None, col_label_yoffset=None,
                  hide_tick_labels=True, hide_tick_lines=True,
                  hspace=0, wspace=0,
                  row_labels_kwargs={}, col_labels_kwargs={}):
        """
        Creates subplots for each well in the plate. Uses func to plot on each axis.
        Follow with a call to matplotlibs show() in order to see the plot.

        Parameters
        ----------
        func : callable
            func is a callable that accepts a measurement
            object (with an optional axis reference) and plots on the current axis.
            Return values from func are ignored.
            .. note: if using applyto='measurement', the function
            when querying for data should make sure that the data
            actually exists
        applyto : 'measurement' | 'data'
        {_graph_grid_layout}
        {bases_OrderedCollection_grid_plot_pars}

        Returns
        -------
        {_graph_grid_layout_returns}

        Examples
        ---------
        >>> def y(well, ax):
        >>>     data = well.get_data()
        >>>     if data is None:
        >>>         return None
        >>>     graph.plotFCM(data, 'Y2-A')
        >>> def z(data, ax):
        >>>     plot(data[0:100, 1], data[0:100, 2])
        >>> plate.plot(y, applyto='measurement');
        >>> plate.plot(z, applyto='data');
        """
        # Acquire call arguments to be passed to create plate layout
        callArgs = locals().copy()  # This statement must remain first. The copy is just defensive.
        [callArgs.pop(varname) for varname in
         ['self', 'func', 'applyto', 'ids', 'colorbar', 'xlim', 'ylim']]  # pop args

        callArgs['rowNum'] = self.shape[0]
        callArgs['colNum'] = self.shape[1]

        subplots_adjust_args = {}
        subplots_adjust_args.setdefault('right', 0.85)
        subplots_adjust_args.setdefault('top', 0.85)
        pl.subplots_adjust(**subplots_adjust_args)

        # Uses plate default row/col labels if user does not override them by specifying row/col
        # labels
        if row_labels == None: callArgs['row_labels'] = self.row_labels
        if col_labels == None: callArgs['col_labels'] = self.col_labels

        ax_main, ax_subplots = graph.create_grid_layout(**callArgs)
        subplots_ax = DF(ax_subplots, index=self.row_labels, columns=self.col_labels)

        if ids is None:
            ids = self.keys()
        ids = to_list(ids)

        for ID in ids:
            measurement = self[ID]
            if not hasattr(measurement, 'data'):
                continue

            row, col = self._positions[ID]
            ax = subplots_ax[col][row]
            pl.sca(ax)  # sets the current axis

            if applyto == 'measurement':
                func(measurement, ax)  # reminder: pandas row/col order is reversed
            elif applyto == 'data':
                data = measurement.get_data()
                if data is not None:
                    if func.func_code.co_argcount == 1:
                        func(data)
                    else:
                        func(data, ax)
            else:
                raise ValueError('Encountered unsupported value {} for applyto parameter.'.format(
                    applyto))

        # Autoscaling axes
        graph.scale_subplots(ax_subplots, xlim=xlim, ylim=ylim)

        #####
        # Placing ticks on the top left subplot
        ax_label = ax_subplots[0, -1]
        pl.sca(ax_label)

        if xlabel:
            xlim = ax_label.get_xlim()
            pl.xticks([xlim[0], xlim[1]], rotation=90)

        if ylabel:
            ylim = ax_label.get_ylim()
            pl.yticks([ylim[0], ylim[1]], rotation=0)

        pl.sca(ax_main)  # sets to the main axis -- more intuitive

        return ax_main, ax_subplots
コード例 #23
0
 def create_artist(self):
     self.poly = pl.Polygon(self.coordinates, color='k', fill=False)
     self.artist_list = to_list(self.poly)
     self.ax.add_artist(self.poly)
コード例 #24
0
 def set_visible(self, visible=True):
     for artist in to_list(self.artist):
         artist.set_visible(visible)
コード例 #25
0
 def create_artist(self):
     self.poly = pl.Polygon(self.coordinates, color='k', fill=False)
     self.artist_list = to_list(self.poly)
     self.ax.add_artist(self.poly)
コード例 #26
0
 def set_visible(self, visible=True):
     for artist in to_list(self.artist):
         artist.set_visible(visible)
コード例 #27
0
    def transform(self, transform, direction='forward',
                  channels=None, return_all=True, auto_range=True,
                  use_spln=True, get_transformer=False, ID=None,
                  apply_now=True,
                  args=(), **kwargs):
        """
        Applies a transformation to the specified channels.

        The transformation parameters are shared between all transformed channels.
        If different parameters need to be applied to different channels,
        use several calls to `transform`.

        Parameters
        ----------
        {FCMeasurement_transform_pars}
        ID : hashable | None
            ID for the resulting collection. If None is passed, the original ID is used.

        Returns
        -------
        new : FCMeasurement
            New measurement containing the transformed data.
        transformer : Transformation
            The Transformation applied to the input measurement.
            Only returned if get_transformer=True.

        Examples
        --------
        {FCMeasurement_transform_examples}
        """
        # Create new measurement
        new = self.copy()
        data = new.data

        channels = to_list(channels)
        if channels is None:
            channels = data.columns
        ## create transformer
        if isinstance(transform, Transformation):
            transformer = transform
        else:
            if auto_range:  # determine transformation range
                if 'd' in kwargs:
                    warnings.warn(
                        'Encountered both auto_range=True and user-specified range value in '
                        'parameter d.\n Range value specified in parameter d is used.')
                else:
                    channel_meta = self.channels
                    # the -1 below because the channel numbers begin from 1 instead of 0
                    # (this is fragile code)
                    ranges = [float(r['$PnR']) for i, r in channel_meta.iterrows() if
                              self.channel_names[i - 1] in channels]
                    if not np.allclose(ranges, ranges[0]):
                        raise Exception("""Not all specified channels have the same data range,
                            therefore they cannot be transformed together.\n
                            HINT: Try transforming one channel at a time.
                            You'll need to provide the name of the channel in the transform.""")

                    if transform in {'hlog', 'tlog', 'hlog_inv', 'tlog_inv'}:
                        # Hacky fix to make sure that 'd' is provided only
                        # for hlog / tlog transformations
                        kwargs['d'] = np.log10(ranges[0])
            transformer = Transformation(transform, direction, args, **kwargs)
        ## create new data
        transformed = transformer(data[channels], use_spln)
        if return_all:
            new_data = data
        else:
            new_data = data.filter(channels)
        new_data[channels] = transformed
        ## update new Measurement
        new.data = new_data

        if ID is not None:
            new.ID = ID
        if get_transformer:
            return new, transformer
        else:
            return new
コード例 #28
0
    def transform(self, transform, direction='forward', share_transform=True,
                  channels=None, return_all=True, auto_range=True,
                  use_spln=True, get_transformer=False, ID=None,
                  apply_now=True,
                  args=(), **kwargs):
        '''
        Apply transform to each Measurement in the Collection.

        Return a new Collection with transformed data.

        {_containers_held_in_memory_warning}

        Parameters
        ----------
        {FCMeasurement_transform_pars}
        ID : hashable | None
            ID for the resulting collection. If None is passed, the original ID is used.

        Returns
        -------
        new : FCCollection
            New collection containing the transformed measurements.
        transformer : Transformation
            The Transformation applied to the measurements.
            Only returned if get_transformer=True & share_transform=True.

        Examples
        --------
        {FCMeasurement_transform_examples}
        '''
        new = self.copy()
        if share_transform:

            channel_meta = list(self.values())[0].channels
            channel_names = list(self.values())[0].channel_names
            if channels is None:
                channels = list(channel_names)
            else:
                channels = to_list(channels)
            ## create transformer
            if isinstance(transform, Transformation):
                transformer = transform
            else:
                if auto_range:  # determine transformation range
                    if 'd' in kwargs:
                        warnings.warn('Encountered both auto_range=True and user-specified range '
                                      'value in parameter d.\n '
                                      'Range value specified in parameter d is used.')
                    else:
                        # the -1 below because the channel numbers begin from 1 instead of 0 (this is fragile code)
                        ranges = [float(r['$PnR']) for i, r in channel_meta.iterrows() if
                                  channel_names[i - 1] in channels]

                        if not np.allclose(ranges, ranges[0]):
                            raise Exception('Not all specified channels have the same '
                                            'data range, therefore they cannot be '
                                            'transformed together.')

                        if transform in {'hlog', 'tlog', 'hlog_inv', 'tlog_inv'}:
                            # Hacky fix to make sure that 'd' is provided only
                            # for hlog / tlog transformations
                            kwargs['d'] = np.log10(ranges[0])
                transformer = Transformation(transform, direction, args, **kwargs)
                if use_spln:
                    xmax = self.apply(lambda x: x[channels].max().max(), applyto='data').max().max()
                    xmin = self.apply(lambda x: x[channels].min().min(), applyto='data').min().min()
                    transformer.set_spline(xmin, xmax)
            ## transform all measurements
            for k, v in new.items():
                new[k] = v.transform(transformer, channels=channels, return_all=return_all,
                                     use_spln=use_spln, apply_now=apply_now)
        else:
            for k, v in new.items():
                new[k] = v.transform(transform, direction=direction, channels=channels,
                                     return_all=return_all, auto_range=auto_range,
                                     get_transformer=False,
                                     use_spln=use_spln, apply_now=apply_now, args=args, **kwargs)
        if ID is not None:
            new.ID = ID
        if share_transform and get_transformer:
            return new, transformer
        else:
            return new
コード例 #29
0
    def grid_plot(self,
                  func,
                  applyto='measurement',
                  ids=None,
                  row_labels=None,
                  col_labels=None,
                  xlim='auto',
                  ylim='auto',
                  xlabel=None,
                  ylabel=None,
                  colorbar=True,
                  row_label_xoffset=None,
                  col_label_yoffset=None,
                  hide_tick_labels=True,
                  hide_tick_lines=True,
                  hspace=0,
                  wspace=0,
                  row_labels_kwargs={},
                  col_labels_kwargs={}):
        """
        Creates subplots for each well in the plate. Uses func to plot on each axis.
        Follow with a call to matplotlibs show() in order to see the plot.

        Parameters
        ----------
        func : callable
            func is a callable that accepts a measurement
            object (with an optional axis reference) and plots on the current axis.
            Return values from func are ignored.
            .. note: if using applyto='measurement', the function
            when querying for data should make sure that the data
            actually exists
        applyto : 'measurement' | 'data'
        {_graph_grid_layout}
        {bases_OrderedCollection_grid_plot_pars}

        Returns
        -------
        {_graph_grid_layout_returns}

        Examples
        ---------
        >>> def y(well, ax):
        >>>     data = well.get_data()
        >>>     if data is None:
        >>>         return None
        >>>     graph.plotFCM(data, 'Y2-A')
        >>> def z(data, ax):
        >>>     plot(data[0:100, 1], data[0:100, 2])
        >>> plate.plot(y, applyto='measurement');
        >>> plate.plot(z, applyto='data');
        """
        # Acquire call arguments to be passed to create plate layout
        callArgs = locals().copy(
        )  # This statement must remain first. The copy is just defensive.
        [
            callArgs.pop(varname) for varname in
            ['self', 'func', 'applyto', 'ids', 'colorbar', 'xlim', 'ylim']
        ]  # pop args

        callArgs['rowNum'] = self.shape[0]
        callArgs['colNum'] = self.shape[1]

        subplots_adjust_args = {}
        subplots_adjust_args.setdefault('right', 0.85)
        subplots_adjust_args.setdefault('top', 0.85)
        pl.subplots_adjust(**subplots_adjust_args)

        # Uses plate default row/col labels if user does not override them by specifying row/col
        # labels
        if row_labels == None: callArgs['row_labels'] = self.row_labels
        if col_labels == None: callArgs['col_labels'] = self.col_labels

        ax_main, ax_subplots = graph.create_grid_layout(**callArgs)
        subplots_ax = DF(ax_subplots,
                         index=self.row_labels,
                         columns=self.col_labels)

        if ids is None:
            ids = self.keys()
        ids = to_list(ids)

        for ID in ids:
            measurement = self[ID]
            if not hasattr(measurement, 'data'):
                continue

            row, col = self._positions[ID]
            ax = subplots_ax[col][row]
            pl.sca(ax)  # sets the current axis

            if applyto == 'measurement':
                func(measurement,
                     ax)  # reminder: pandas row/col order is reversed
            elif applyto == 'data':
                data = measurement.get_data()
                if data is not None:
                    if func.func_code.co_argcount == 1:
                        func(data)
                    else:
                        func(data, ax)
            else:
                raise ValueError(
                    'Encountered unsupported value {} for applyto parameter.'.
                    format(applyto))

        # Autoscaling axes
        graph.scale_subplots(ax_subplots, xlim=xlim, ylim=ylim)

        #####
        # Placing ticks on the top left subplot
        ax_label = ax_subplots[0, -1]
        pl.sca(ax_label)

        if xlabel:
            xlim = ax_label.get_xlim()
            pl.xticks([xlim[0], xlim[1]], rotation=90)

        if ylabel:
            ylim = ax_label.get_ylim()
            pl.yticks([ylim[0], ylim[1]], rotation=0)

        pl.sca(ax_main)  # sets to the main axis -- more intuitive

        return ax_main, ax_subplots
コード例 #30
0
ファイル: graph.py プロジェクト: eyurtsev/FlowCytometryTools
def plotFCM(data, channel_names, kind='histogram', ax=None,
            autolabel=True, xlabel_kwargs={}, ylabel_kwargs={},
            colorbar=False, grid=False,
            **kwargs):
    """
    Plots the sample on the current axis.

    Follow with a call to matplotlibs show() in order to see the plot.

    Parameters
    ----------
    data : DataFrame
    {graph_plotFCM_pars}
    {common_plot_ax}

    Returns
    -------
    The output of the plot command used
    """

    if ax == None: ax = pl.gca()

    xlabel_kwargs.setdefault('size', 16)
    ylabel_kwargs.setdefault('size', 16)

    channel_names = to_list(channel_names)

    if len(channel_names) == 1:
        # 1D so histogram plot
        kwargs.setdefault('color', 'gray')
        kwargs.setdefault('histtype', 'stepfilled')
        kwargs.setdefault('bins', 200)  # Do not move above

        x = data[channel_names[0]].values
        if len(x) >= 1:
            if (len(x) == 1) and isinstance(kwargs['bins'], int):
                # Only needed for hist (not hist2d) due to hist function doing
                # excessive input checking
                warnings.warn("One of the data sets only has a single event. "
                              "This event won't be plotted unless the bin locations"
                              " are explicitly provided to the plotting function. ")
                return None
            plot_output = ax.hist(x, **kwargs)
        else:
            return None

    elif len(channel_names) == 2:
        x = data[channel_names[0]].values  # value of first channel
        y = data[channel_names[1]].values  # value of second channel

        if len(x) == 0:
            # Don't draw a plot if there's no data
            return None
        if kind == 'scatter':
            kwargs.setdefault('edgecolor', 'none')
            plot_output = ax.scatter(x, y, **kwargs)
        elif kind == 'histogram':
            kwargs.setdefault('bins', 200)  # Do not move above
            kwargs.setdefault('cmin', 1)
            kwargs.setdefault('cmap', pl.cm.copper)
            kwargs.setdefault('norm', matplotlib.colors.LogNorm())
            plot_output = ax.hist2d(x, y, **kwargs)
            mappable = plot_output[-1]

            if colorbar:
                pl.colorbar(mappable, ax=ax)
        else:
            raise ValueError("Not a valid plot type. Must be 'scatter', 'histogram'")
    else:
        raise ValueError('Received an unexpected number of channels: "{}"'.format(channel_names))

    pl.grid(grid)

    if autolabel:
        y_label_text = 'Counts' if len(channel_names) == 1 else channel_names[1]
        ax.set_xlabel(channel_names[0], **xlabel_kwargs)
        ax.set_ylabel(y_label_text, **ylabel_kwargs)

    return plot_output
コード例 #31
0
    def plot(self, channel_names, kind='histogram',
             gates=None, gate_colors=None,
             ids=None, row_labels=None, col_labels=None,
             xlim='auto', ylim='auto',
             autolabel=True,
             **kwargs):
        """
        Produces a grid plot with each subplot corresponding to the data at the given position.

        Parameters
        ---------------
        {FCMeasurement_plot_pars}
        {graph_plotFCM_pars}
        {_graph_grid_layout}

        Returns
        -------
        {_graph_grid_layout_returns}

        Examples
        --------

        Below, plate is an instance of FCOrderedCollection

        >>> plate.plot(['SSC-A', 'FSC-A'], kind='histogram', autolabel=True)
        >>> plate.plot(['SSC-A', 'FSC-A'], xlim=(0, 10000))
        >>> plate.plot(['B1-A', 'Y2-A'], kind='scatter', color='red', s=1, alpha=0.3)
        >>> plate.plot(['B1-A', 'Y2-A'], bins=100, alpha=0.3)
        >>> plate.plot(['B1-A', 'Y2-A'], bins=[linspace(-1000, 10000, 100), linspace(-1000, 10000, 100)], alpha=0.3)

        .. note::

            For more details see documentation for FCMeasurement.plot
            **kwargs passes arguments to both grid_plot and to FCMeasurement.plot.
        """
        ##
        # Note
        # -------
        # The function assumes that grid_plot and FCMeasurement.plot use unique key words.
        # Any key word arguments that appear in both functions are passed only to grid_plot in the end.

        ##
        # Automatically figure out which of the kwargs should
        # be sent to grid_plot instead of two sample.plot
        # (May not be a robust solution, we'll see as the code evolves

        grid_arg_list = inspect.getargspec(OrderedCollection.grid_plot).args

        grid_plot_kwargs = {'ids': ids,
                            'row_labels': row_labels,
                            'col_labels': col_labels}

        for key, value in list(kwargs.items()):
            if key in grid_arg_list:
                kwargs.pop(key)
                grid_plot_kwargs[key] = value

        ##
        # Make sure channel names is a list to make the code simpler below
        channel_names = to_list(channel_names)

        ##
        # Determine data limits for binning
        #

        if kind == 'histogram':
            nbins = kwargs.get('bins', 200)

            if isinstance(nbins, int):
                min_list = []
                max_list = []
                for sample in self:
                    min_list.append(self[sample].data[channel_names].min().values)
                    max_list.append(self[sample].data[channel_names].max().values)

                min_list = list(zip(*min_list))
                max_list = list(zip(*max_list))

                bins = []

                for i, c in enumerate(channel_names):
                    min_v = min(min_list[i])
                    max_v = max(max_list[i])
                    bins.append(np.linspace(min_v, max_v, nbins))

                # Check if 1d
                if len(channel_names) == 1:
                    bins = bins[0]  # bins should be an ndarray, not a list of ndarrays

                kwargs['bins'] = bins

        ##########
        # Defining the plotting function that will be used.
        # At the moment grid_plot handles the labeling
        # (rather than sample.plot or the base function
        # in GoreUtilities.graph

        def plot_sample(sample, ax):
            return sample.plot(channel_names, ax=ax,
                               gates=gates, gate_colors=gate_colors,
                               colorbar=False,
                               kind=kind, autolabel=False, **kwargs)

        xlabel, ylabel = None, None

        if autolabel:
            cnames = to_list(channel_names)
            xlabel = cnames[0]
            if len(cnames) == 2:
                ylabel = cnames[1]

        return self.grid_plot(plot_sample, xlim=xlim, ylim=ylim,
                              xlabel=xlabel, ylabel=ylabel,
                              **grid_plot_kwargs)