Browse Source

Document pygal.graph package

pull/242/head
Florian Mounier 10 years ago
parent
commit
fcd3460089
  1. 3
      pygal/__init__.py
  2. 4
      pygal/graph/__init__.py
  3. 13
      pygal/graph/bar.py
  4. 11
      pygal/graph/base.py
  5. 17
      pygal/graph/box.py
  6. 10
      pygal/graph/dot.py
  7. 16
      pygal/graph/funnel.py
  8. 14
      pygal/graph/gauge.py
  9. 23
      pygal/graph/graph.py
  10. 20
      pygal/graph/histogram.py
  11. 14
      pygal/graph/horizontal.py
  12. 7
      pygal/graph/horizontalbar.py
  13. 6
      pygal/graph/horizontalstackedbar.py
  14. 14
      pygal/graph/line.py
  15. 13
      pygal/graph/map.py
  16. 8
      pygal/graph/pie.py
  17. 9
      pygal/graph/public.py
  18. 56
      pygal/graph/pyramid.py
  19. 16
      pygal/graph/radar.py
  20. 13
      pygal/graph/stackedbar.py
  21. 16
      pygal/graph/stackedline.py
  22. 23
      pygal/graph/time.py
  23. 7
      pygal/graph/treemap.py
  24. 63
      pygal/graph/verticalpyramid.py
  25. 22
      pygal/graph/xy.py
  26. 4
      pygal/maps/__init__.py

3
pygal/__init__.py

@ -36,13 +36,12 @@ from pygal.graph.horizontalbar import HorizontalBar
from pygal.graph.horizontalstackedbar import HorizontalStackedBar from pygal.graph.horizontalstackedbar import HorizontalStackedBar
from pygal.graph.line import Line from pygal.graph.line import Line
from pygal.graph.pie import Pie from pygal.graph.pie import Pie
from pygal.graph.pyramid import Pyramid from pygal.graph.pyramid import Pyramid, VerticalPyramid
from pygal.graph.radar import Radar from pygal.graph.radar import Radar
from pygal.graph.stackedbar import StackedBar from pygal.graph.stackedbar import StackedBar
from pygal.graph.stackedline import StackedLine from pygal.graph.stackedline import StackedLine
from pygal.graph.time import DateLine, DateTimeLine, TimeLine, TimeDeltaLine from pygal.graph.time import DateLine, DateTimeLine, TimeLine, TimeDeltaLine
from pygal.graph.treemap import Treemap from pygal.graph.treemap import Treemap
from pygal.graph.verticalpyramid import VerticalPyramid
from pygal.graph.xy import XY from pygal.graph.xy import XY
from pygal.graph.graph import Graph from pygal.graph.graph import Graph
from pygal.config import Config from pygal.config import Config

4
pygal/graph/__init__.py

@ -16,7 +16,5 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Graph modules
""" """Graph package containing all builtin charts"""

13
pygal/graph/bar.py

@ -16,9 +16,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Bar chart
"""
Bar chart that presents grouped data with rectangular bars with lengths
proportional to the values that they represent.
""" """
from __future__ import division from __future__ import division
@ -27,16 +28,19 @@ from pygal.util import swap, ident, compute_scale, decorate, alter
class Bar(Graph): class Bar(Graph):
"""Bar graph"""
"""Bar graph class"""
_series_margin = .06 _series_margin = .06
_serie_margin = .06 _serie_margin = .06
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Bar chart creation"""
self._x_ranges = None self._x_ranges = None
super(Bar, self).__init__(*args, **kwargs) super(Bar, self).__init__(*args, **kwargs)
def _bar(self, serie, parent, x, y, i, zero, secondary=False): def _bar(self, serie, parent, x, y, i, zero, secondary=False):
"""Internal bar drawing function"""
width = (self.view.x(1) - self.view.x(0)) / self._len width = (self.view.x(1) - self.view.x(0)) / self._len
x, y = self.view((x, y)) x, y = self.view((x, y))
series_margin = width * self._series_margin series_margin = width * self._series_margin
@ -88,6 +92,7 @@ class Bar(Graph):
self._static_value(serie_node, val, x_center, y_center) self._static_value(serie_node, val, x_center, y_center)
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
if self._min: if self._min:
self._box.ymin = min(self._min, self.zero) self._box.ymin = min(self._min, self.zero)
if self._max: if self._max:
@ -109,6 +114,7 @@ class Bar(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _compute_secondary(self): def _compute_secondary(self):
"""Compute parameters for secondary series rendering"""
if self.secondary_series: if self.secondary_series:
y_pos = list(zip(*self._y_labels))[1] y_pos = list(zip(*self._y_labels))[1]
ymin = self._secondary_min ymin = self._secondary_min
@ -133,6 +139,7 @@ class Bar(Graph):
for y in y_pos] for y in y_pos]
def _plot(self): def _plot(self):
"""Draw bars for series and secondary series"""
for serie in self.series: for serie in self.series:
self.bar(serie) self.bar(serie)
for serie in self.secondary_series: for serie in self.secondary_series:

11
pygal/graph/base.py

@ -16,10 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Base for pygal charts
""" """Base for pygal charts"""
from __future__ import division from __future__ import division
from pygal._compat import is_list_like from pygal._compat import is_list_like
@ -38,11 +36,13 @@ import os
class BaseGraph(object): class BaseGraph(object):
"""Chart internal behaviour related functions""" """Chart internal behaviour related functions"""
_adapters = [] _adapters = []
def __init__(self, config=None, **kwargs): def __init__(self, config=None, **kwargs):
"""Config preparation and various initialization"""
if config: if config:
if isinstance(config, type): if isinstance(config, type):
config = config() config = config()
@ -60,12 +60,14 @@ class BaseGraph(object):
self.xml_filters = [] self.xml_filters = []
def __setattr__(self, name, value): def __setattr__(self, name, value):
"""Set an attribute on the class or in the state if there is one"""
if name.startswith('__') or getattr(self, 'state', None) is None: if name.startswith('__') or getattr(self, 'state', None) is None:
super(BaseGraph, self).__setattr__(name, value) super(BaseGraph, self).__setattr__(name, value)
else: else:
setattr(self.state, name, value) setattr(self.state, name, value)
def __getattribute__(self, name): def __getattribute__(self, name):
"""Get an attribute from the class or from the state if there is one"""
if name.startswith('__') or name == 'state' or getattr( if name.startswith('__') or name == 'state' or getattr(
self, 'state', None self, 'state', None
) is None or name not in self.state.__dict__: ) is None or name not in self.state.__dict__:
@ -173,7 +175,7 @@ class BaseGraph(object):
return series return series
def setup(self, **kwargs): def setup(self, **kwargs):
"""Init the graph""" """Set up the transient state prior rendering"""
# Keep labels in case of map # Keep labels in case of map
if getattr(self, 'x_labels', None) is not None: if getattr(self, 'x_labels', None) is not None:
self.x_labels = list(self.x_labels) self.x_labels = list(self.x_labels)
@ -212,6 +214,7 @@ class BaseGraph(object):
self.svg.pre_render() self.svg.pre_render()
def teardown(self): def teardown(self):
"""Remove the transient state after rendering"""
if os.getenv('PYGAL_KEEP_STATE'): if os.getenv('PYGAL_KEEP_STATE'):
return return

