Пример #1
0
 def update_limits(self, update_profile=True):
     with delay_callback(self, 'v_min', 'v_max'):
         if update_profile:
             self.update_profile(update_limits=False)
         if self._profile_cache is not None and len(self._profile_cache[1]) > 0:
             self.v_min = nanmin(self._profile_cache[1])
             self.v_max = nanmax(self._profile_cache[1])
Пример #2
0
 def update_limits(self, update_profile=True):
     with delay_callback(self, 'v_min', 'v_max'):
         if update_profile:
             self.update_profile(update_limits=False)
         if self._profile_cache is not None and len(self._profile_cache[1]) > 0:
             self.v_min = nanmin(self._profile_cache[1])
             self.v_max = nanmax(self._profile_cache[1])
Пример #3
0
def python_export_histogram_layer(layer, *args):

    if len(layer.mpl_artists) == 0 or not layer.enabled or not layer.visible:
        return [], None

    script = ""
    imports = ["import numpy as np"]

    x = layer.layer[layer._viewer_state.x_att]
    x_min = nanmin(x)
    x_max = nanmax(x)

    hist_x_min = layer._viewer_state.hist_x_min
    hist_x_max = layer._viewer_state.hist_x_max

    script += "# Get main data values\n"
    script += "x = layer_data['{0}']\n\n".format(
        layer._viewer_state.x_att.label)

    script += "# Set up histogram bins\n"
    script += "hist_n_bin = {0}\n".format(layer._viewer_state.hist_n_bin)

    if abs((x_min - hist_x_min) / (hist_x_max - hist_x_min)) < 0.001:
        script += "hist_x_min = np.nanmin(x)\n"
    else:
        script += "hist_x_min = {0}\n".format(hist_x_min)

    if abs((x_max - hist_x_max) / (hist_x_max - hist_x_min)) < 0.001:
        script += "hist_x_max = np.nanmax(x)\n"
    else:
        script += "hist_x_max = {0}\n".format(hist_x_max)

    options = dict(alpha=layer.state.alpha,
                   color=layer.state.color,
                   zorder=layer.state.zorder,
                   edgecolor='none')

    if layer._viewer_state.x_log:
        script += "bins = np.logspace(np.log10(hist_x_min), np.log10(hist_x_max), hist_n_bin)\n"
        options['bins'] = code('bins')
    else:
        options['range'] = code('[hist_x_min, hist_x_max]')
        options['bins'] = code('hist_n_bin')

    if layer._viewer_state.normalize:
        if MATPLOTLIB_GE_30:
            options['density'] = True
        else:
            options['normed'] = True

    if layer._viewer_state.cumulative:
        options['cumulative'] = True

    script += "\nx = x[(x >= hist_x_min) & (x <= hist_x_max)]\n"

    script += "\nax.hist(x, {0})".format(serialize_options(options))

    return imports, script.strip()
Пример #4
0
    def _calculate_profile(self):

        x, y = self.state.profile

        if x is None or y is None:
            self.disable_invalid_attributes(self._viewer_state.x_att)
            return
        else:
            self.enable()

        self._visible_data = x, y

        # Update the data values.
        if len(x) > 0:
            # Normalize profile values to the [0:1] range based on limits
            if self._viewer_state.normalize:
                y = self.state.normalize_values(y)
            self.plot_artist.set_data(x, y)
            self.plot_artist.set_visible(True)
        else:
            # We need to do this otherwise we get issues on Windows when
            # passing an empty list to plot_artist
            self.plot_artist.set_visible(False)

        if len(x) == 0:
            return

        # TODO: the following was copy/pasted from the histogram viewer, maybe
        # we can find a way to avoid duplication?

        # We have to do the following to make sure that we reset the y_max as
        # needed. We can't simply reset based on the maximum for this layer
        # because other layers might have other values, and we also can't do:
        #
        #   self._viewer_state.y_max = max(self._viewer_state.y_max, result[0].max())
        #
        # because this would never allow y_max to get smaller.

        if not self._viewer_state.normalize:

            self.state._y_min = nanmin(y)
            self.state._y_max = nanmax(y) * 1.2

            largest_y_max = max(
                getattr(layer, '_y_max', 0)
                for layer in self._viewer_state.layers)
            if largest_y_max != self._viewer_state.y_max:
                self._viewer_state.y_max = largest_y_max

            smallest_y_min = min(
                getattr(layer, '_y_min', np.inf)
                for layer in self._viewer_state.layers)
            if smallest_y_min != self._viewer_state.y_min:
                self._viewer_state.y_min = smallest_y_min

        self.redraw()
