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
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.iteritems(): new_collection[k] = result[k] if ID is not None: new_collection.ID = ID return new_collection else: # Return a dictionary return result
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)
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)
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.iterkeys()} if ID is None: ID = self.ID return self.filter(fil, applyto=applyto, ID=ID)
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.iterkeys()} if ID is None: ID = self.ID + '.filtered_by_cols' return self.filter(fil, applyto=applyto, ID=ID)
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.validiate_input()
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()
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
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)
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.iteritems(): 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)
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.iteritems(): 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)
def set_visible(self, visible=True): for artist in to_list(self.artist): artist.set_visible(visible)
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 explicitely 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'") 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
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)
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
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 explicitely 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'") 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
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
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.iteritems(): new_collection[k] = result[k] if ID is not None: new_collection.ID = ID return new_collection else: # Return a dictionary return result