File: base.py

package info (click to toggle)
python-leather 0.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 652 kB
  • sloc: python: 2,385; makefile: 117; sh: 5
file content (136 lines) | stat: -rw-r--r-- 4,439 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from datetime import date, datetime

from leather.data_types import Date, DateTime, Number, Text
from leather.shapes import Bars, Columns


class Scale:
    """
    Base class for various kinds of scale objects.
    """
    @classmethod
    def infer(cls, layers, dimension, data_type):
        """
        Infer's an appropriate default scale for a given sequence of
        :class:`.Series`.

        :param chart_series:
            A sequence of :class:`.Series` instances
        :param dimension:
            The dimension, :code:`X` or :code:`Y` of the data to infer for.
        :param data_type:
            The type of data contained in the series dimension.
        """
        from leather.scales.linear import Linear
        from leather.scales.ordinal import Ordinal
        from leather.scales.temporal import Temporal

        # Default Time scale is Temporal
        if data_type is Date:
            data_min = date.max
            data_max = date.min

            for series, shape in layers:
                data_min = min(data_min, series.min(dimension))
                data_max = max(data_max, series.max(dimension))

            scale = Temporal(data_min, data_max)
        elif data_type is DateTime:
            data_min = datetime.max
            data_max = datetime.min

            for series, shape in layers:
                data_min = min(data_min, series.min(dimension))
                data_max = max(data_max, series.max(dimension))

            scale = Temporal(data_min, data_max)
        # Default Number scale is Linear
        elif data_type is Number:
            force_zero = False
            data_min = None
            data_max = None

            for series, shape in layers:
                if isinstance(shape, (Bars, Columns)):
                    force_zero = True

                if data_min is None:
                    data_min = series.min(dimension)
                else:
                    data_min = min(data_min, series.min(dimension))

                if data_max is None:
                    data_max = series.max(dimension)
                else:
                    data_max = max(data_max, series.max(dimension))

            if force_zero:
                if data_min > 0:
                    data_min = 0

                if data_max < 0:
                    data_max = 0

            scale = Linear(data_min, data_max)
        # Default Text scale is Ordinal
        elif data_type is Text:
            scale_values = None

            # First case: a single set of ordinal labels
            if len(layers) == 1:
                scale_values = layers[0][0].values(dimension)
            else:
                first_series = set(layers[0][0].values(dimension))
                data_series = [series.values(dimension) for series, shape in layers]
                all_same = True

                for series in data_series:
                    if set(series) != first_series:
                        all_same = False
                        break

                # Second case: multiple identical sets of ordinal labels
                if all_same:
                    scale_values = layers[0][0].values(dimension)
                # Third case: multiple different sets of ordinal labels
                else:
                    scale_values = sorted(set().union(*data_series))

            scale = Ordinal(scale_values)

        return scale

    def contains(self, v):
        """
        Return :code:`True` if a given value is contained within this scale's
        displayed domain.
        """
        raise NotImplementedError

    def project(self, value, range_min, range_max):
        """
        Project a value in this scale's domain to a target range.
        """
        raise NotImplementedError

    def project_interval(self, value, range_min, range_max):
        """
        Project a value in this scale's domain to an interval in the target
        range. This is used for places :class:`.Bars` and :class:`.Columns`.
        """
        raise NotImplementedError

    def ticks(self):
        """
        Generate a series of ticks for this scale.
        """
        raise NotImplementedError

    def format_tick(self, value, i, count):
        """
        Format ticks for display.

        This method is used as a default which will be ignored if the user
        provides a custom tick formatter to the axis.
        """
        return str(value)