Пример #5
0
    def _calculate_profile_postthread(self):

        # It's possible for this method to get called but for the state to have
        # been updated in the mean time to have a histogram that raises an
        # exception (for example an IncompatibleAttribute). If any errors happen
        # here, we simply ignore them since _calculate_histogram_error will get
        # called directly.
        try:
            visible_data = self.state.profile
        except Exception:
            return

        if visible_data is None:
            return

        self.enable()

        x, y = visible_data

        # Update the data values.
        if len(x) > 0:
            self.state.update_limits()
            # Normalize profile values to the [0:1] range based on limits
            if self._viewer_state.normalize:
                y = self.state.normalize_values(y)
            with self.line_mark.hold_sync():
                self.line_mark.x = x
                self.line_mark.y = y
        else:
            with self.line_mark.hold_sync():
                self.line_mark.x = [0.]
                self.line_mark.y = [0.]

        if not self._viewer_state.normalize and len(y) > 0:

            y_min = nanmin(y)
            y_max = nanmax(y)
            y_range = y_max - y_min

            self.state._y_min = y_min - y_range * 0.1
            self.state._y_max = y_max + y_range * 0.1

            largest_y_max = max(
                getattr(layer, '_y_max', 0)
                for layer in self._viewer_state.layers)
            if largest_y_max != self._viewer_state.y_max:
                self._viewer_state.y_max = largest_y_max

            smallest_y_min = min(
                getattr(layer, '_y_min', np.inf)
                for layer in self._viewer_state.layers)
            if smallest_y_min != self._viewer_state.y_min:
                self._viewer_state.y_min = smallest_y_min

        self.redraw()
Пример #6
0
def python_export_histogram_layer(layer, *args):

    if len(layer.mpl_artists) == 0 or not layer.enabled or not layer.visible:
        return [], None

    script = ""
    imports = ["import numpy as np"]

    x = layer.layer[layer._viewer_state.x_att]
    x_min = nanmin(x)
    x_max = nanmax(x)

    hist_x_min = layer._viewer_state.hist_x_min
    hist_x_max = layer._viewer_state.hist_x_max

    script += "# Get main data values\n"
    script += "x = layer_data['{0}']\n\n".format(layer._viewer_state.x_att.label)

    script += "# Set up histogram bins\n"
    script += "hist_n_bin = {0}\n".format(layer._viewer_state.hist_n_bin)

    if abs((x_min - hist_x_min) / (hist_x_max - hist_x_min)) < 0.001:
        script += "hist_x_min = np.nanmin(x)\n"
    else:
        script += "hist_x_min = {0}\n".format(hist_x_min)

    if abs((x_max - hist_x_max) / (hist_x_max - hist_x_min)) < 0.001:
        script += "hist_x_max = np.nanmax(x)\n"
    else:
        script += "hist_x_max = {0}\n".format(hist_x_max)

    options = dict(alpha=layer.state.alpha,
                   color=layer.state.color,
                   zorder=layer.state.zorder,
                   edgecolor='none')

    if layer._viewer_state.x_log:
        script += "bins = np.logspace(np.log10(hist_x_min), np.log10(hist_x_max), hist_n_bin)\n"
        options['bins'] = code('bins')
    else:
        options['range'] = code('[hist_x_min, hist_x_max]')
        options['bins'] = code('hist_n_bin')

    if layer._viewer_state.normalize:
        options['normed'] = True

    if layer._viewer_state.cumulative:
        options['cumulative'] = True

    script += "\nx = x[(x >= hist_x_min) & (x <= hist_x_max)]\n"

    script += "\nax.hist(x, {0})".format(serialize_options(options))

    return imports, script.strip()
Пример #7
0
    def _set_limits(self):
        data = self.ui.component_selector.data
        cid = self.ui.component_selector.component

        vals = data[cid]

        wmin = self.ui.value_min
        wmax = self.ui.value_max

        wmin.setText(pretty_number(nanmin(vals)))
        wmax.setText(pretty_number(nanmax(vals)))
Пример #8
0
def visible_limits(artists, axis):
    """
    Determines the data limits for the data in a set of artists.

    Ignores non-visible artists

    Assumes each artist as a get_data method wich returns a tuple of x,y

    Returns a tuple of min, max for the requested axis, or None if no data
    present

    Parameters
    ----------
    artists : iterable
        An iterable collection of artists
    axis : int
        Which axis to compute. 0=xaxis, 1=yaxis
    """
    data = []
    for art in artists:
        if not art.visible:
            continue
        xy = art.get_data()
        assert isinstance(xy, tuple)
        val = xy[axis]
        if val.size > 0:
            data.append(xy[axis])

    if len(data) == 0:
        return
    data = np.hstack(data)
    if data.size == 0:
        return

    data = data[np.isfinite(data)]
    if data.size == 0:
        return

    lo, hi = nanmin(data), nanmax(data)
    if not np.isfinite(lo):
        return

    return lo, hi
