Skip to content

Commit

Permalink
Consolidate scale inference.
Browse files Browse the repository at this point in the history
  • Loading branch information
onyxfish committed May 18, 2016
1 parent 4914426 commit a2c4457
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 106 deletions.
4 changes: 2 additions & 2 deletions leather/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#!/usr/bin/env python

from leather.axis import Axis
from leather.data_types import Number, Text
from leather.chart import Chart
from leather.grid import Grid
from leather.lattice import Lattice
from leather.legend import Legend
from leather.scales.linear import LinearScale
from leather.scales.ordinal import OrdinalScale
from leather.scales import Linear, Ordinal
from leather.shapes.columns import Columns
from leather.shapes.dots import Dots
from leather.shapes.lines import Lines
40 changes: 10 additions & 30 deletions leather/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import six

from leather.axis import Axis
from leather.data_types import Number, Text
from leather.scales.linear import LinearScale
from leather.scales.ordinal import OrdinalScale
from leather.scales import Scale
from leather.series import Series
from leather.shapes.bars import Bars
from leather.shapes.columns import Columns
Expand Down Expand Up @@ -69,7 +67,7 @@ def set_y_axis(self, axis):
"""
self._set_axis(Y, axis)

def add_series(self, series, shape):
def add_series(self, series):
"""
Add a data :class:`.Series` to the chart.
"""
Expand All @@ -79,31 +77,31 @@ def add_series(self, series, shape):
elif series.types[dim] is not self._types[dim]:
raise TypeError('Can\'t mix axis-data types: %s and %s' % (series.types[dim], self._types[dim]))

self._layers.append((series, shape))
self._layers.append(series)

def add_bars(self, data, name=None):
"""
Shortcut method for adding a bar series to the chart.
"""
self.add_series(Series(data, name=name), DEFAULT_BARS)
self.add_series(Series(data, DEFAULT_BARS, name=name))

def add_columns(self, data, name=None):
"""
Shortcut method for adding a column series to the chart.
"""
self.add_series(Series(data, name=name), DEFAULT_COLUMNS)
self.add_series(Series(data, DEFAULT_COLUMNS, name=name))

def add_dots(self, data, name=None):
"""
Shortcut method for adding a dotted series to the chart.
"""
self.add_series(Series(data, name=name), DEFAULT_DOTS)
self.add_series(Series(data, DEFAULT_DOTS, name=name))

def add_lines(self, data, name=None):
"""
Shortcut method for adding a line series to the chart.
"""
self.add_series(Series(data, name=name), DEFAULT_LINES)
self.add_series(Series(data, DEFAULT_LINES, name=name))

def _validate_dimension(self, dimension):
"""
Expand All @@ -117,25 +115,7 @@ def _validate_dimension(self, dimension):

if not axis:
if not scale:
# Default Number scale is Linear
if data_type is Number:
data_min = min([series.min(dimension) for series, shape in self._layers])
data_max = max([series.max(dimension) for series, shape in self._layers])

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

for series, shape in self._layers:
if not scale_values:
scale_values = series.values(dimension)
continue

if series.values(dimension) != scale_values:
raise ValueError('Mismatched series scales')

scale = OrdinalScale(scale_values)
scale = Scale.infer(self._layers, dimension, data_type)
else:
scale = scale

Expand Down Expand Up @@ -189,8 +169,8 @@ def to_svg_group(self, width, height, margin=None):
# Series
series_group = ET.Element('g')

for series, shape in self._layers:
series_group.append(shape.to_svg(canvas_width, canvas_height, x_scale, y_scale, series))
for series in self._layers:
series_group.append(series.to_svg(canvas_width, canvas_height, x_scale, y_scale))

root_group.append(axes_group)
root_group.append(series_group)
Expand Down
3 changes: 0 additions & 3 deletions leather/data_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

import six

from leather.scales.linear import LinearScale
from leather.scales.ordinal import OrdinalScale


class DataType(object):
"""
Expand Down
37 changes: 6 additions & 31 deletions leather/lattice.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#!/usr/bin/env python

from leather.chart import Chart
from leather.data_types import Number, Text
from leather.grid import Grid
from leather.scales.linear import LinearScale
from leather.scales.ordinal import OrdinalScale
from leather.scales import Scale
from leather.series import Series
from leather.utils import X, Y

Expand All @@ -22,35 +20,14 @@ def __init__(self, data, shape):

def _validate_dimension(self, dimension, chart_series):
scale = None
axis = None

data_type = chart_series[0].types[dimension]

for series in chart_series[1:]:
if series.types[dimension] is not data_type:
raise TypeError('All series must have the same data types.')

# Default Number scale is Linear
if data_type is Number:
data_min = min([series.min(dimension) for series in chart_series])
data_max = max([series.max(dimension) for series in chart_series])

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

for series in chart_series:
if not scale_values:
scale_values = series.values(dimension)
continue

if series.values(dimension) != scale_values:
raise ValueError('Mismatched series scales')

scale = OrdinalScale(scale_values)

return (scale, axis)
return Scale.infer(chart_series, dimension, data_type)

def to_svg(self, path, width=1200, height=1200):
"""
Expand All @@ -59,20 +36,18 @@ def to_svg(self, path, width=1200, height=1200):
chart_series = []