17
pygal/graph/box.py

@ -16,8 +16,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Box plot Box plot: a convenient way to display series as box with whiskers and outliers
Different types are available throught the box_mode option
""" """
from __future__ import division from __future__ import division
@ -28,6 +30,7 @@ from bisect import bisect_left, bisect_right
class Box(Graph): class Box(Graph):
""" """
Box plot Box plot
For each series, shows the median value, the 25th and 75th percentiles, For each series, shows the median value, the 25th and 75th percentiles,
@ -36,10 +39,8 @@ class Box(Graph):
See http://en.wikipedia.org/wiki/Box_plot See http://en.wikipedia.org/wiki/Box_plot
""" """
_series_margin = .06
def __init__(self, *args, **kwargs): _series_margin = .06
super(Box, self).__init__(*args, **kwargs)
@property @property
def _format(self): def _format(self):
@ -91,9 +92,7 @@ class Box(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _plot(self): def _plot(self):
""" """Plot the series data"""
Plot the series data
"""
for serie in self.series: for serie in self.series:
self._boxf(serie) self._boxf(serie)
@ -103,9 +102,7 @@ class Box(Graph):
return 7 return 7
def _boxf(self, serie): def _boxf(self, serie):
""" """For a specific series, draw the box plot."""
For a specific series, draw the box plot.
"""
serie_node = self.svg.serie(serie) serie_node = self.svg.serie(serie)
# Note: q0 and q4 do not literally mean the zero-th quartile # Note: q0 and q4 do not literally mean the zero-th quartile
# and the fourth quartile, but rather the distance from 1.5 times # and the fourth quartile, but rather the distance from 1.5 times

10
pygal/graph/dot.py

@ -16,9 +16,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Dot chart
"""
Dot chart displaying values as a grid of dots, the bigger the value
the bigger the dot
""" """
from __future__ import division from __future__ import division
@ -29,7 +30,8 @@ from math import log10
class Dot(Graph): class Dot(Graph):
"""Dot graph"""
"""Dot graph class"""
def dot(self, serie, r_max): def dot(self, serie, r_max):
"""Draw a dot line""" """Draw a dot line"""
@ -68,6 +70,7 @@ class Dot(Graph):
self._static_value(serie_node, value, x, y) self._static_value(serie_node, value, x, y)
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
x_len = self._len x_len = self._len
y_len = self._order y_len = self._order
self._box.xmax = x_len self._box.xmax = x_len
@ -106,6 +109,7 @@ class Dot(Graph):
else (max(map(abs, self._values)) if self._values else None)) else (max(map(abs, self._values)) if self._values else None))
def _plot(self): def _plot(self):
"""Plot all dots for series"""
r_max = min( r_max = min(
self.view.x(1) - self.view.x(0), self.view.x(1) - self.view.x(0),
(self.view.y(0) or 0) - self.view.y(1)) / ( (self.view.y(0) or 0) - self.view.y(1)) / (

16
pygal/graph/funnel.py

@ -16,10 +16,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Funnel chart: Represent values as a funnel"""
Funnel chart
"""
from __future__ import division from __future__ import division
from pygal.util import decorate, cut, compute_scale, alter from pygal.util import decorate, cut, compute_scale, alter
@ -28,15 +25,18 @@ from pygal.graph.graph import Graph
class Funnel(Graph): class Funnel(Graph):
"""Funnel graph"""
"""Funnel graph class"""
_adapters = [positive, none_to_zero] _adapters = [positive, none_to_zero]
def _format(self, value): def _format(self, value):
return super(Funnel, self)._format(abs(value)) """Return the value formatter for this graph here its absolute value"""
value = value and abs(value)
return super(Funnel, self)._format(value)
def funnel(self, serie): def funnel(self, serie):
"""Draw a dot line""" """Draw a funnel slice"""
serie_node = self.svg.serie(serie) serie_node = self.svg.serie(serie)
fmt = lambda x: '%f %f' % x fmt = lambda x: '%f %f' % x
for i, poly in enumerate(serie.points): for i, poly in enumerate(serie.points):
@ -60,6 +60,7 @@ class Funnel(Graph):
self._static_value(serie_node, value, x, y) self._static_value(serie_node, value, x, y)
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
x_pos = [ x_pos = [
(x + 1) / self._order for x in range(self._order) (x + 1) / self._order for x in range(self._order)
] if self._order != 1 else [.5] # Center if only one value ] if self._order != 1 else [.5] # Center if only one value
@ -94,5 +95,6 @@ class Funnel(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _plot(self): def _plot(self):
"""Plot the funnel"""
for serie in self.series: for serie in self.series:
self.funnel(serie) self.funnel(serie)

14
pygal/graph/gauge.py

@ -16,10 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Gauge chart
""" """Gauge chart representing values as needles on a polar scale"""
from __future__ import division from __future__ import division
from pygal.util import decorate, compute_scale, alter from pygal.util import decorate, compute_scale, alter
@ -28,10 +26,13 @@ from pygal.graph.graph import Graph
class Gauge(Graph): class Gauge(Graph):
"""Gauge graph"""
"""Gauge graph class"""
needle_width = 1 / 20 needle_width = 1 / 20
def _set_view(self): def _set_view(self):
"""Assign a view to current graph"""
if self.logarithmic: if self.logarithmic:
view_class = PolarThetaLogView view_class = PolarThetaLogView
else: else:
@ -43,6 +44,7 @@ class Gauge(Graph):
self._box) self._box)
def needle(self, serie): def needle(self, serie):
"""Draw a needle for each value"""
serie_node = self.svg.serie(serie) serie_node = self.svg.serie(serie)
for i, theta in enumerate(serie.values): for i, theta in enumerate(serie.values):
if theta is None: if theta is None:
@ -87,6 +89,7 @@ class Gauge(Graph):
self._static_value(serie_node, value, x, y) self._static_value(serie_node, value, x, y)
def _y_axis(self, draw_axes=True): def _y_axis(self, draw_axes=True):
"""Override y axis to plot a polar axis"""
axis = self.svg.node(self.nodes['plot'], class_="axis y x gauge") axis = self.svg.node(self.nodes['plot'], class_="axis y x gauge")
for i, (label, theta) in enumerate(self._y_labels): for i, (label, theta) in enumerate(self._y_labels):
@ -112,11 +115,13 @@ class Gauge(Graph):
).text = label ).text = label
def _x_axis(self, draw_axes=True): def _x_axis(self, draw_axes=True):
"""Override x axis to put a center circle in center"""
axis = self.svg.node(self.nodes['plot'], class_="axis x gauge") axis = self.svg.node(self.nodes['plot'], class_="axis x gauge")
x, y = self.view((0, 0)) x, y = self.view((0, 0))
self.svg.node(axis, 'circle', cx=x, cy=y, r=4) self.svg.node(axis, 'circle', cx=x, cy=y, r=4)
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
self.min_ = self._min or 0 self.min_ = self._min or 0
self.max_ = self._max or 0 self.max_ = self._max or 0
if self.max_ - self.min_ == 0: if self.max_ - self.min_ == 0:
@ -135,5 +140,6 @@ class Gauge(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _plot(self): def _plot(self):
"""Plot all needles"""
for serie in self.series: for serie in self.series:
self.needle(serie) self.needle(serie)

23
pygal/graph/graph.py

@ -16,10 +16,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Chart properties and drawing"""
Commmon graphing functions
"""
from __future__ import division from __future__ import division
from pygal._compat import is_list_like from pygal._compat import is_list_like
@ -35,7 +32,9 @@ from itertools import repeat, chain
class Graph(PublicApi): class Graph(PublicApi):
"""Graph super class containing generic common functions""" """Graph super class containing generic common functions"""
_dual = False _dual = False
def _decorate(self): def _decorate(self):
@ -427,12 +426,14 @@ class Graph(PublicApi):
**self.interpolation_parameters)) **self.interpolation_parameters))
def _rescale(self, points): def _rescale(self, points):
"""Scale for secondary"""
return [ return [
(x, self._scale_diff + (y - self._scale_min_2nd) * self._scale (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale
if y is not None else None) if y is not None else None)
for x, y in points] for x, y in points]
def _tooltip_data(self, node, value, x, y, classes=None): def _tooltip_data(self, node, value, x, y, classes=None):
"""Insert in desc tags informations for the javascript tooltip"""
self.svg.node(node, 'desc', class_="value").text = value self.svg.node(node, 'desc', class_="value").text = value
if classes is None: if classes is None:
classes = [] classes = []
@ -448,6 +449,7 @@ class Graph(PublicApi):
class_="y " + classes).text = str(y) class_="y " + classes).text = str(y)
def _static_value(self, serie_node, value, x, y): def _static_value(self, serie_node, value, x, y):
"""Write the print value"""
if self.print_values: if self.print_values:
self.svg.node( self.svg.node(
serie_node['text_overlay'], 'text', serie_node['text_overlay'], 'text',
@ -461,6 +463,10 @@ class Graph(PublicApi):
return self._format(values[i][1]) return self._format(values[i][1])
def _points(self, x_pos): def _points(self, x_pos):
"""
Convert given data values into drawable points (x, y)
and interpolated points if interpolate option is specified
"""
for serie in self.all_series: for serie in self.all_series:
serie.points = [ serie.points = [
(x_pos[i], v) (x_pos[i], v)
@ -471,6 +477,7 @@ class Graph(PublicApi):
serie.interpolated = [] serie.interpolated = []
def _compute_secondary(self): def _compute_secondary(self):
"""Compute secondary axis min max and label positions"""
# secondary y axis support # secondary y axis support
if self.secondary_series and self._y_labels: if self.secondary_series and self._y_labels:
y_pos = list(zip(*self._y_labels))[1] y_pos = list(zip(*self._y_labels))[1]
@ -492,10 +499,12 @@ class Graph(PublicApi):
self._scale_min_2nd = ymin self._scale_min_2nd = ymin
def _post_compute(self): def _post_compute(self):
"""Hook called after compute and before margin computations and plot"""
pass pass
@property @property
def all_series(self): def all_series(self):
"""Getter for all series (nomal and secondary)"""
return self.series + self.secondary_series return self.series + self.secondary_series
@property @property
@ -721,7 +730,9 @@ class Graph(PublicApi):
def _has_data(self): def _has_data(self):
"""Check if there is any data""" """Check if there is any data"""
return any([ return any([
len([v for v in ( len([v for a in (
s[1] if is_list_like(s) else [s]) if v is not None]) s[1] if is_list_like(s) else [s])
for v in (a if self._dual else [a])
if v is not None])
for s in self.raw_series for s in self.raw_series
]) ])

20
pygal/graph/histogram.py

@ -17,18 +17,20 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Histogram chart Histogram chart: like a bar chart but with data plotted along a x axis
as bars of varying width.
""" """
from __future__ import division from __future__ import division
from pygal._compat import is_list_like
from pygal.graph.graph import Graph from pygal.graph.graph import Graph
from pygal.util import ( from pygal.util import (
swap, ident, compute_scale, decorate, cached_property, alter) swap, ident, compute_scale, decorate, cached_property, alter)
class Histogram(Graph): class Histogram(Graph):
"""Histogram chart"""
"""Histogram chart class"""
_dual = True _dual = True
_series_margin = 0 _series_margin = 0
@ -43,6 +45,7 @@ class Histogram(Graph):
@cached_property @cached_property
def xvals(self): def xvals(self):
"""All x values"""
return [val return [val
for serie in self.all_series for serie in self.all_series
for dval in serie.values for dval in serie.values
@ -51,19 +54,14 @@ class Histogram(Graph):
@cached_property @cached_property
def yvals(self): def yvals(self):
"""All y values"""
return [val[0] return [val[0]
for serie in self.series for serie in self.series
for val in serie.values for val in serie.values
if val[0] is not None] if val[0] is not None]
def _has_data(self):
"""Check if there is any data"""
return sum(
map(len, map(lambda s: s.safe_values, self.series))) != 0 and any((
sum(map(abs, self.xvals)) != 0,
sum(map(abs, self.yvals)) != 0))
def _bar(self, serie, parent, x0, x1, y, i, zero, secondary=False): def _bar(self, serie, parent, x0, x1, y, i, zero, secondary=False):
"""Internal bar drawing function"""
x, y = self.view((x0, y)) x, y = self.view((x0, y))
x1, _ = self.view((x1, y)) x1, _ = self.view((x1, y))
width = x1 - x width = x1 - x
@ -104,6 +102,7 @@ class Histogram(Graph):
self._static_value(serie_node, val, x_center, y_center) self._static_value(serie_node, val, x_center, y_center)
def _compute(self): def _compute(self):
"""Compute x/y min and max and x/y scale and set labels"""
if self.xvals: if self.xvals:
xmin = min(self.xvals) xmin = min(self.xvals)
xmax = max(self.xvals) xmax = max(self.xvals)
@ -139,6 +138,7 @@ class Histogram(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _plot(self): def _plot(self):
"""Draw bars for series and secondary series"""
for serie in self.series: for serie in self.series:
self.bar(serie) self.bar(serie)
for serie in self.secondary_series: for serie in self.secondary_series:

14
pygal/graph/horizontal.py

@ -16,32 +16,34 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """Horizontal graph mixin"""
Horizontal graph base
"""
from pygal.graph.graph import Graph from pygal.graph.graph import Graph
from pygal.view import HorizontalView, HorizontalLogView from pygal.view import HorizontalView, HorizontalLogView
class HorizontalGraph(Graph): class HorizontalGraph(Graph):
"""Horizontal graph"""
"""Horizontal graph mixin"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Set the horizontal flag to True"""
self.horizontal = True self.horizontal = True
super(HorizontalGraph, self).__init__(*args, **kwargs) super(HorizontalGraph, self).__init__(*args, **kwargs)
def _post_compute(self): def _post_compute(self):
"""After computations transpose labels"""
self._x_labels, self._y_labels = self._y_labels, self._x_labels self._x_labels, self._y_labels = self._y_labels, self._x_labels
self._x_2nd_labels, self._y_2nd_labels = ( self._x_2nd_labels, self._y_2nd_labels = (
self._y_2nd_labels, self._x_2nd_labels) self._y_2nd_labels, self._x_2nd_labels)
def _axes(self): def _axes(self):
"""Set the _force_vertical flag when rendering axes"""
self.view._force_vertical = True self.view._force_vertical = True
super(HorizontalGraph, self)._axes() super(HorizontalGraph, self)._axes()
self.view._force_vertical = False self.view._force_vertical = False
def _set_view(self): def _set_view(self):
"""Assign a view to current graph""" """Assign a horizontal view to current graph"""
if self.logarithmic: if self.logarithmic:
view_class = HorizontalLogView view_class = HorizontalLogView
else: else:

7
pygal/graph/horizontalbar.py

@ -16,18 +16,19 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Horizontal bar graph
""" """Horizontal bar graph"""
from pygal.graph.horizontal import HorizontalGraph from pygal.graph.horizontal import HorizontalGraph
from pygal.graph.bar import Bar from pygal.graph.bar import Bar
class HorizontalBar(HorizontalGraph, Bar): class HorizontalBar(HorizontalGraph, Bar):
"""Horizontal Bar graph""" """Horizontal Bar graph"""
def _plot(self): def _plot(self):
"""Draw the bars in reverse order"""
for serie in self.series[::-1]: for serie in self.series[::-1]:
self.bar(serie) self.bar(serie)
for serie in self.secondary_series[::-1]: for serie in self.secondary_series[::-1]:

6
pygal/graph/horizontalstackedbar.py

@ -16,13 +16,13 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Horizontal stacked graph
""" """Horizontal stacked graph"""
from pygal.graph.horizontal import HorizontalGraph from pygal.graph.horizontal import HorizontalGraph
from pygal.graph.stackedbar import StackedBar from pygal.graph.stackedbar import StackedBar
class HorizontalStackedBar(HorizontalGraph, StackedBar): class HorizontalStackedBar(HorizontalGraph, StackedBar):
"""Horizontal Stacked Bar graph""" """Horizontal Stacked Bar graph"""

14
pygal/graph/line.py

@ -16,24 +16,29 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Line chart
""" """
Line chart: Display series of data as markers (dots)
connected by straight segments
"""
from __future__ import division from __future__ import division
from pygal.graph.graph import Graph from pygal.graph.graph import Graph
from pygal.util import cached_property, compute_scale, decorate, alter from pygal.util import cached_property, compute_scale, decorate, alter
class Line(Graph): class Line(Graph):
"""Line graph"""
"""Line graph class"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Set _self_close as False, it's True for Radar like Line"""
self._self_close = False self._self_close = False
super(Line, self).__init__(*args, **kwargs) super(Line, self).__init__(*args, **kwargs)
@cached_property @cached_property
def _values(self): def _values(self):
"""Getter for series values (flattened)"""
return [ return [
val[1] val[1]
for serie in self.series for serie in self.series
@ -43,6 +48,7 @@ class Line(Graph):
@cached_property @cached_property
def _secondary_values(self): def _secondary_values(self):
"""Getter for secondary series values (flattened)"""
return [ return [
val[1] val[1]
for serie in self.secondary_series for serie in self.secondary_series
@ -132,6 +138,7 @@ class Line(Graph):
class_='line reactive' + (' nofill' if not serie.fill else '')) class_='line reactive' + (' nofill' if not serie.fill else ''))
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
# X Labels # X Labels
x_pos = [ x_pos = [
x / (self._len - 1) for x in range(self._len) x / (self._len - 1) for x in range(self._len)
@ -171,6 +178,7 @@ class Line(Graph):
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))
def _plot(self): def _plot(self):
"""Plot the serie lines and secondary serie lines"""
for serie in self.series: for serie in self.series:
self.line(serie) self.line(serie)

13
pygal/graph/map.py

@ -17,6 +17,12 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
pygal contains no map but a base class to create extension
see the pygal_maps_world package to get an exemple.
https://github.com/Kozea/pygal_maps_world
"""
from __future__ import division from __future__ import division
from pygal.graph.graph import Graph from pygal.graph.graph import Graph
from pygal.util import cut, cached_property, decorate from pygal.util import cut, cached_property, decorate
@ -24,7 +30,9 @@ from pygal.etree import etree
class BaseMap(Graph): class BaseMap(Graph):
"""Base map."""
"""Base class for maps"""
_dual = True _dual = True
@cached_property @cached_property
@ -36,12 +44,15 @@ class BaseMap(Graph):
if val[1] is not None] if val[1] is not None]
def enumerate_values(self, serie): def enumerate_values(self, serie):
"""Hook to replace default enumeration on values"""
return enumerate(serie.values) return enumerate(serie.values)
def adapt_code(self, area_code): def adapt_code(self, area_code):
"""Hook to change the area code"""
return area_code return area_code
def _plot(self): def _plot(self):
"""Insert a map in the chart and apply data on it"""
map = etree.fromstring(self.svg_map) map = etree.fromstring(self.svg_map)
map.set('width', str(self.view.width)) map.set('width', str(self.view.width))
map.set('height', str(self.view.height)) map.set('height', str(self.view.height))

8
pygal/graph/pie.py

@ -17,8 +17,8 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Pie chart Pie chart: A circular chart divided into slice to illustrate proportions
It can be made as a donut or a half pie.
""" """
from __future__ import division from __future__ import division
@ -29,7 +29,8 @@ from math import pi
class Pie(Graph): class Pie(Graph):
"""Pie graph"""
"""Pie graph class"""
_adapters = [positive, none_to_zero] _adapters = [positive, none_to_zero]
@ -94,6 +95,7 @@ class Pie(Graph):
return serie_angle return serie_angle
def _plot(self): def _plot(self):
"""Draw all the serie slices"""
total = sum(map(sum, map(lambda x: x.values, self.series))) total = sum(map(sum, map(lambda x: x.values, self.series)))
if total == 0: if total == 0:
return return

9
pygal/graph/public.py

@ -16,10 +16,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """pygal public api functions"""
pygal public api functions
"""
import io import io
from pygal._compat import u, is_list_like from pygal._compat import u, is_list_like
@ -27,6 +24,7 @@ from pygal.graph.base import BaseGraph
class PublicApi(BaseGraph): class PublicApi(BaseGraph):
"""Chart public functions""" """Chart public functions"""
def add(self, title, values, **kwargs): def add(self, title, values, **kwargs):
@ -40,6 +38,7 @@ class PublicApi(BaseGraph):
return self return self
def add_xml_filter(self, callback): def add_xml_filter(self, callback):
"""Add an xml filter for in tree post processing"""
self.xml_filters.append(callback) self.xml_filters.append(callback)
return self return self
@ -61,6 +60,7 @@ class PublicApi(BaseGraph):
return svg return svg
def render_table(self, **kwargs): def render_table(self, **kwargs):
"""Render the data as a html table"""
# Import here to avoid lxml import # Import here to avoid lxml import
try: try:
from pygal.table import Table from pygal.table import Table
@ -130,6 +130,7 @@ class PublicApi(BaseGraph):
return chart return chart
def render_sparkline(self, **kwargs): def render_sparkline(self, **kwargs):
"""Render a sparkline"""
spark_options = dict( spark_options = dict(
width=200, width=200,
height=50, height=50,

56
pygal/graph/pyramid.py

@ -16,14 +16,62 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Pyramid chart
"""
Pyramid chart: Stacked bar chart containing only positive values divided by two
axes, generally gender for age pyramid.
""" """
from __future__ import division
from pygal.adapters import positive
from pygal.graph.horizontal import HorizontalGraph from pygal.graph.horizontal import HorizontalGraph
from pygal.graph.verticalpyramid import VerticalPyramid from pygal.graph.stackedbar import StackedBar
class VerticalPyramid(StackedBar):
"""Vertical Pyramid graph class"""
_adapters = [positive]
def _format(self, value):
"""Return the value formatter for this graph here its absolute value"""
value = value and abs(value)
return super(VerticalPyramid, self)._format(value)
def _get_separated_values(self, secondary=False):
"""Separate values between odd and even series stacked"""
series = self.secondary_series if secondary else self.series
positive_vals = map(sum, zip(
*[serie.safe_values
for index, serie in enumerate(series)
if index % 2]))
negative_vals = map(sum, zip(
*[serie.safe_values
for index, serie in enumerate(series)
if not index % 2]))
return list(positive_vals), list(negative_vals)
def _compute_box(self, positive_vals, negative_vals):
"""Compute Y min and max"""
self._box.ymax = max(max(positive_vals or [self.zero]),
max(negative_vals or [self.zero]))
self._box.ymin = - self._box.ymax
def _pre_compute_secondary(self, positive_vals, negative_vals):
"""Compute secondary y min and max"""
self._secondary_max = max(max(positive_vals), max(negative_vals))
self._secondary_min = - self._secondary_max
def _bar(self, serie, parent, x, y, i, zero, secondary=False):
"""Internal stacking bar drawing function"""
if serie.index % 2:
y = -y
return super(VerticalPyramid, self)._bar(
serie, parent, x, y, i, zero, secondary)
class Pyramid(HorizontalGraph, VerticalPyramid): class Pyramid(HorizontalGraph, VerticalPyramid):
"""Horizontal Pyramid graph"""
"""Horizontal Pyramid graph class like the one used by age pyramid"""

16
pygal/graph/radar.py

@ -16,9 +16,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Radar chart
"""
Radar chart: As known as kiviat chart or spider chart is a polar line chart
useful for multivariate observation.
""" """
from __future__ import division from __future__ import division
@ -30,23 +31,28 @@ from math import cos, pi
class Radar(Line): class Radar(Line):
"""Kiviat graph"""
"""Rada graph class"""
_adapters = [positive, none_to_zero] _adapters = [positive, none_to_zero]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Init custom vars"""
self.x_pos = None self.x_pos = None
self._rmax = None self._rmax = None
super(Radar, self).__init__(*args, **kwargs) super(Radar, self).__init__(*args, **kwargs)
def _fill(self, values): def _fill(self, values):
"""Add extra values to fill the line"""
return values return values
def _get_value(self, values, i): def _get_value(self, values, i):
"""Get the value formatted for tooltip"""
return self._format(values[i][0]) return self._format(values[i][0])
@cached_property @cached_property
def _values(self): def _values(self):
"""Getter for series values (flattened)"""
if self.interpolate: if self.interpolate:
return [val[0] for serie in self.series return [val[0] for serie in self.series
for val in serie.interpolated] for val in serie.interpolated]
@ -54,6 +60,7 @@ class Radar(Line):
return super(Line, self)._values return super(Line, self)._values
def _set_view(self): def _set_view(self):
"""Assign a view to current graph"""
if self.logarithmic: if self.logarithmic:
view_class = PolarLogView view_class = PolarLogView
else: else:
@ -65,6 +72,7 @@ class Radar(Line):
self._box) self._box)
def _x_axis(self, draw_axes=True): def _x_axis(self, draw_axes=True):
"""Override x axis to make it polar"""
if not self._x_labels: if not self._x_labels:
return return
@ -114,6 +122,7 @@ class Radar(Line):
deg(angle), format_(pos_text)) deg(angle), format_(pos_text))
def _y_axis(self, draw_axes=True): def _y_axis(self, draw_axes=True):
"""Override y axis to make it polar"""
if not self._y_labels: if not self._y_labels:
return return
@ -156,6 +165,7 @@ class Radar(Line):
).text = label ).text = label
def _compute(self): def _compute(self):
"""Compute r min max and labels position"""
delta = 2 * pi / self._len if self._len else 0 delta = 2 * pi / self._len if self._len else 0
x_pos = [.5 * pi + i * delta for i in range(self._len + 1)] x_pos = [.5 * pi + i * delta for i in range(self._len + 1)]
for serie in self.all_series: for serie in self.all_series:

13
pygal/graph/stackedbar.py

@ -17,8 +17,8 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Stacked Bar chart Stacked Bar chart: Like a bar chart but with all series stacking
on top of the others instead of being displayed side by side.
""" """
from __future__ import division from __future__ import division
@ -28,11 +28,13 @@ from pygal.adapters import none_to_zero
class StackedBar(Bar): class StackedBar(Bar):
"""Stacked Bar graph"""
"""Stacked Bar graph class"""
_adapters = [none_to_zero] _adapters = [none_to_zero]
def _get_separated_values(self, secondary=False): def _get_separated_values(self, secondary=False):
"""Separate values between positives and negatives stacked"""
series = self.secondary_series if secondary else self.series series = self.secondary_series if secondary else self.series
transposed = list(zip(*[serie.values for serie in series])) transposed = list(zip(*[serie.values for serie in series]))
positive_vals = [sum([ positive_vals = [sum([
@ -47,10 +49,12 @@ class StackedBar(Bar):
return positive_vals, negative_vals return positive_vals, negative_vals
def _compute_box(self, positive_vals, negative_vals): def _compute_box(self, positive_vals, negative_vals):
"""Compute Y min and max"""
self._box.ymin = negative_vals and min(min(negative_vals), self.zero) self._box.ymin = negative_vals and min(min(negative_vals), self.zero)
self._box.ymax = positive_vals and max(max(positive_vals), self.zero) self._box.ymax = positive_vals and max(max(positive_vals), self.zero)
def _compute(self): def _compute(self):
"""Compute y min and max and y scale and set labels"""
positive_vals, negative_vals = self._get_separated_values() positive_vals, negative_vals = self._get_separated_values()
self._compute_box(positive_vals, negative_vals) self._compute_box(positive_vals, negative_vals)
@ -88,12 +92,14 @@ class StackedBar(Bar):
self._pre_compute_secondary(positive_vals, negative_vals) self._pre_compute_secondary(positive_vals, negative_vals)
def _pre_compute_secondary(self, positive_vals, negative_vals): def _pre_compute_secondary(self, positive_vals, negative_vals):
"""Compute secondary y min and max"""
self._secondary_min = (negative_vals and min( self._secondary_min = (negative_vals and min(
min(negative_vals), self.zero)) or self.zero min(negative_vals), self.zero)) or self.zero
self._secondary_max = (positive_vals and max( self._secondary_max = (positive_vals and max(
max(positive_vals), self.zero)) or self.zero max(positive_vals), self.zero)) or self.zero
def _bar(self, serie, parent, x, y, i, zero, secondary=False): def _bar(self, serie, parent, x, y, i, zero, secondary=False):
"""Internal stacking bar drawing function"""
if secondary: if secondary:
cumulation = (self.secondary_negative_cumulation cumulation = (self.secondary_negative_cumulation
if y < self.zero else if y < self.zero else
@ -131,6 +137,7 @@ class StackedBar(Bar):
return transpose((x + width / 2, y + height / 2)) return transpose((x + width / 2, y + height / 2))
def _plot(self): def _plot(self):
"""Draw bars for series and secondary series"""
for serie in self.series[::-1 if self.stack_from_top else 1]: for serie in self.series[::-1 if self.stack_from_top else 1]:
self.bar(serie) self.bar(serie)
for serie in self.secondary_series[::-1 if self.stack_from_top else 1]: for serie in self.secondary_series[::-1 if self.stack_from_top else 1]:

16
pygal/graph/stackedline.py

@ -16,25 +16,30 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Stacked Line chart
""" """
Stacked Line chart: Like a line chart but with all lines stacking
on top of the others. Used along fill=True option.
"""
from __future__ import division from __future__ import division
from pygal.graph.line import Line from pygal.graph.line import Line
from pygal.adapters import none_to_zero from pygal.adapters import none_to_zero
class StackedLine(Line): class StackedLine(Line):
"""Stacked Line graph"""
"""Stacked Line graph class"""
_adapters = [none_to_zero] _adapters = [none_to_zero]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Custom variable initialization"""
self._previous_line = None self._previous_line = None
super(StackedLine, self).__init__(*args, **kwargs) super(StackedLine, self).__init__(*args, **kwargs)
def _fill(self, values): def _fill(self, values):
"""Add extra values to fill the line"""
if not self._previous_line: if not self._previous_line:
self._previous_line = values self._previous_line = values
return super(StackedLine, self)._fill(values) return super(StackedLine, self)._fill(values)
@ -43,6 +48,10 @@ class StackedLine(Line):
return new_values return new_values
def _points(self, x_pos): def _points(self, x_pos):
"""
Convert given data values into drawable points (x, y)
and interpolated points if interpolate option is specified
"""
for series_group in (self.series, self.secondary_series): for series_group in (self.series, self.secondary_series):
accumulation = [0] * self._len accumulation = [0] * self._len
for serie in series_group[::-1 if self.stack_from_top else 1]: for serie in series_group[::-1 if self.stack_from_top else 1]:
@ -56,6 +65,7 @@ class StackedLine(Line):
serie.interpolated = [] serie.interpolated = []
def _plot(self): def _plot(self):
"""Plot stacked serie lines and stacked secondary lines"""
for serie in self.series[::-1 if self.stack_from_top else 1]: for serie in self.series[::-1 if self.stack_from_top else 1]:
self.line(serie) self.line(serie)
for serie in self.secondary_series[::-1 if self.stack_from_top else 1]: for serie in self.secondary_series[::-1 if self.stack_from_top else 1]:

23
pygal/graph/time.py

@ -16,9 +16,12 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
""" """
Various datetime line plot XY time extensions: handle convertion of date, time, datetime, timedelta
into float for xy plot and back to their type for display
""" """
from pygal.adapters import positive from pygal.adapters import positive
from pygal.graph.xy import XY from pygal.graph.xy import XY
from datetime import datetime, date, time, timedelta from datetime import datetime, date, time, timedelta
@ -26,36 +29,42 @@ from pygal._compat import timestamp, total_seconds
def datetime_to_timestamp(x): def datetime_to_timestamp(x):
"""Convert a datetime into a utc float timestamp"""
if isinstance(x, datetime): if isinstance(x, datetime):
return timestamp(x) return timestamp(x)
return x return x
def datetime_to_time(x): def datetime_to_time(x):
"""Convert a datetime into a time"""
if isinstance(x, datetime): if isinstance(x, datetime):
return x.time() return x.time()
return x return x
def date_to_datetime(x): def date_to_datetime(x):
"""Convert a date into a datetime"""
if not isinstance(x, datetime) and isinstance(x, date): if not isinstance(x, datetime) and isinstance(x, date):
return datetime.combine(x, time()) return datetime.combine(x, time())
return x return x
def time_to_datetime(x): def time_to_datetime(x):
"""Convert a time into a datetime"""
if isinstance(x, time): if isinstance(x, time):
return datetime.combine(date(1970, 1, 1), x) return datetime.combine(date(1970, 1, 1), x)
return x return x
def timedelta_to_seconds(x): def timedelta_to_seconds(x):
"""Convert a timedelta into an amount of seconds"""
if isinstance(x, timedelta): if isinstance(x, timedelta):
return total_seconds(x) return total_seconds(x)
return x return x
def time_to_seconds(x): def time_to_seconds(x):
"""Convert a time in a seconds sum"""
if isinstance(x, time): if isinstance(x, time):
return (( return ((
((x.hour * 60) + x.minute) * 60 + x.second ((x.hour * 60) + x.minute) * 60 + x.second
@ -64,6 +73,7 @@ def time_to_seconds(x):
def seconds_to_time(x): def seconds_to_time(x):
"""Convert a number of second into a time"""
t = int(x * 10 ** 6) t = int(x * 10 ** 6)
ms = t % 10 ** 6 ms = t % 10 ** 6
t = t // 10 ** 6 t = t // 10 ** 6
@ -76,6 +86,9 @@ def seconds_to_time(x):
class DateTimeLine(XY): class DateTimeLine(XY):
"""DateTime abscissa xy graph class"""
_x_adapters = [datetime_to_timestamp, date_to_datetime] _x_adapters = [datetime_to_timestamp, date_to_datetime]
@property @property
@ -91,6 +104,8 @@ class DateTimeLine(XY):
class DateLine(DateTimeLine): class DateLine(DateTimeLine):
"""Date abscissa xy graph class"""
@property @property
def _x_format(self): def _x_format(self):
"""Return the value formatter for this graph""" """Return the value formatter for this graph"""
@ -103,6 +118,9 @@ class DateLine(DateTimeLine):
class TimeLine(DateTimeLine): class TimeLine(DateTimeLine):
"""Time abscissa xy graph class"""
_x_adapters = [positive, time_to_seconds, datetime_to_time] _x_adapters = [positive, time_to_seconds, datetime_to_time]
@property @property
@ -117,6 +135,9 @@ class TimeLine(DateTimeLine):
class TimeDeltaLine(XY): class TimeDeltaLine(XY):
"""TimeDelta abscissa xy graph class"""
_x_adapters = [timedelta_to_seconds] _x_adapters = [timedelta_to_seconds]
@property @property

7
pygal/graph/treemap.py

@ -16,10 +16,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Treemap chart
""" """Treemap chart: Visualize data using nested recangles"""
from __future__ import division from __future__ import division
from pygal.util import decorate, cut, alter from pygal.util import decorate, cut, alter
@ -28,7 +26,8 @@ from pygal.adapters import positive, none_to_zero
class Treemap(Graph): class Treemap(Graph):
"""Treemap graph"""
"""Treemap graph class"""
_adapters = [positive, none_to_zero] _adapters = [positive, none_to_zero]

63
pygal/graph/verticalpyramid.py

@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2015 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Pyramid chart
"""
from __future__ import division
from pygal.adapters import positive
from pygal.graph.stackedbar import StackedBar
class VerticalPyramid(StackedBar):
"""Pyramid graph"""
_adapters = [positive]
def _format(self, value):
value = value and abs(value)
return super(VerticalPyramid, self)._format(value)
def _get_separated_values(self, secondary=False):
series = self.secondary_series if secondary else self.series
positive_vals = map(sum, zip(
*[serie.safe_values
for index, serie in enumerate(series)
if index % 2]))
negative_vals = map(sum, zip(
*[serie.safe_values
for index, serie in enumerate(series)
if not index % 2]))
return list(positive_vals), list(negative_vals)
def _compute_box(self, positive_vals, negative_vals):
self._box.ymax = max(max(positive_vals or [self.zero]),
max(negative_vals or [self.zero]))
self._box.ymin = - self._box.ymax
def _pre_compute_secondary(self, positive_vals, negative_vals):
self._secondary_max = max(max(positive_vals), max(negative_vals))
self._secondary_min = - self._secondary_max
def _bar(self, serie, parent, x, y, i, zero, secondary=False):
if serie.index % 2:
y = -y
return super(VerticalPyramid, self)._bar(
serie, parent, x, y, i, zero, secondary)

22
pygal/graph/xy.py

@ -16,9 +16,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
XY Line graph
"""
XY Line graph: Plot a set of couple data points (x, y) connected by
straight segments.
""" """
from __future__ import division from __future__ import division
@ -28,16 +29,20 @@ from pygal.graph.line import Line
class XY(Line): class XY(Line):
"""XY Line graph"""
"""XY Line graph class"""
_dual = True _dual = True
_x_adapters = [] _x_adapters = []
def _get_value(self, values, i): def _get_value(self, values, i):
"""Get the value formatted for tooltip"""
vals = values[i] vals = values[i]
return '%s: %s' % (self._x_format(vals[0]), self._format(vals[1])) return '%s: %s' % (self._x_format(vals[0]), self._format(vals[1]))
@cached_property @cached_property
def xvals(self): def xvals(self):
"""All x values"""
return [val[0] return [val[0]
for serie in self.all_series for serie in self.all_series
for val in serie.values for val in serie.values
@ -45,6 +50,7 @@ class XY(Line):
@cached_property @cached_property
def yvals(self): def yvals(self):
"""All y values"""
return [val[1] return [val[1]
for serie in self.series for serie in self.series
for val in serie.values for val in serie.values
@ -52,22 +58,18 @@ class XY(Line):
@cached_property @cached_property
def _min(self): def _min(self):
"""Getter for the minimum series value"""
return (self.range[0] if (self.range and self.range[0] is not None) return (self.range[0] if (self.range and self.range[0] is not None)
else (min(self.yvals) if self.yvals else None)) else (min(self.yvals) if self.yvals else None))
@cached_property @cached_property
def _max(self): def _max(self):
"""Getter for the maximum series value"""
return (self.range[1] if (self.range and self.range[1] is not None) return (self.range[1] if (self.range and self.range[1] is not None)
else (max(self.yvals) if self.yvals else None)) else (max(self.yvals) if self.yvals else None))
def _has_data(self):
"""Check if there is any data"""
return sum(
map(len, map(lambda s: s.safe_values, self.series))) != 0 and any((
sum(map(abs, self.xvals)) != 0,
sum(map(abs, self.yvals)) != 0))
def _compute(self): def _compute(self):
"""Compute x/y min and max and x/y scale and set labels"""
if self.xvals: if self.xvals:
if self.xrange: if self.xrange:
x_adapter = reduce( x_adapter = reduce(

4
pygal/maps/__init__.py

@ -16,7 +16,5 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>. # along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Maps extensions namespace module
""" """Maps extensions namespace module"""

Loading…
Cancel
Save