Пример #9
0
def visible_limits(artists, axis):
    """
    Determines the data limits for the data in a set of artists.

    Ignores non-visible artists

    Assumes each artist as a get_data method which returns a tuple of x,y

    Returns a tuple of min, max for the requested axis, or None if no data
    present

    Parameters
    ----------
    artists : iterable
        An iterable collection of artists
    axis : int
        Which axis to compute. 0=xaxis, 1=yaxis
    """
    data = []
    for art in artists:
        if not art.visible:
            continue
        xy = art.get_data()
        assert isinstance(xy, tuple)
        val = xy[axis]
        if val.size > 0:
            data.append(xy[axis])

    if len(data) == 0:
        return
    data = np.hstack(data)
    if data.size == 0:
        return

    data = data[np.isfinite(data)]
    if data.size == 0:
        return

    lo, hi = nanmin(data), nanmax(data)
    if not np.isfinite(lo):
        return

    return lo, hi
Пример #10
0
    def update_values(self,
                      force=False,
                      use_default_modifiers=False,
                      **properties):

        if not force and not any(prop in properties for prop in
                                 ('attribute', 'n_bin')) or self.data is None:
            self.set()
            return

        comp = self.data_component

        if 'n_bin' in properties:
            self.set()
            if self._common_n_bin is not None and not comp.categorical:
                self._common_n_bin = properties['n_bin']
                self._apply_common_n_bin()

        if 'attribute' in properties or force:

            if comp.categorical:

                n_bin = max(1, min(comp.categories.size, self._max_n_bin))
                lower = -0.5
                upper = lower + comp.categories.size

            else:

                if self._common_n_bin is None:
                    n_bin = self._default_n_bin
                else:
                    n_bin = self._common_n_bin

                values = self.data_values

                if comp.datetime:
                    lower = values.min()
                    upper = values.max()
                else:
                    lower = nanmin(values)
                    upper = nanmax(values)

            self.set(lower=lower, upper=upper, n_bin=n_bin)
Пример #11
0
 def max(self, array):
     return 10.**(np.log10(nanmax(array)) * self.contrast)
Пример #12
0
    def _update_data(self):

        # Layer artist has been cleared already
        if len(self.mpl_artists) == 0:
            return

        try:
            if not self.state.density_map:
                x = ensure_numerical(
                    self.layer[self._viewer_state.x_att].ravel())
                if x.dtype.kind == 'M':
                    x = datetime64_to_mpl(x)

        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.x_att)
            return
        else:
            self.enable()

        try:
            if not self.state.density_map:
                y = ensure_numerical(
                    self.layer[self._viewer_state.y_att].ravel())
                if y.dtype.kind == 'M':
                    y = datetime64_to_mpl(y)
        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.y_att)
            return
        else:
            self.enable()

        if self.state.markers_visible:

            if self.state.density_map:
                # We don't use x, y here because we actually make use of the
                # ability of the density artist to call a custom histogram
                # method which is defined on this class and does the data
                # access.
                self.plot_artist.set_data([], [])
                self.scatter_artist.set_offsets(np.zeros((0, 2)))
            else:
                if self.state.cmap_mode == 'Fixed' and self.state.size_mode == 'Fixed':
                    # In this case we use Matplotlib's plot function because it has much
                    # better performance than scatter.
                    self.plot_artist.set_data(x, y)
                    self.scatter_artist.set_offsets(np.zeros((0, 2)))
                else:
                    self.plot_artist.set_data([], [])
                    offsets = np.vstack((x, y)).transpose()
                    self.scatter_artist.set_offsets(offsets)
        else:
            self.plot_artist.set_data([], [])
            self.scatter_artist.set_offsets(np.zeros((0, 2)))

        if self.state.line_visible:
            if self.state.cmap_mode == 'Fixed':
                self.line_collection.set_points(x, y, oversample=False)
            else:
                # In the case where we want to color the line, we need to over
                # sample the line by a factor of two so that we can assign the
                # correct colors to segments - if we didn't do this, then
                # segments on one side of a point would be a different color
                # from the other side. With oversampling, we can have half a
                # segment on either side of a point be the same color as a
                # point
                self.line_collection.set_points(x, y)
        else:
            self.line_collection.set_points([], [])

        for eartist in ravel_artists(self.errorbar_artist):
            try:
                eartist.remove()
            except ValueError:
                pass

        if self.vector_artist is not None:
            self.vector_artist.remove()
            self.vector_artist = None

        if self.state.vector_visible:

            if self.state.vx_att is not None and self.state.vy_att is not None:

                vx = ensure_numerical(self.layer[self.state.vx_att].ravel())
                vy = ensure_numerical(self.layer[self.state.vy_att].ravel())

                if self.state.vector_mode == 'Polar':
                    ang = vx
                    length = vy
                    # assume ang is anti clockwise from the x axis
                    vx = length * np.cos(np.radians(ang))
                    vy = length * np.sin(np.radians(ang))

            else:
                vx = None
                vy = None

            if self.state.vector_arrowhead:
                hw = 3
                hl = 5
            else:
                hw = 1
                hl = 0

            vmax = nanmax(np.hypot(vx, vy))

            self.vector_artist = self.axes.quiver(
                x,
                y,
                vx,
                vy,
                units='width',
                pivot=self.state.vector_origin,
                headwidth=hw,
                headlength=hl,
                scale_units='width',
                angles='xy',
                scale=10 / self.state.vector_scaling * vmax)
            self.mpl_artists[self.vector_index] = self.vector_artist

        if self.state.xerr_visible or self.state.yerr_visible:

            keep = ~np.isnan(x) & ~np.isnan(y)

            if self.state.xerr_visible and self.state.xerr_att is not None:
                xerr = ensure_numerical(
                    self.layer[self.state.xerr_att].ravel()).copy()
                keep &= ~np.isnan(xerr)
            else:
                xerr = None

            if self.state.yerr_visible and self.state.yerr_att is not None:
                yerr = ensure_numerical(
                    self.layer[self.state.yerr_att].ravel()).copy()
                keep &= ~np.isnan(yerr)
            else:
                yerr = None

            if xerr is not None:
                xerr = xerr[keep]
            if yerr is not None:
                yerr = yerr[keep]

            self._errorbar_keep = keep

            self.errorbar_artist = self.axes.errorbar(x[keep],
                                                      y[keep],
                                                      fmt='none',
                                                      xerr=xerr,
                                                      yerr=yerr)
            self.mpl_artists[self.errorbar_index] = self.errorbar_artist
