Skip to content

Commit

Permalink
Add missing files. Rename scales.
Browse files Browse the repository at this point in the history
  • Loading branch information
onyxfish committed May 26, 2016
1 parent 49cb966 commit 828db2c
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 21 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
-----

* Expanded unit tests for :class:`.Scale` subclasses.
* Implemented :class:`.Monthly` scale type.
* Implemented :class:`.Annual` scale type.
* Implemented :class:`.Months` scale type.
* Implemented :class:`.Years` scale type.
* Zero lines now render above other tick marks. (#31)
* Fixed rendering of :class:`.Bar` and :class:`.Column` shapes for negative values. (#52)
* Refactored the :class:`.Lattice` API.
Expand Down
4 changes: 3 additions & 1 deletion docs/api/scales.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ Scales

.. autoclass:: leather.Temporal

.. autoclass:: leather.Annual
.. autoclass:: leather.Years

.. autoclass:: leather.Months
2 changes: 1 addition & 1 deletion leather/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from leather.grid import Grid
from leather.lattice import Lattice
from leather.legend import Legend
from leather.scales import Scale, Annual, Linear, Monthly, Ordinal, Temporal
from leather.scales import Scale, Linear, Months, Ordinal, Temporal, Years
from leather.series import Series, key_function
from leather.shapes import Shape, Bars, Columns, Dots, Lines, style_function
from leather import theme
4 changes: 2 additions & 2 deletions leather/scales/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python

from leather.scales.annual import Annual
from leather.scales.base import Scale
from leather.scales.linear import Linear
from leather.scales.monthly import Monthly
from leather.scales.months import Months
from leather.scales.ordinal import Ordinal
from leather.scales.temporal import Temporal
from leather.scales.years import Years
100 changes: 100 additions & 0 deletions leather/scales/months.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python

from datetime import datetime, date
import math

from leather.scales.temporal import Temporal


def to_month_count(d):
return (d.year * 12) + d.month


class Months(Temporal):
"""
A scale that maps years and months to a coordinate range.
This scale takes dates and datetimes, but treats them as ordinal values for
purposes of projection. Thus you can use this scale to render
:class:`.Bars` or :class:`.Columns` for yearly data.
:param domain_min:
The minimum value of the domain.
:param domain_max:
The maximum value of the domain.
"""
def __init__(self, domain_min, domain_max):
self._min = self._value_as_date(domain_min)
self._max = self._value_as_date(domain_max)

def _value_as_date(self, value):
"""
Convert a date or number to a date for consistent logic.
"""
if isinstance(value, (datetime, date)):
return value

raise ValueError('Unsupported domain value for Annual scale.')

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

segments = to_month_count(self._max) - to_month_count(self._min) + 1
segment_size = float(range_max - range_min) / segments

pos = to_month_count(d) - to_month_count(self._min)

return range_min + ((pos + 0.5) * segment_size)

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`.
"""
d = self._value_as_date(value)

segments = to_month_count(self._max) - to_month_count(self._min) + 1
segment_size = float(range_max - range_min) / segments
gap = segment_size * 0.05

pos = to_month_count(d) - to_month_count(self._min)
a = pos
b = pos

if pos >= 0:
b += 1
else:
a -= 1

a = range_min + (a * segment_size) + gap
b = range_min + (b * segment_size) - gap

return (a, b)

def ticks(self, count):
"""
Generate a series of ticks for this scale.
"""
a = to_month_count(self._min)
b = to_month_count(self._max)

size = int(math.ceil(float(b - a) / (count - 1)))
values = []

for i in range(count):
month_count = a + (size * i)
years = month_count // 12
months = month_count % 12

values.append(date(years, months, 1))

return values

def format_tick(self, value, i, count):
"""
Display only year component.
"""
return '%i-%i' % (value.year, value.month + 1)
2 changes: 1 addition & 1 deletion leather/scales/annual.py → leather/scales/years.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from leather.scales.temporal import Temporal


class Annual(Temporal):
class Years(Temporal):
"""
A scale that maps years to a coordinate range.
Expand Down
2 changes: 1 addition & 1 deletion test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
chart.add_columns(data)
chart.add_lines(data)
chart.add_dots(data)
chart.set_x_scale(leather.Monthly(datetime.date(2010, 1, 1), datetime.date(2014, 1, 1)))
chart.set_x_scale(leather.Months(datetime.date(2010, 1, 1), datetime.date(2014, 1, 1)))
chart.to_svg('test.svg')
26 changes: 13 additions & 13 deletions tests/test_scales.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,33 +106,33 @@ def test_ticks(self):
self.assertEqual(ticks[-1], date(2014, 1, 1))


class TestAnnual(XMLTest):
class TestYears(XMLTest):
def test_project(self):
scale = leather.Annual(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Years(date(2010, 1, 1), date(2014, 1, 1))

self.assertEqual(scale.project(date(2011, 1, 1), 0, 20), 6)
self.assertEqual(scale.project(date(2012, 1, 1), 0, 20), 10)
self.assertEqual(scale.project(date(2009, 1, 1), 0, 20), -6)

scale = leather.Annual(datetime(2010, 1, 1), datetime(2014, 1, 1))
scale = leather.Years(datetime(2010, 1, 1), datetime(2014, 1, 1))

self.assertEqual(scale.project(datetime(2011, 1, 1), 0, 20), 6)
self.assertEqual(scale.project(datetime(2012, 1, 1), 0, 20), 10)
self.assertEqual(scale.project(datetime(2009, 1, 1), 0, 20), -6)

scale = leather.Annual(2010, 2014)
scale = leather.Years(2010, 2014)

self.assertEqual(scale.project(2011, 0, 20), 6)
self.assertEqual(scale.project(2012, 0, 20), 10)
self.assertEqual(scale.project(2009, 0, 20), -6)

def test_project_interval(self):
scale = leather.Annual(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Years(date(2010, 1, 1), date(2014, 1, 1))

self.assertEqual(scale.project_interval(date(2011, 1, 1), 0, 20), (4.2, 7.8))

def test_ticks(self):
scale = leather.Annual(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Years(date(2010, 1, 1), date(2014, 1, 1))

self.assertEqual(scale.ticks(5), [
date(2010, 1, 1),
Expand All @@ -143,35 +143,35 @@ def test_ticks(self):
])


class TestMonthly(XMLTest):
class TestMonths(XMLTest):
"""
See notes for :class:`.TestTemporal`.
"""
def test_project(self):
scale = leather.Monthly(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Months(date(2010, 1, 1), date(2014, 1, 1))

self.assertAlmostEqual(scale.project(date(2011, 1, 1), 0, 48), 12, 0)
self.assertAlmostEqual(scale.project(date(2012, 1, 1), 0, 48), 24, 0)
self.assertAlmostEqual(scale.project(date(2008, 12, 1), 0, 48), -12, 0)

scale = leather.Monthly(datetime(2010, 1, 1), datetime(2014, 1, 1))
scale = leather.Months(datetime(2010, 1, 1), datetime(2014, 1, 1))

self.assertAlmostEqual(scale.project(datetime(2011, 1, 1), 0, 48), 12, 0)
self.assertAlmostEqual(scale.project(datetime(2012, 1, 1), 0, 48), 24, 0)
self.assertAlmostEqual(scale.project(datetime(2008, 12, 1), 0, 48), -12, 0)

with self.assertRaises(ValueError):
scale = leather.Monthly(2010, 2014)
scale = leather.Months(2010, 2014)

def test_project_interval(self):
scale = leather.Monthly(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Months(date(2010, 1, 1), date(2014, 1, 1))

a, b = scale.project_interval(date(2011, 1, 1), 0, 48)
self.assertAlmostEqual(a, 11.5, 0)
self.assertAlmostEqual(b, 12.5, 0)

def test_ticks(self):
scale = leather.Monthly(date(2010, 1, 1), date(2014, 1, 1))
scale = leather.Months(date(2010, 1, 1), date(2014, 1, 1))

self.assertEqual(scale.ticks(5), [
date(2010, 1, 1),
Expand All @@ -181,7 +181,7 @@ def test_ticks(self):
date(2014, 1, 1)
])

scale = leather.Monthly(date(2010, 1, 1), date(2012, 1, 1))
scale = leather.Months(date(2010, 1, 1), date(2012, 1, 1))

self.assertEqual(scale.ticks(5), [
date(2010, 1, 1),
Expand Down

0 comments on commit 828db2c

Please sign in to comment.