def _raw_ticks(self, vmin, vmax):
     if self._nbins == 'auto':
         if self.axis is not None:
             nbins = np.clip(self.axis.get_tick_space(),
                             max(1, self._min_n_ticks - 1), 9)
         else:
             nbins = 9
     else:
         nbins = self._nbins
     scale, offset = mticker.scale_range(vmin, vmax, nbins)
     _vmin = vmin - offset
     _vmax = vmax - offset
     raw_step = 1 / (1 / vmax - 1 / vmin) / nbins
     steps = self._extended_steps * scale
     if self._integer:
         # For steps > 1, keep only integer values.
         igood = (steps < 1) | (np.abs(steps - np.round(steps)) < 0.001)
         steps = steps[igood]
     self.numticks = len(self.other_axis.get_xticks())
     ticks = 1 / np.linspace(1 / vmin, 1 / vmax, self.numticks)
     for step in steps:
         best_vmin = (_vmin // step) * step
         low = np.round(mticker.Base(step).le(_vmin - best_vmin) / step)
         high = np.round(mticker.Base(step).ge(_vmax - best_vmin) / step)
     return ticks
Exemplo n.º 2
0
    def linear_tics(self, vmin, vmax):
        nbins = self._ntics
        scale, offset = ticker.scale_range(vmin, vmax, nbins)
        vmin -= offset
        vmax -= offset
        raw_step = (vmax - vmin) / nbins
        scaled_raw_step = raw_step / scale

        for step in [1, 2, 5, 10]:
            if step < scaled_raw_step:
                continue
            step *= scale
            best_vmin = step * divmod(vmin, step)[0]
            best_vmax = best_vmin + step * nbins
            if (best_vmax >= vmax):
                break
        if self._trim:
            extra_bins = int(divmod((best_vmax - vmax), step)[0])
            nbins -= extra_bins
        return (np.arange(nbins + 1) * step + best_vmin + offset)
Exemplo n.º 3
0
    def linear_tics(self, vmin, vmax):
        nbins = self._ntics
        scale, offset = ticker.scale_range(vmin, vmax, nbins)
        vmin -= offset
        vmax -= offset
        raw_step = (vmax-vmin)/nbins
        scaled_raw_step = raw_step/scale

        for step in [1,2,5,10]:
            if step < scaled_raw_step:
                continue
            step *= scale
            best_vmin = step*divmod(vmin, step)[0]
            best_vmax = best_vmin + step*nbins
            if (best_vmax >= vmax):
                break
        if self._trim:
            extra_bins = int(divmod((best_vmax - vmax), step)[0])
            nbins -= extra_bins
        return (npy.arange(nbins+1) * step + best_vmin + offset)
Exemplo n.º 4
0
    def tick_values(self, vmin, vmax):
        # Max N locator will produce locations outside vmin, vmax, so even if pruned
        # there can be points very close to the actual bounds. Let's cut them out.
        # Also account for tick labels with aspect ratio > 3 (default often-violated heuristic)
        # - use better heuristic based on number of characters in label and typical font aspect ratio

        axes = self.axis.axes
        tick = self.axis._get_tick(True)
        rotation = tick._labelrotation[1]

        if isinstance(self.axis, YAxis):
            rotation += 90
            ends = axes.transAxes.transform([[0, 0], [0, 1]])
            length = ((ends[1][1] - ends[0][1]) / axes.figure.dpi) * 72
        else:
            ends = axes.transAxes.transform([[0, 0], [1, 0]])
            length = ((ends[1][0] - ends[0][0]) / axes.figure.dpi) * 72
        size_ratio = tick.label1.get_size() / length
        cos_rotation = abs(math.cos(math.radians(rotation)))
        self._font_aspect = 0.65 * cos_rotation
        self._char_size_scale = size_ratio * (vmax - vmin)
        self._formatter = self.axis.major.formatter
        self._range = (vmin, vmax)

        # first guess
        if cos_rotation > 0.05:
            label_len = size_ratio * 1.5 * (vmax - vmin)
            label_space = label_len * 1.1
        else:
            # text orthogonal to axis
            label_len = size_ratio * _min_label_len_chars * (vmax - vmin)
            label_space = label_len * 1.25

        delta = label_len / 2 if self.bounded_prune else 0
        nbins = int((vmax - vmin - 2 * delta) / label_space) + 1
        if nbins > 4:
            # use more space for ticks
            nbins = int((vmax - vmin - 2 * delta) /
                        ((1.5 if nbins > 6 else 1.3) * label_space)) + 1
        min_n_ticks = min(nbins, 2)
        nbins = min(self._nbins if self._nbins != 'auto' else 9, nbins)
        # First get typical ticks so we can calculate actual label length
        while True:
            locs, _ = self._spaced_ticks(vmin + delta, vmax - delta, label_len,
                                         min_n_ticks, nbins, False)
            if len(locs) or min_n_ticks == 1:
                break
            if nbins == 2:
                min_n_ticks -= 1
            nbins = max(min_n_ticks, 2)
        if cos_rotation > 0.05 and isinstance(
                self._formatter, ticker.ScalarFormatter) and len(locs) > 1:

            label_len = self._get_label_len(locs)
            locs = self._bounded_prune(locs, label_len)
            if len(locs) > 1:
                step = locs[1] - locs[0]
            # noinspection PyUnboundLocalVariable
            if len(locs) < max(3, nbins) or step < label_len * (1.1 if len(locs) < 4 else 1.5) \
                    or (locs[0] - vmin > min(step * 1.01, label_len * 1.5) or
                        vmax - locs[-1] > min(step * 1.01, label_len * 1.5)):
                # check for long labels, labels that are too tightly spaced, or large tick-free gaps at axes ends
                delta = label_len / 2 if self.bounded_prune else 0
                for fac in [1.5, 1.35, 1.1]:
                    nbins = int(
                        (vmax - vmin - 2 * delta) /
                        (fac * max(2 * self._char_size_scale, label_len))) + 1
                    if nbins >= 4:
                        break
                if self._nbins != 'auto':
                    nbins = min(self._nbins, nbins)
                min_n_ticks = min(min_n_ticks, nbins)
                retry = True
                try_shorter = True
                locs = []
                while min_n_ticks > 1:
                    locs, good = self._spaced_ticks(vmin + delta, vmax - delta,
                                                    label_len, min_n_ticks,
                                                    nbins)
                    if len(locs):
                        if not good:
                            new_len = self._get_label_len(locs)
                            if not np.isclose(new_len, label_len):
                                label_len = new_len
                                delta = label_len / 2 if self.bounded_prune else 0
                                if retry:
                                    retry = False
                                    continue
                                locs = self._bounded_prune(locs, label_len)
                    elif min_n_ticks > 1 and try_shorter:
                        # Original label length may be too long for good ticks which exist
                        delta /= 2
                        label_len /= 2
                        try_shorter = False
                        locs, _ = self._spaced_ticks(vmin + delta,
                                                     vmax - delta, label_len,
                                                     min_n_ticks, nbins)
                        if len(locs):
                            label_len = self._get_label_len(locs)
                            delta = label_len / 2 if self.bounded_prune else 0
                            continue

                    if min_n_ticks == 1 and len(locs) == 1 or len(locs) >= min_n_ticks > 1 \
                            and locs[1] - locs[0] > self._get_label_len(locs) * 1.1:
                        break
                    min_n_ticks -= 1
                    locs = []
                if len(locs) <= 1 and size_ratio * self._font_aspect < 0.9:
                    scale, offset = ticker.scale_range(vmin, vmax, 1)
                    # Try to get any two points that will fit
                    for sc in [scale, scale / 10.]:
                        locs = [
                            round((vmin * 3 + vmax) / (4 * sc)) * sc,
                            round((vmin + 3 * vmax) / (4 * sc)) * sc
                        ]
                        if locs[0] != locs[1] and locs[0] >= vmin and locs[
                                1] <= vmax:
                            if self._valid(locs):
                                return locs
                    # if no ticks, check for short integer number location in the range that may have been missed
                    # because adding any other values would make label length much longer
                    loc = round((vmin + vmax) / (2 * scale)) * scale
                    if vmin < loc < vmax:
                        locs = [loc]
                        label_len = self._get_label_len(locs)
                        return self._bounded_prune(locs, label_len)
        else:
            return self._bounded_prune(locs, label_len)

        return locs
Exemplo n.º 5
0
    def _spaced_ticks(self,
                      vmin,
                      vmax,
                      _label_len,
                      min_ticks,
                      nbins,
                      changing_lengths=True):

        scale, offset = ticker.scale_range(vmin, vmax, nbins)
        _vmin = vmin - offset
        _vmax = vmax - offset
        _range = _vmax - _vmin
        eps = _range * 1e-6
        _full_range = self._range[1] - self._range[0]
        for sc in [100, 10, 1]:
            round_center = round(
                (_vmin + _vmax) / (2 * sc * scale)) * sc * scale
            if _vmin - eps <= round_center <= _vmax + eps:
                break

        label_len = _label_len * 1.1
        raw_step = max(label_len, _range / ((nbins - 2) if nbins > 2 else 1))
        raw_step1 = _range / max(1, (nbins - (0 if self.bounded_prune else 1)))
        best = []
        best_score = -np.infty
        for step_ix, (_steps, _offsets) in enumerate(
                zip(self._step_groups, self._offsets)):

            steps = _steps * scale
            if step_ix and len(best) < 3:
                raw_step = max(raw_step, _range / 2)

            istep = min(len(steps) - 1, bisect_left(steps, raw_step))
            if not istep:
                continue
            # This is an upper limit; move to smaller or half-phase steps if necessary.
            for off in [False, True]:
                if off and (len(best) > 2 or len(best) == 2 and
                            (not round_center or step_ix > 1)):
                    break
                for i in reversed(range(istep + 1)):
                    if off and not _offsets[i]:
                        continue
                    step = steps[i]
                    if step < label_len:
                        break

                    if step_ix and _vmin <= round_center <= _vmax:
                        # For less nice steps, try to make them hit any round numbers in range
                        best_vmin = round_center - (
                            (round_center - _vmin) // step) * step
                    else:
                        best_vmin = (_vmin // step) * step

                    if off:
                        # try half-offset steps, e.g. to get -x/2, x/2 as well as -x,0,x
                        low = scale * _offsets[i]
                        if best_vmin - low >= _vmin:
                            best_vmin -= low
                        else:
                            best_vmin += low

                    sc = 10**(math.log10(step) // 1)
                    step_int = round(step / sc)

                    low = _ge(_vmin - best_vmin, offset, step)
                    high = _le(_vmax - best_vmin, offset, step)
                    if min_ticks <= high - low + 1 <= nbins:
                        ticks = np.arange(low, high + 1) * step + (best_vmin +
                                                                   offset)

                        if off and round_center and changing_lengths:
                            # If no nice number, see if we can shift points to get one
                            if step > 2 * sc:
                                for shift in [0, -1, 1, -2, 2]:
                                    if abs(shift * sc) >= step / 2:
                                        break
                                    shifted = ticks + shift * sc
                                    if any(np.round(shifted / sc / 10) * 10 == np.round(shifted / sc)) \
                                            and self._valid(shifted):
                                        ticks = shifted

                        big_step = step > raw_step1 and step > label_len * 1.5
                        no_more_ticks = min(3, len(ticks)) <= len(best)
                        odd_gaps = min_ticks > 1 and (
                            (len(ticks) == 2 and step > _full_range * 0.7)
                            or self.bounded_prune and
                            ((ticks[0] - self._range[0] > max(
                                min(_full_range / 3, step), label_len * 1.1)
                              or self._range[1] - ticks[-1] > max(
                                  min(_full_range / 3, step), label_len * 1.1))
                             ) or not self.bounded_prune and len(ticks) == 3
                            and step > max(2 * label_len, _full_range / 3) and
                            step_int > 1 and round(ticks[-1] / sc) % 10 > 0)

                        close_ticks = step < label_len * 1.3 and len(ticks) > 2
                        if (big_step and odd_gaps
                                or close_ticks) and no_more_ticks:
                            continue
                        if len(
                                best
                        ) and odd_gaps and step_ix or changing_lengths and not self._valid(
                                ticks):
                            continue

                        too_few_points = (
                            len(ticks) < 3 and
                            (nbins >
                             (3 if step_ix else 4)) or (len(ticks) < max(
                                 2,
                                 (nbins + 1) // 2))) and step > label_len * 1.5
                        _score = -1 * too_few_points - step_ix * 2 - close_ticks * 2 - odd_gaps * 1
                        if len(ticks) < 3 and big_step:
                            _score -= 2
                        if off:
                            _score -= 3
                        if step_int == 1.0 and not off:
                            _score += 1
                        if 0. in steps:
                            _score += 1
                        if _score <= best_score:
                            continue
                        if off and not step_ix or big_step and (not len(best) or len(ticks) < len(best)) \
                                or close_ticks or too_few_points or odd_gaps:
                            # prefer spacing where some ticks nearish the ends and ticks not too close in centre
                            best = ticks
                            best_score = _score
                        else:
                            return ticks, True
        return best, False