Пример #13
0
 def max(self, array):
     return 10. ** (np.log10(nanmax(array)) * self.contrast)
Пример #14
0
    def _update_data(self):

        # Layer artist has been cleared already
        if len(self.mpl_artists) == 0:
            return

        try:
            if not self.state.density_map:
                x = ensure_numerical(self.layer[self._viewer_state.x_att].ravel())
        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.x_att)
            return
        else:
            self.enable()

        try:
            if not self.state.density_map:
                y = ensure_numerical(self.layer[self._viewer_state.y_att].ravel())
        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.y_att)
            return
        else:
            self.enable()

        if self.state.markers_visible:
            if self.state.density_map:
                # We don't use x, y here because we actually make use of the
                # ability of the density artist to call a custom histogram
                # method which is defined on this class and does the data
                # access.
                self.plot_artist.set_data([], [])
                self.scatter_artist.set_offsets(np.zeros((0, 2)))
            else:
                if self.state.cmap_mode == 'Fixed' and self.state.size_mode == 'Fixed':
                    # In this case we use Matplotlib's plot function because it has much
                    # better performance than scatter.
                    self.plot_artist.set_data(x, y)
                    self.scatter_artist.set_offsets(np.zeros((0, 2)))
                else:
                    self.plot_artist.set_data([], [])
                    offsets = np.vstack((x, y)).transpose()
                    self.scatter_artist.set_offsets(offsets)
        else:
            self.plot_artist.set_data([], [])
            self.scatter_artist.set_offsets(np.zeros((0, 2)))

        if self.state.line_visible:
            if self.state.cmap_mode == 'Fixed':
                self.line_collection.set_points(x, y, oversample=False)
            else:
                # In the case where we want to color the line, we need to over
                # sample the line by a factor of two so that we can assign the
                # correct colors to segments - if we didn't do this, then
                # segments on one side of a point would be a different color
                # from the other side. With oversampling, we can have half a
                # segment on either side of a point be the same color as a
                # point
                self.line_collection.set_points(x, y)
        else:
            self.line_collection.set_points([], [])

        for eartist in ravel_artists(self.errorbar_artist):
            try:
                eartist.remove()
            except ValueError:
                pass

        if self.vector_artist is not None:
            self.vector_artist.remove()
            self.vector_artist = None

        if self.state.vector_visible:

            if self.state.vx_att is not None and self.state.vy_att is not None:

                vx = ensure_numerical(self.layer[self.state.vx_att].ravel())
                vy = ensure_numerical(self.layer[self.state.vy_att].ravel())

                if self.state.vector_mode == 'Polar':
                    ang = vx
                    length = vy
                    # assume ang is anti clockwise from the x axis
                    vx = length * np.cos(np.radians(ang))
                    vy = length * np.sin(np.radians(ang))

            else:
                vx = None
                vy = None

            if self.state.vector_arrowhead:
                hw = 3
                hl = 5
            else:
                hw = 1
                hl = 0

            vmax = nanmax(np.hypot(vx, vy))

            self.vector_artist = self.axes.quiver(x, y, vx, vy, units='width',
                                                  pivot=self.state.vector_origin,
                                                  headwidth=hw, headlength=hl,
                                                  scale_units='width', angles='xy',
                                                  scale=10 / self.state.vector_scaling * vmax)
            self.mpl_artists[self.vector_index] = self.vector_artist

        if self.state.xerr_visible or self.state.yerr_visible:

            if self.state.xerr_visible and self.state.xerr_att is not None:
                xerr = ensure_numerical(self.layer[self.state.xerr_att].ravel())
            else:
                xerr = None

            if self.state.yerr_visible and self.state.yerr_att is not None:
                yerr = ensure_numerical(self.layer[self.state.yerr_att].ravel())
            else:
                yerr = None

            self.errorbar_artist = self.axes.errorbar(x, y, fmt='none',
                                                      xerr=xerr, yerr=yerr)
            self.mpl_artists[self.errorbar_index] = self.errorbar_artist
