mirror of https://github.com/Kozea/pygal.git
Python to generate nice looking SVG graph
http://pygal.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
4.7 KiB
163 lines
4.7 KiB
17 years ago
|
#!python
|
||
|
|
||
|
from itertools import chain, repeat, izip
|
||
17 years ago
|
import datetime
|
||
17 years ago
|
|
||
|
# from itertools recipes (python documentation)
|
||
|
def grouper(n, iterable, padvalue=None):
|
||
17 years ago
|
"""
|
||
|
>>> tuple(grouper(3, 'abcdefg', 'x'))
|
||
|
(('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x'))
|
||
|
"""
|
||
17 years ago
|
return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
|
||
|
|
||
|
def reverse_mapping(mapping):
|
||
|
"""
|
||
|
For every key, value pair, return the mapping for the
|
||
|
equivalent value, key pair
|
||
17 years ago
|
>>> reverse_mapping({'a': 'b'}) == {'b': 'a'}
|
||
|
True
|
||
17 years ago
|
"""
|
||
17 years ago
|
keys, values = zip(*mapping.items())
|
||
17 years ago
|
return dict(zip(values, keys))
|
||
|
|
||
|
def flatten_mapping(mapping):
|
||
|
"""
|
||
|
For every key that has an __iter__ method, assign the values
|
||
|
to a key for each.
|
||
|
>>> flatten_mapping({'ab': 3, ('c','d'): 4}) == {'ab': 3, 'c': 4, 'd': 4}
|
||
|
True
|
||
|
"""
|
||
|
return dict(flatten_items(mapping.items()))
|
||
|
|
||
|
def flatten_items(items):
|
||
|
for keys, value in items:
|
||
|
if hasattr(keys, '__iter__'):
|
||
|
for key in keys:
|
||
|
yield (key, value)
|
||
|
else:
|
||
|
yield (keys, value)
|
||
|
|
||
|
def float_range(start=0, stop=None, step=1):
|
||
17 years ago
|
"""
|
||
|
Much like the built-in function range, but accepts floats
|
||
|
>>> tuple(float_range(0, 9, 1.5))
|
||
|
(0.0, 1.5, 3.0, 4.5, 6.0, 7.5)
|
||
|
"""
|
||
17 years ago
|
start = float(start)
|
||
|
while start < stop:
|
||
|
yield start
|
||
|
start += step
|
||
|
|
||
|
def date_range(start=None, stop=None, step=None):
|
||
17 years ago
|
"""
|
||
|
Much like the built-in function range, but works with dates
|
||
|
>>> my_range = tuple(date_range(datetime.datetime(2005,12,21), datetime.datetime(2005,12,25)))
|
||
|
>>> datetime.datetime(2005,12,21) in my_range
|
||
|
True
|
||
|
>>> datetime.datetime(2005,12,22) in my_range
|
||
|
True
|
||
|
>>> datetime.datetime(2005,12,25) in my_range
|
||
|
False
|
||
|
"""
|
||
|
if step is None: step = datetime.timedelta(days=1)
|
||
17 years ago
|
if start is None: start = datetime.datetime.now()
|
||
|
while start < stop:
|
||
|
yield start
|
||
|
start += step
|
||
17 years ago
|
|
||
|
# copied from jaraco.datetools
|
||
|
def divide_timedelta_float(td, divisor):
|
||
|
"""
|
||
|
Meant to work around the limitation that Python datetime doesn't support
|
||
|
floats as divisors or multiplicands to datetime objects
|
||
|
>>> one_day = datetime.timedelta(days=1)
|
||
|
>>> half_day = datetime.timedelta(days=.5)
|
||
|
>>> divide_timedelta_float(one_day, 2.0) == half_day
|
||
|
True
|
||
|
>>> divide_timedelta_float(one_day, 2) == half_day
|
||
|
False
|
||
|
"""
|
||
|
# td is comprised of days, seconds, microseconds
|
||
|
dsm = [getattr(td, attr) for attr in ('days', 'seconds', 'microseconds')]
|
||
|
dsm = map(lambda elem: elem/divisor, dsm)
|
||
|
return datetime.timedelta(*dsm)
|
||
|
|
||
|
def get_timedelta_total_microseconds(td):
|
||
|
seconds = td.days*86400 + td.seconds
|
||
|
microseconds = td.microseconds + seconds*(10**6)
|
||
|
return microseconds
|
||
|
|
||
|
def divide_timedelta(td1, td2):
|
||
|
"""
|
||
|
Get the ratio of two timedeltas
|
||
|
>>> one_day = datetime.timedelta(days=1)
|
||
|
>>> one_hour = datetime.timedelta(hours=1)
|
||
|
>>> divide_timedelta(one_hour, one_day) == 1/24.0
|
||
|
True
|
||
|
"""
|
||
|
|
||
|
td1_total = float(get_timedelta_total_microseconds(td1))
|
||
|
td2_total = float(get_timedelta_total_microseconds(td2))
|
||
|
return td1_total/td2_total
|
||
|
|
||
|
class TimeScale(object):
|
||
|
"Describes a scale factor based on time instead of a scalar"
|
||
|
def __init__(self, width, range):
|
||
|
self.width = width
|
||
|
self.range = range
|
||
|
|
||
|
def __mul__(self, delta):
|
||
|
scale = divide_timedelta(delta, self.range)
|
||
17 years ago
|
return scale*self.width
|
||
|
|
||
|
# the following three functions were copied from jaraco.util.iter_
|
||
|
|
||
|
# todo, factor out caching capability
|
||
|
class iterable_test(dict):
|
||
|
"Test objects for iterability, caching the result by type"
|
||
|
def __init__(self, ignore_classes=(basestring,)):
|
||
|
"""ignore_classes must include basestring, because if a string
|
||
|
is iterable, so is a single character, and the routine runs
|
||
|
into an infinite recursion"""
|
||
|
assert basestring in ignore_classes, 'basestring must be in ignore_classes'
|
||
|
self.ignore_classes = ignore_classes
|
||
|
|
||
|
def __getitem__(self, candidate):
|
||
|
return dict.get(self, type(candidate)) or self._test(candidate)
|
||
|
|
||
|
def _test(self, candidate):
|
||
|
try:
|
||
|
if isinstance(candidate, self.ignore_classes):
|
||
|
raise TypeError
|
||
|
iter(candidate)
|
||
|
result = True
|
||
|
except TypeError:
|
||
|
result = False
|
||
|
self[type(candidate)] = result
|
||
|
return result
|
||
|
|
||
|
def iflatten(subject, test=None):
|
||
|
if test is None:
|
||
|
test = iterable_test()
|
||
|
if not test[subject]:
|
||
|
yield subject
|
||
|
else:
|
||
|
for elem in subject:
|
||
|
for subelem in iflatten(elem, test):
|
||
|
yield subelem
|
||
|
|
||
|
def flatten(subject, test=None):
|
||
|
"""flatten an iterable with possible nested iterables.
|
||
|
Adapted from
|
||
|
http://mail.python.org/pipermail/python-list/2003-November/233971.html
|
||
|
>>> flatten(['a','b',['c','d',['e','f'],'g'],'h']) == ['a','b','c','d','e','f','g','h']
|
||
|
True
|
||
|
|
||
|
Note this will normally ignore string types as iterables.
|
||
|
>>> flatten(['ab', 'c'])
|
||
|
['ab', 'c']
|
||
|
"""
|
||
|
return list(iflatten(subject, test))
|
||
|
|