for seq in self._data:
chart_series.append(Series(seq))
chart_series.append(Series(seq, self._shape))

x_scale, x_axis = self._validate_dimension(X, chart_series)
y_scale, y_axis = self._validate_dimension(Y, chart_series)
x_scale = self._validate_dimension(X, chart_series)
y_scale = self._validate_dimension(Y, chart_series)

grid = Grid()

for series in chart_series:
chart = Chart()
chart.set_x_scale(x_scale)
chart.set_y_scale(y_scale)
chart.set_x_axis(x_axis)
chart.set_y_axis(y_axis)
chart.add_series(series, self._shape)
chart.add_series(series)

grid.add_chart(chart)

Expand Down
5 changes: 5 additions & 0 deletions leather/scales/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python

from leather.scales.base import Scale
from leather.scales.linear import Linear
from leather.scales.ordinal import Ordinal
54 changes: 54 additions & 0 deletions leather/scales/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,63 @@
#!/usr/bin/env python

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


class Scale(object):
"""
Base class for various kinds of scale objects.
"""
@classmethod
def infer(cls, series_list, dimension, data_type):
"""
Infer's an appropriate default scale for a given sequence of series.
:param chart_series:
A sequence of :code:`(series, shape)` pairs.
:param dimension:
The dimension, :code:`X` or :code:`Y` of the data to infer for.
"""
from leather.scales.linear import Linear
from leather.scales.ordinal import Ordinal

# Default Number scale is Linear
if data_type is Number:
force_zero = False
data_min = 0
data_max = 0

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

data_min = min(data_min, series.min(dimension))
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

for series in series_list:
if not scale_values:
scale_values = series.values(dimension)
continue

if series.values(dimension) != scale_values:
raise ValueError('Mismatched series scales')

scale = Ordinal(scale_values)

return scale

def project(self, value, target_range):
raise NotImplementedError

Expand Down
3 changes: 2 additions & 1 deletion leather/scales/linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from leather.scales.base import Scale

class LinearScale(Scale):

class Linear(Scale):
"""
A scale that linearly maps values from a domain to a range.
"""
Expand Down
2 changes: 1 addition & 1 deletion leather/scales/ordinal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from leather.scales.base import Scale

class OrdinalScale(Scale):
class Ordinal(Scale):
"""
A scale that maps individual values to a range.
"""
Expand Down
9 changes: 8 additions & 1 deletion leather/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ class Series(object):
:param name:
An optional name to be used in labeling this series.
"""
def __init__(self, data, name=None):
def __init__(self, data, shape, name=None):
self.data = data
self.shape = shape
self.name = name

self.types = [None, None]
Expand Down Expand Up @@ -48,3 +49,9 @@ def max(self, dimension):
Compute the minimum value of a given dimension.
"""
return max(self.values(dimension))

def to_svg(self, width, height, x_scale, y_scale):
"""
Render this series to SVG elements using it's assigned shape.
"""
return self.shape.to_svg(width, height, x_scale, y_scale, self)
7 changes: 7 additions & 0 deletions leather/shapes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python

from leather.shapes.base import Shape
from leather.shapes.bars import Bars
from leather.shapes.columns import Columns
from leather.shapes.dots import Dots
from leather.shapes.lines import Lines
74 changes: 37 additions & 37 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,42 +64,42 @@
#
# grid.to_svg('test.svg', 1200, 600)

# data = [[
# (0, 3),
# (4, 5),
# (7, 9),
# (8, 4)
# ], [
# (0, 4),
# (1, 3),
# (2, 3),
# (10, 7),
# (15, 5)
# ], [
# (0, 4),
# (5, 5),
# (6, 6),
# (7, 7),
# (8, 8)
# ], [
# (4, 4),
# (6, 3),
# (7, 5),
# (8, 6),
# (12, 10)
# ]]
#
# lattice = leather.Lattice(data, leather.Line())
#
# lattice.to_svg('test.svg', 1200, 600)
data = [[
(0, 3),
(4, 5),
(7, 9),
(8, 4)
], [
(0, 4),
(1, 3),
(2, 3),
(10, 7),
(15, 5)
], [
(0, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8)
], [
(4, 4),
(6, 3),
(7, 5),
(8, 6),
(12, 10)
]]

lattice = leather.Lattice(data, leather.Lines())

data = [
(3, 'foo'),
(5, 'bing'),
(9, 'baz'),
(4, 'blurg')
]
lattice.to_svg('test.svg', 1200, 600)

chart = leather.Chart()
chart.add_bars(data)
chart.to_svg('test.svg')
# data = [
# (3, 'foo'),
# (5, 'bing'),
# (9, 'baz'),
# (4, 'blurg')
# ]
#
# chart = leather.Chart()
# chart.add_bars(data)
# chart.to_svg('test.svg')

0 comments on commit a2c4457

Please sign in to comment.