Пример #15
0
def facet_subsets(data_collection,
                  cid,
                  lo=None,
                  hi=None,
                  steps=5,
                  prefix='',
                  log=False):
    """
    Create a series of subsets that partition the values of a particular
    attribute into several bins

    This creates `steps` new subset groups, adds them to the data collection, and
    returns the list of newly created subset groups.

    Parameters
    ----------
    data : :class:`~glue.core.data_collection.DataCollection`
        The DataCollection object to use
    cid : :class:`~glue.core.component_id.ComponentID`
         The ComponentID to facet on
    lo : float, optional
        The lower bound for the faceting. Defaults to minimum value in data
    hi : float, optional
        The upper bound for the faceting. Defaults to maximum value in data
    steps : int, optional
        The number of subsets to create. Defaults to 5
    prefix : str, optional
        If present, the new subset labels will begin with `prefix`
    log : bool, optional
        If `True`, space divisions logarithmically. Default is `False`

    Returns
    -------
    subset_groups : iterable
        List of :class:`~glue.core.subset_group.SubsetGroup` instances added to
        `data`

    Examples
    --------

    ::

        facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2)

    creates 2 new subsets. The first represents the constraint 0 <=
    mass < 5. The second represents 5 <= mass <= 10::

        facet_subset(data, data.id['mass'], lo=10, hi=0, steps=2)

    Creates 2 new subsets. The first represents the constraint 10 >= x > 5
    The second represents 5 >= mass >= 0::

        facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2, prefix='m')

    Labels the subsets ``m_1`` and ``m_2``.

    Note that the last range is inclusive on both sides. For example, if ``lo``
    is 0 and ``hi`` is 5, and ``steps`` is 5, then the intervals for the subsets
    are [0,1), [1,2), [2,3), [3,4), and [4,5].
    """
    from glue.core.exceptions import IncompatibleAttribute
    if lo is None or hi is None:
        for data in data_collection:
            try:
                vals = data[cid]
                break
            except IncompatibleAttribute:
                continue
        else:
            raise ValueError("Cannot infer data limits for ComponentID %s" %
                             cid)
        if lo is None:
            lo = nanmin(vals)
        if hi is None:
            hi = nanmax(vals)

    reverse = lo > hi
    if log:
        rng = np.logspace(np.log10(lo), np.log10(hi), steps + 1)
    else:
        rng = np.linspace(lo, hi, steps + 1)

    states = []
    labels = []
    for i in range(steps):

        # The if i < steps - 1 clauses are needed because the last interval
        # has to be inclusive on both sides.

        if reverse:
            if i < steps - 1:
                states.append((cid <= rng[i]) & (cid > rng[i + 1]))
                labels.append(prefix +
                              '{0}<{1}<={2}'.format(rng[i + 1], cid, rng[i]))
            else:
                states.append((cid <= rng[i]) & (cid >= rng[i + 1]))
                labels.append(prefix +
                              '{0}<={1}<={2}'.format(rng[i + 1], cid, rng[i]))

        else:
            if i < steps - 1:
                states.append((cid >= rng[i]) & (cid < rng[i + 1]))
                labels.append(prefix +
                              '{0}<={1}<{2}'.format(rng[i], cid, rng[i + 1]))
            else:
                states.append((cid >= rng[i]) & (cid <= rng[i + 1]))
                labels.append(prefix +
                              '{0}<={1}<={2}'.format(rng[i], cid, rng[i + 1]))

    result = []
    for lbl, s in zip(labels, states):
        sg = data_collection.new_subset_group(label=lbl, subset_state=s)
        result.append(sg)

    return result
