mirror of https://github.com/Kozea/pygal.git
Florian Mounier
13 years ago
9 changed files with 233 additions and 26 deletions
@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*- |
||||
# This file is part of pygal |
||||
# |
||||
# A python svg graph plotting library |
||||
# Copyright © 2012 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/>. |
||||
""" |
||||
Funnel chart |
||||
|
||||
""" |
||||
|
||||
from __future__ import division |
||||
from pygal.util import decorate, cut, compute_scale |
||||
from pygal.serie import PositiveValue |
||||
from pygal.graph.graph import Graph |
||||
|
||||
|
||||
class Funnel(Graph): |
||||
"""Funnel graph""" |
||||
|
||||
__value__ = PositiveValue |
||||
|
||||
def _format(self, value): |
||||
return super(Funnel, self)._format(abs(value)) |
||||
|
||||
def funnel(self, serie_node, serie): |
||||
"""Draw a dot line""" |
||||
|
||||
fmt = lambda x: '%f %f' % x |
||||
for i, poly in enumerate(serie.points): |
||||
metadata = serie.metadata[i] |
||||
value = self._format(serie.values[i]) |
||||
|
||||
funnels = decorate( |
||||
self.svg, |
||||
self.svg.node(serie_node['plot'], class_="funnels"), |
||||
metadata) |
||||
|
||||
self.svg.node( |
||||
funnels, 'polygon', |
||||
points=' '.join(map(fmt, map(self.view, poly))), |
||||
class_='funnel reactive tooltip-trigger') |
||||
|
||||
x, y = self.view(( |
||||
self._x_labels[serie.index][1], # Poly center from label |
||||
sum([point[1] for point in poly]) / len(poly))) |
||||
self._tooltip_data(funnels, value, x, y, classes='centered') |
||||
self._static_value(serie_node, value, x, y) |
||||
|
||||
def _compute(self): |
||||
xlen = len(self.series) |
||||
x_pos = [(x + 1) / xlen for x in range(xlen) |
||||
] if xlen != 1 else [.5] # Center if only one value |
||||
|
||||
previous = [[0, 0] for i in range(self._len)] |
||||
for i, serie in enumerate(self.series): |
||||
y_height = - sum(serie.values) / 2 |
||||
all_x_pos = [0] + x_pos |
||||
serie.points = [] |
||||
for j, value in enumerate(serie.values): |
||||
poly = [] |
||||
poly.append((all_x_pos[i], previous[j][0])) |
||||
poly.append((all_x_pos[i], previous[j][1])) |
||||
previous[j][0] = y_height |
||||
y_height = previous[j][1] = y_height + value |
||||
poly.append((all_x_pos[i + 1], previous[j][1])) |
||||
poly.append((all_x_pos[i + 1], previous[j][0])) |
||||
serie.points.append(poly) |
||||
|
||||
val_max = max(map(sum, cut(self.series, 'values'))) |
||||
self._box.ymin = -val_max |
||||
self._box.ymax = val_max |
||||
|
||||
y_pos = compute_scale( |
||||
self._box.ymin, self._box.ymax, self.logarithmic |
||||
) if not self.y_labels else map(float, self.y_labels) |
||||
|
||||
self._x_labels = zip(cut(self.series, 'title'), |
||||
map(lambda x: x - 1 / (2 * xlen), x_pos)) |
||||
self._y_labels = zip(map(self._format, y_pos), y_pos) |
||||
|
||||
def _plot(self): |
||||
for serie in self.series: |
||||
self.funnel( |
||||
self._serie(serie.index), serie) |
@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*- |
||||
# This file is part of pygal |
||||
# |
||||
# A python svg graph plotting library |
||||
# Copyright © 2012 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.util import decorate, cut, compute_scale |
||||
from pygal.serie import PositiveValue |
||||
from pygal.graph.bar import Bar |
||||
from pygal.graph.horizontal import HorizontalGraph |
||||
|
||||
|
||||
class VerticalPyramid(Bar): |
||||
"""Pyramid graph""" |
||||
|
||||
__value__ = PositiveValue |
||||
|
||||
def _format(self, value): |
||||
return super(VerticalPyramid, self)._format(abs(value)) |
||||
|
||||
def _compute(self): |
||||
positive_vals = zip(*[serie.values for serie in self.series |
||||
if serie.index % 2]) |
||||
negative_vals = zip(*[serie.values for serie in self.series |
||||
if not serie.index % 2]) |
||||
positive_sum = map(sum, positive_vals) or [0] |
||||
negative_sum = map(sum, negative_vals) |
||||
|
||||
self._box.ymax = max(max(positive_sum), max(negative_sum)) |
||||
self._box.ymin = - self._box.ymax |
||||
|
||||
x_pos = [x / self._len |
||||
for x in range(self._len + 1) |
||||
] if self._len > 1 else [0, 1] # Center if only one value |
||||
y_pos = compute_scale( |
||||
self._box.ymin, self._box.ymax, self.logarithmic |
||||
) if not self.y_labels else map(float, self.y_labels) |
||||
|
||||
self._x_ranges = zip(x_pos, x_pos[1:]) |
||||
|
||||
self._x_labels = self.x_labels and zip(self.x_labels, [ |
||||
sum(x_range) / 2 for x_range in self._x_ranges]) |
||||
self._y_labels = zip(map(self._format, y_pos), y_pos) |
||||
|
||||
def _plot(self): |
||||
stack_vals = [[0, 0] for i in range(self._len)] |
||||
for serie in self.series: |
||||
serie_node = self._serie(serie.index) |
||||
stack_vals = self.bar( |
||||
serie_node, serie, [ |
||||
tuple( |
||||
(self._x_ranges[i][j], |
||||
v * (-1 if serie.index % 2 else 1)) for j in range(2)) |
||||
for i, v in enumerate(serie.values)], |
||||
stack_vals) |
||||
|
||||
|
||||
class Pyramid(HorizontalGraph, VerticalPyramid): |
||||
"""Horizontal Pyramid graph""" |
Loading…
Reference in new issue