|
|
@ -24,23 +24,29 @@ Stacked Bar chart |
|
|
|
from __future__ import division |
|
|
|
from __future__ import division |
|
|
|
from pygal.graph.bar import Bar |
|
|
|
from pygal.graph.bar import Bar |
|
|
|
from pygal.util import compute_scale |
|
|
|
from pygal.util import compute_scale |
|
|
|
|
|
|
|
from pygal.adapters import none_to_zero |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StackedBar(Bar): |
|
|
|
class StackedBar(Bar): |
|
|
|
"""Stacked Bar graph""" |
|
|
|
"""Stacked Bar graph""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_adapters = [none_to_zero] |
|
|
|
|
|
|
|
|
|
|
|
def _compute(self): |
|
|
|
def _compute(self): |
|
|
|
transposed = zip(*[serie.values for serie in self.series]) |
|
|
|
transposed = zip(*[serie.values for serie in self.series]) |
|
|
|
positive_vals = [sum([ |
|
|
|
positive_vals = [sum([ |
|
|
|
val if val is not None and val > self.zero else self.zero |
|
|
|
val for val in vals |
|
|
|
for val in vals]) - self.zero |
|
|
|
if val is not None and val >= self.zero]) |
|
|
|
for vals in transposed] |
|
|
|
for vals in transposed] |
|
|
|
negative_vals = [sum([ |
|
|
|
negative_vals = [sum([ |
|
|
|
val - self.zero |
|
|
|
val |
|
|
|
if val is not None and val < self.zero else self.zero |
|
|
|
for val in vals |
|
|
|
for val in vals]) + self.zero |
|
|
|
if val is not None and val < self.zero]) |
|
|
|
for vals in transposed] |
|
|
|
for vals in transposed] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
positive_vals = positive_vals or [self.zero] |
|
|
|
|
|
|
|
negative_vals = negative_vals or [self.zero] |
|
|
|
|
|
|
|
|
|
|
|
self._box.ymin, self._box.ymax = ( |
|
|
|
self._box.ymin, self._box.ymax = ( |
|
|
|
min(min(negative_vals), self.zero), |
|
|
|
min(min(negative_vals), self.zero), |
|
|
|
max(max(positive_vals), self.zero)) |
|
|
|
max(max(positive_vals), self.zero)) |
|
|
@ -48,6 +54,8 @@ class StackedBar(Bar): |
|
|
|
x_pos = [ |
|
|
|
x_pos = [ |
|
|
|
x / self._len for x in range(self._len + 1) |
|
|
|
x / self._len for x in range(self._len + 1) |
|
|
|
] if self._len > 1 else [0, 1] # Center if only one value |
|
|
|
] if self._len > 1 else [0, 1] # Center if only one value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._points(x_pos) |
|
|
|
y_pos = compute_scale( |
|
|
|
y_pos = compute_scale( |
|
|
|
self._box.ymin, self._box.ymax, self.logarithmic, self.order_min |
|
|
|
self._box.ymin, self._box.ymax, self.logarithmic, self.order_min |
|
|
|
) if not self.y_labels else map(float, self.y_labels) |
|
|
|
) if not self.y_labels else map(float, self.y_labels) |
|
|
@ -57,12 +65,16 @@ class StackedBar(Bar): |
|
|
|
sum(x_range) / 2 for x_range in self._x_ranges]) |
|
|
|
sum(x_range) / 2 for x_range in self._x_ranges]) |
|
|
|
self._y_labels = zip(map(self._format, y_pos), y_pos) |
|
|
|
self._y_labels = zip(map(self._format, y_pos), y_pos) |
|
|
|
|
|
|
|
|
|
|
|
def _points(self, x_pos): |
|
|
|
self.negative_cumulation = [0] * self._len |
|
|
|
accumulation = [0] * self._len |
|
|
|
self.positive_cumulation = [0] * self._len |
|
|
|
for serie in self.series: |
|
|
|
|
|
|
|
accumulation = map(sum, zip(accumulation, serie.values)) |
|
|
|
def _bar(self, parent, x, val, index, i, zero): |
|
|
|
serie.points = [ |
|
|
|
cumulation = (self.negative_cumulation if val < self.zero else |
|
|
|
(x_pos[i], v) |
|
|
|
self.positive_cumulation) |
|
|
|
for i, v in enumerate(accumulation)] |
|
|
|
zero = cumulation[i] |
|
|
|
if self.interpolate: |
|
|
|
cumulation[i] = zero + val |
|
|
|
serie.interpolated = self._interpolate(accumulation, x_pos) |
|
|
|
if zero == 0: |
|
|
|
|
|
|
|
zero = self.zero |
|
|
|
|
|
|
|
val -= self.zero |
|
|
|
|
|
|
|
return super(StackedBar, self)._bar( |
|
|
|
|
|
|
|
parent, x, zero + val, index, i, zero, False) |
|
|
|