Пример #16
0
    def _update_profile(self, *event):

        if self.viewer_state is None or self.viewer_state.x_att is None or self.attribute is None:
            self._profile = None, None
            return

        # Check what pixel axis in the current dataset x_att corresponds to
        pix_cid = is_convertible_to_single_pixel_cid(self.layer,
                                                     self.viewer_state.x_att)

        if pix_cid is None:
            self._profile = None, None
            return

        # If we get here, then x_att does correspond to a single pixel axis in
        # the cube, so we now prepare a list of axes to collapse over.
        axes = tuple(i for i in range(self.layer.ndim) if i != pix_cid.axis)

        # We now get the y values for the data

        # TODO: in future we should optimize the case where the mask is much
        # smaller than the data to just average the relevant 'spaxels' in the
        # data rather than collapsing the whole cube.

        try:
            if isinstance(self.layer, Data):
                data = self.layer
                data_values = data[self.attribute]
            else:
                data = self.layer.data
                if isinstance(self.layer.subset_state, SliceSubsetState):
                    data_values = self.layer.subset_state.to_array(
                        self.layer.data, self.attribute)
                else:
                    # We need to force a copy *and* convert to float just in case
                    data_values = np.array(data[self.attribute], dtype=float)
                    mask = self.layer.to_mask()
                    if np.sum(mask) == 0:
                        self._profile = [], []
                        return
                    data_values[~mask] = np.nan
        except IncompatibleAttribute:
            self._profile = None, None
            return

        # Collapse along all dimensions except x_att
        if self.layer.ndim > 1:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore", category=RuntimeWarning)
                profile_values = self.viewer_state.function(data_values,
                                                            axis=axes)
        else:
            profile_values = data_values

        # Finally, we get the coordinate values for the requested axis
        axis_view = [0] * data.ndim
        axis_view[pix_cid.axis] = slice(None)
        axis_values = data[self.viewer_state.x_att, axis_view]

        with delay_callback(self, 'v_min', 'v_max'):
            self._profile = axis_values, profile_values
            self.v_min = nanmin(profile_values)
            self.v_max = nanmax(profile_values)
Пример #17
0
def facet_subsets(data_collection, cid, lo=None, hi=None, steps=5,
                  prefix='', log=False):
    """
    Create a series of subsets that partition the values of a particular
    attribute into several bins

    This creates `steps` new subet groups, adds them to the data collection, and
    returns the list of newly created subset groups.

    Parameters
    ----------
    data : :class:`~glue.core.data_collection.DataCollection`
        The DataCollection object to use
    cid : :class:`~glue.core.component_id.ComponentID`
         The ComponentID to facet on
    lo : float, optional
        The lower bound for the faceting. Defaults to minimum value in data
    hi : float, optional
        The upper bound for the faceting. Defaults to maximum value in data
    steps : int, optional
        The number of subsets to create. Defaults to 5
    prefix : str, optional
        If present, the new subset labels will begin with `prefix`
    log : bool, optional
        If `True`, space divisions logarithmically. Default is `False`

    Returns
    -------
    subset_groups : iterable
        List of :class:`~glue.core.subset_group.SubsetGroup` instances added to
        `data`

    Examples
    --------

    ::

        facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2)

    creates 2 new subsets. The first represents the constraint 0 <=
    mass < 5. The second represents 5 <= mass <= 10::

        facet_subset(data, data.id['mass'], lo=10, hi=0, steps=2)

    Creates 2 new subsets. The first represents the constraint 10 >= x > 5
    The second represents 5 >= mass >= 0::

        facet_subset(data, data.id['mass'], lo=0, hi=10, steps=2, prefix='m')

    Labels the subsets ``m_1`` and ``m_2``.

    Note that the last range is inclusive on both sides. For example, if ``lo``
    is 0 and ``hi`` is 5, and ``steps`` is 5, then the intervals for the subsets
    are [0,1), [1,2), [2,3), [3,4), and [4,5].
    """
    from glue.core.exceptions import IncompatibleAttribute
    if lo is None or hi is None:
        for data in data_collection:
            try:
                vals = data[cid]
                break
            except IncompatibleAttribute:
                continue
        else:
            raise ValueError("Cannot infer data limits for ComponentID %s"
                             % cid)
        if lo is None:
            lo = nanmin(vals)
        if hi is None:
            hi = nanmax(vals)

    reverse = lo > hi
    if log:
        rng = np.logspace(np.log10(lo), np.log10(hi), steps + 1)
    else:
        rng = np.linspace(lo, hi, steps + 1)

    states = []
    labels = []
    for i in range(steps):

        # The if i < steps - 1 clauses are needed because the last interval
        # has to be inclusive on both sides.

        if reverse:
            if i < steps - 1:
                states.append((cid <= rng[i]) & (cid > rng[i + 1]))
                labels.append(prefix + '{0}<{1}<={2}'.format(rng[i + 1], cid, rng[i]))
            else:
                states.append((cid <= rng[i]) & (cid >= rng[i + 1]))
                labels.append(prefix + '{0}<={1}<={2}'.format(rng[i + 1], cid, rng[i]))

        else:
            if i < steps - 1:
                states.append((cid >= rng[i]) & (cid < rng[i + 1]))
                labels.append(prefix + '{0}<={1}<{2}'.format(rng[i], cid, rng[i + 1]))
            else:
                states.append((cid >= rng[i]) & (cid <= rng[i + 1]))
                labels.append(prefix + '{0}<={1}<={2}'.format(rng[i], cid, rng[i + 1]))

    result = []
    for lbl, s in zip(labels, states):
        sg = data_collection.new_subset_group(label=lbl, subset_state=s)
        result.append(sg)

    return result
