Browse Source

Adds errors in charts

113_errors
Jean-Marc Martins 11 years ago
parent
commit
3b42619c05
  1. 12
      pygal/graph/bar.py
  2. 10
      pygal/graph/line.py
  3. 32
      pygal/svg.py
  4. 33
      pygal/util.py

12
pygal/graph/bar.py

@ -82,6 +82,18 @@ class Bar(Graph):
x_center, y_center = self._bar( x_center, y_center = self._bar(
bar, x, y, index, i, self.zero, secondary=rescale, bar, x, y, index, i, self.zero, secondary=rescale,
rounded=serie.rounded_bars) rounded=serie.rounded_bars)
errors = metadata.get('errors') if metadata else None
if errors:
error_node = self.svg.node(bar, class_='errors')
base_x = x_center
transpose = ident
if self.horizontal:
transpose = swap
base_x = y_center
self.svg.draw_errors(error_node, errors, base_x,
transpose=transpose)
self._tooltip_data( self._tooltip_data(
bar, val, x_center, y_center, classes="centered") bar, val, x_center, y_center, classes="centered")
self._static_value(serie_node, val, x_center, y_center) self._static_value(serie_node, val, x_center, y_center)

10
pygal/graph/line.py

@ -103,8 +103,9 @@ class Line(Graph):
self.svg.node(serie_node['overlay'], class_="dots"), self.svg.node(serie_node['overlay'], class_="dots"),
metadata) metadata)
val = self._get_value(serie.points, i) val = self._get_value(serie.points, i)
self.svg.node(dots, 'circle', cx=x, cy=y, r=serie.dots_size, self.svg.node(dots, 'circle', cx=x, cy=y,
class_='dot reactive tooltip-trigger') r=serie.dots_size,
class_='dot reactive tooltip-trigger')
self._tooltip_data( self._tooltip_data(
dots, val, x, y) dots, val, x, y)
self._static_value( self._static_value(
@ -112,6 +113,11 @@ class Line(Graph):
x + self.value_font_size, x + self.value_font_size,
y + self.value_font_size) y + self.value_font_size)
errors = metadata.get('errors') if metadata else None
if errors:
error_node = self.svg.node(dots, class_='errors')
self.svg.draw_errors(error_node, errors, x)
if serie.stroke: if serie.stroke:
if self.interpolate: if self.interpolate:
view_values = list(map(self.view, serie.interpolated)) view_values = list(map(self.view, serie.interpolated))

32
pygal/svg.py

@ -30,7 +30,7 @@ from datetime import date, datetime
from numbers import Number from numbers import Number
from lxml import etree from lxml import etree
from math import cos, sin, pi from math import cos, sin, pi
from pygal.util import template, coord_format, minify_css from pygal.util import template, coord_format, minify_css, ident
from pygal import __version__ from pygal import __version__
@ -240,3 +240,33 @@ class Svg(object):
if self.graph.disable_xml_declaration or is_unicode: if self.graph.disable_xml_declaration or is_unicode:
svg = svg.decode('utf-8') svg = svg.decode('utf-8')
return svg return svg
def draw_errors(self, node, errors, base_x, transpose=None):
"""Draws the confidence level for a given point."""
if not transpose:
transpose = ident
width = (self.graph.view.x(1) - self.graph.view.x(0)) / self.graph._len
series_margin = width * getattr(self.graph, '_series_margin', 0)
width -= 2 * series_margin
offset = (width - width / 2) / 2
lower_tip = transpose((base_x, self.graph.view.y(errors.get('min'))))
upper_tip = transpose((base_x, self.graph.view.y(errors.get('max'))))
lower_left_tip = transpose((base_x - offset,
self.graph.view.y(errors.get('min'))))
lower_right_tip = transpose((base_x + offset,
self.graph.view.y(errors.get('min'))))
upper_left_tip = transpose((base_x - offset,
self.graph.view.y(errors.get('max'))))
upper_right_tip = transpose((base_x + offset,
self.graph.view.y(errors.get('max'))))
# base line
self.line(node, coords=[lower_tip, upper_tip], class_='error')
# hat/foot
self.line(node, coords=[lower_left_tip, lower_right_tip],
class_='error')
self.line(node, coords=[upper_left_tip, upper_right_tip],
class_='error')

33
pygal/util.py

@ -24,7 +24,7 @@ from __future__ import division
from pygal._compat import to_str, u, is_list_like from pygal._compat import to_str, u, is_list_like
import re import re
from decimal import Decimal from decimal import Decimal
from math import floor, pi, log, log10, ceil from math import floor, pi, log, log10, ceil, fsum, sqrt
from itertools import cycle from itertools import cycle
from functools import reduce from functools import reduce
from pygal.adapters import not_zero, positive from pygal.adapters import not_zero, positive
@ -228,7 +228,7 @@ def get_texts_box(texts, fs):
def decorate(svg, node, metadata): def decorate(svg, node, metadata):
"""Add metedata next to a node""" """Add metadata next to a node"""
if not metadata: if not metadata:
return node return node
xlink = metadata.get('xlink') xlink = metadata.get('xlink')
@ -376,6 +376,13 @@ def prepare_values(raw, config, cls):
if isinstance(raw_value, dict): if isinstance(raw_value, dict):
raw_value = dict(raw_value) raw_value = dict(raw_value)
value = raw_value.pop('value', None) value = raw_value.pop('value', None)
if not value:
stdd = raw_value.pop('stdd', None)
errors = raw_value.pop('errors', None)
if stdd and is_list_like(stdd):
value = mean(stdd)
if not errors:
raw_value.update(dict(errors=compute_errors(stdd)))
metadata[index] = raw_value metadata[index] = raw_value
else: else:
value = raw_value value = raw_value
@ -423,3 +430,25 @@ def split_title(title, width, title_fs):
title_line = title_line[i:].strip() title_line = title_line[i:].strip()
titles.append(title_line) titles.append(title_line)
return titles return titles
def mean(values):
"""Return the mean of a serie of values."""
return fsum(values) / len(values)
def variance(values):
"""Return the variance of a serie of values."""
values_mean = mean(values)
return fsum((v-values_mean) ** 2 for v in values) / len(values)
def std_deviation(values):
"""Returns the standard deviation of values."""
return sqrt(variance(values))
def compute_errors(values):
m = mean(values)
stdd = std_deviation(values)
return dict(mean=m, min=m-stdd, max=m+stdd)

Loading…
Cancel
Save