Пример #18
0
    def _calculate_profile_postthread(self):

        self.notify_end_computation()

        # It's possible for this method to get called but for the state to have
        # been updated in the mean time to have a histogram that raises an
        # exception (for example an IncompatibleAttribute). If any errors happen
        # here, we simply ignore them since _calculate_histogram_error will get
        # called directly.
        try:
            visible_data = self.state.profile
        except Exception:
            return

        if visible_data is None:
            return

        self.enable()

        x, y = visible_data

        # Update the data values.
        if len(x) > 0:
            self.state.update_limits()
            # Normalize profile values to the [0:1] range based on limits
            if self._viewer_state.normalize:
                y = self.state.normalize_values(y)
            self.plot_artist.set_data(x, y)
            self.plot_artist.set_visible(self.state.visible)
        else:
            # We need to do this otherwise we get issues on Windows when
            # passing an empty list to plot_artist
            self.plot_artist.set_visible(False)

        # TODO: the following was copy/pasted from the histogram viewer, maybe
        # we can find a way to avoid duplication?

        # We have to do the following to make sure that we reset the y_max as
        # needed. We can't simply reset based on the maximum for this layer
        # because other layers might have other values, and we also can't do:
        #
        #   self._viewer_state.y_max = max(self._viewer_state.y_max, result[0].max())
        #
        # because this would never allow y_max to get smaller.

        if not self._viewer_state.normalize and len(y) > 0:

            y_min = nanmin(y)
            y_max = nanmax(y)
            y_range = y_max - y_min

            self.state._y_min = y_min - y_range * 0.1
            self.state._y_max = y_max + y_range * 0.1

            largest_y_max = max(
                getattr(layer, '_y_max', 0)
                for layer in self._viewer_state.layers)
            if largest_y_max != self._viewer_state.y_max:
                self._viewer_state.y_max = largest_y_max

            smallest_y_min = min(
                getattr(layer, '_y_min', np.inf)
                for layer in self._viewer_state.layers)
            if smallest_y_min != self._viewer_state.y_min:
                self._viewer_state.y_min = smallest_y_min

        self.redraw()
Пример #19
0
    def _update_data(self, changed):

        # Layer artist has been cleared already
        if len(self.mpl_artists) == 0:
            return

        try:
            x = self.layer[self._viewer_state.x_att].ravel()
        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.x_att)
            return
        else:
            self.enable()

        try:
            y = self.layer[self._viewer_state.y_att].ravel()
        except (IncompatibleAttribute, IndexError):
            # The following includes a call to self.clear()
            self.disable_invalid_attributes(self._viewer_state.y_att)
            return
        else:
            self.enable()

        if self.state.markers_visible:
            if self.state.density_map:
                self.density_artist.set_xy(x, y)
                self.plot_artist.set_data([], [])
                self.scatter_artist.set_offsets(np.zeros((0, 2)))
            else:
                self.density_artist.set_xy([], [])
                self.density_artist.set_c(None)
                if self.state.cmap_mode == 'Fixed' and self.state.size_mode == 'Fixed':
                    # In this case we use Matplotlib's plot function because it has much
                    # better performance than scatter.
                    self.plot_artist.set_data(x, y)
                    self.scatter_artist.set_offsets(np.zeros((0, 2)))
                else:
                    self.plot_artist.set_data([], [])
                    offsets = np.vstack((x, y)).transpose()
                    self.scatter_artist.set_offsets(offsets)
        else:
            self.plot_artist.set_data([], [])
            self.scatter_artist.set_offsets(np.zeros((0, 2)))
            self.density_artist.set_xy([], [])

        if self.state.line_visible:
            if self.state.cmap_mode == 'Fixed':
                self.line_collection.set_points(x, y, oversample=False)
            else:
                # In the case where we want to color the line, we need to over
                # sample the line by a factor of two so that we can assign the
                # correct colors to segments - if we didn't do this, then
                # segments on one side of a point would be a different color
                # from the other side. With oversampling, we can have half a
                # segment on either side of a point be the same color as a
                # point
                self.line_collection.set_points(x, y)
        else:
            self.line_collection.set_points([], [])

        for eartist in ravel_artists(self.errorbar_artist):
            try:
                eartist.remove()
            except ValueError:
                pass

        if self.vector_artist is not None:
            self.vector_artist.remove()
            self.vector_artist = None

        if self.state.vector_visible:

            if self.state.vx_att is not None and self.state.vy_att is not None:

                vx = self.layer[self.state.vx_att].ravel()
                vy = self.layer[self.state.vy_att].ravel()

                if self.state.vector_mode == 'Polar':
                    ang = vx
                    length = vy
                    # assume ang is anti clockwise from the x axis
                    vx = length * np.cos(np.radians(ang))
                    vy = length * np.sin(np.radians(ang))

            else:
                vx = None
                vy = None

            if self.state.vector_arrowhead:
                hw = 3
                hl = 5
            else:
                hw = 1
                hl = 0

            vmax = nanmax(np.hypot(vx, vy))

            self.vector_artist = self.axes.quiver(
                x,
                y,
                vx,
                vy,
                units='width',
                pivot=self.state.vector_origin,
                headwidth=hw,
                headlength=hl,
                scale_units='width',
                angles='xy',
                scale=10 / self.state.vector_scaling * vmax)
            self.mpl_artists[self.vector_index] = self.vector_artist

        if self.state.xerr_visible or self.state.yerr_visible:

            if self.state.xerr_visible and self.state.xerr_att is not None:
                xerr = self.layer[self.state.xerr_att].ravel()
            else:
                xerr = None

            if self.state.yerr_visible and self.state.yerr_att is not None:
                yerr = self.layer[self.state.yerr_att].ravel()
            else:
                yerr = None

            self.errorbar_artist = self.axes.errorbar(x,
                                                      y,
                                                      fmt='none',
                                                      xerr=xerr,
                                                      yerr=yerr)
            self.mpl_artists[self.errorbar_index] = self.errorbar_artist
Пример #20
0
    def _calculate_profile_postthread(self):

        # If the worker has started running again, we should stop at this point
        # since this function will get called again.
        if QT_INSTALLED and self._worker.running:
            return

        self.notify_end_computation()

        # It's possible for this method to get called but for the state to have
        # been updated in the mean time to have a histogram that raises an
        # exception (for example an IncompatibleAttribute). If any errors happen
        # here, we simply ignore them since _calculate_histogram_error will get
        # called directly.
        try:
            visible_data = self.state.profile
        except Exception:
            return

        if visible_data is None:
            return

        self.enable()

        x, y = visible_data

        # Update the data values.
        if len(x) > 0:
            self.state.update_limits()
            # Normalize profile values to the [0:1] range based on limits
            if self._viewer_state.normalize:
                y = self.state.normalize_values(y)
            self.plot_artist.set_data(x, y)
            self.plot_artist.set_visible(self.state.visible)
        else:
            # We need to do this otherwise we get issues on Windows when
            # passing an empty list to plot_artist
            self.plot_artist.set_visible(False)

        # TODO: the following was copy/pasted from the histogram viewer, maybe
        # we can find a way to avoid duplication?

        # We have to do the following to make sure that we reset the y_max as
        # needed. We can't simply reset based on the maximum for this layer
        # because other layers might have other values, and we also can't do:
        #
        #   self._viewer_state.y_max = max(self._viewer_state.y_max, result[0].max())
        #
        # because this would never allow y_max to get smaller.

        if not self._viewer_state.normalize and len(y) > 0:

            y_min = nanmin(y)
            y_max = nanmax(y)
            y_range = y_max - y_min

            self.state._y_min = y_min - y_range * 0.1
            self.state._y_max = y_max + y_range * 0.1

            largest_y_max = max(getattr(layer, '_y_max', 0) for layer in self._viewer_state.layers)
            if largest_y_max != self._viewer_state.y_max:
                self._viewer_state.y_max = largest_y_max

            smallest_y_min = min(getattr(layer, '_y_min', np.inf) for layer in self._viewer_state.layers)
            if smallest_y_min != self._viewer_state.y_min:
                self._viewer_state.y_min = smallest_y_min

        self.redraw()