Browse Source

Add lagrange interpolation and hermite variants

pull/35/merge
Florian Mounier 12 years ago
parent
commit
fc7daba3f1
  1. 28
      demo/moulinrouge/__init__.py
  2. 7
      demo/moulinrouge/tests.py
  3. 5
      pygal/config.py
  4. 4
      pygal/graph/graph.py
  5. 73
      pygal/interpolate.py

28
demo/moulinrouge/__init__.py

@ -167,15 +167,37 @@ def create_app():
order = random.randrange(1, 10)
series = b64encode(pickle.dumps(_random_series(type, data, order)))
svgs = []
for interpolation in (
'linear', 'slinear', 'nearest', 'zero', 'quadratic', 'cubic',
'krogh', 'barycentric', 'univariate', 4, 5, 6, 7, 8):
for interpolation in 'quadratic', 'cubic', 'lagrange', 'trigonometric':
config.title = "%s interpolation" % interpolation
config.interpolate = interpolation
svgs.append({'type': 'StackedLine',
'series': series,
'config': b64encode(pickle.dumps(config))})
for params in [
{'type': 'catmull_rom'},
{'type': 'finite_difference'},
{'type': 'cardinal', 'c': .25},
{'type': 'cardinal', 'c': .5},
{'type': 'cardinal', 'c': .75},
{'type': 'cardinal', 'c': 1.5},
{'type': 'cardinal', 'c': 2},
{'type': 'cardinal', 'c': 5},
{'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': 1},
{'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1},
{'type': 'kochanek_bartels', 'b': 1, 'c': -1, 't': 1},
{'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': -1},
{'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': -1},
{'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': 1},
{'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': -1}
]:
config.title = "Hermite interpolation with params %r" % params
config.interpolate = 'hermite'
config.interpolation_parameters = params
svgs.append({'type': 'StackedLine',
'series': series,
'config': b64encode(pickle.dumps(config))})
return render_template('svgs.jinja2',
svgs=svgs,
width=width,

7
demo/moulinrouge/tests.py

@ -174,7 +174,12 @@ def get_test_routes(app):
@app.route('/test/interpolate/<chart>')
def test_interpolate_for(chart):
graph = CHARTS_BY_NAME[chart](interpolate='trigonometric')
graph = CHARTS_BY_NAME[chart](interpolate='lagrange',
interpolation_parameters={
'type': 'kochanek_bartels',
'c': 1,
'b': -1,
't': -1})
graph.add('1', [1, 3, 12, 3, 4])
graph.add('2', [7, -4, 10, None, 8, 3, 1])
return graph.render_response()

5
pygal/config.py

@ -194,6 +194,11 @@ class Config(object):
interpolation_precision = Key(
250, int, "Value", "Number of interpolated points between two values")
interpolation_parameters = Key(
{}, dict, "Value", "Various parameters for parametric interpolations",
"ie: For hermite interpolation, you can set the cardinal tension with"
"{'type': 'cardinal', 'c': .5}")
order_min = Key(
None, int, "Value", "Minimum order of scale, defaults to None")

4
pygal/graph/graph.py

@ -416,7 +416,9 @@ class Graph(BaseGraph):
interpolate = INTERPOLATIONS[self.interpolate]
return list(interpolate(x, y, self.interpolation_precision))
return list(interpolate(
x, y, self.interpolation_precision,
**self.interpolation_parameters))
def _tooltip_data(self, node, value, x, y, classes=None):
self.svg.node(node, 'desc', class_="value").text = value

73
pygal/interpolate.py

@ -19,14 +19,12 @@
"""
Interpolation
TODO: Implement more interpolations (cosine, lagrange...)
"""
from __future__ import division
from math import sin
def quadratic_interpolate(x, y, precision=250):
def quadratic_interpolate(x, y, precision=250, **kwargs):
n = len(x) - 1
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
delta_y = [y2 - y1 for y1, y2 in zip(y, y[1:])]
@ -52,9 +50,7 @@ def quadratic_interpolate(x, y, precision=250):
yield x[i] + X, a[i] + b[i] * X + c[i] * X2
def cubic_interpolate(x, y, precision=250):
"""Takes a list of (x, y) and returns an iterator over
the natural cubic spline of points with `precision` points between them"""
def cubic_interpolate(x, y, precision=250, **kwargs):
n = len(x) - 1
# Spline equation is a + bx + cx² + dx³
# ie: Spline part i equation is a[i] + b[i]x + c[i]x² + d[i]x³
@ -93,14 +89,36 @@ def cubic_interpolate(x, y, precision=250):
yield x[i] + X, a[i] + b[i] * X + c[i] * X2 + d[i] * X3
def hermite_interpolate(x, y, precision=250):
def hermite_interpolate(x, y, precision=250,
type='cardinal', c=None, b=None, t=None):
n = len(x) - 1
m = [1] * (n + 1)
c = 0
w = [1] * (n + 1)
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
for i in range(1, n):
m[i] = (1 - c) * (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1])
if type == 'catmull_rom':
type = 'cardinal'
c = 0
if type == 'finite_difference':
for i in range(1, n):
m[i] = w[i] = .5 * (
(y[i + 1] - y[i]) / (x[i + 1] - x[i]) +
(y[i] - y[i - 1]) / (x[i] - x[i - 1]))
elif type == 'kochanek_bartels':
c = c or 0
b = b or 0
t = t or 0
for i in range(1, n):
m[i] = .5 * ((1 - t) * (1 + b) * (1 + c) * (y[i] - y[i - 1]) +
(1 - t) * (1 - b) * (1 - c) * (y[i + 1] - y[i]))
w[i] = .5 * ((1 - t) * (1 + b) * (1 - c) * (y[i] - y[i - 1]) +
(1 - t) * (1 - b) * (1 + c) * (y[i + 1] - y[i]))
if type == 'cardinal':
c = c or 0
for i in range(1, n):
m[i] = w[i] = (1 - c) * (
y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1])
def p(i, x_):
t = (x_ - x[i]) / delta_x[i]
@ -115,7 +133,7 @@ def hermite_interpolate(x, y, precision=250):
return (h00 * y[i] +
h10 * m[i] * delta_x[i] +
h01 * y[i + 1] +
h11 * m[i + 1] * delta_x[i])
h11 * w[i + 1] * delta_x[i])
for i in range(n + 1):
yield x[i], y[i]
@ -126,8 +144,29 @@ def hermite_interpolate(x, y, precision=250):
yield X, p(i, X)
def trigonometric_interpolate(x, y, precision=250):
""" As per http://en.wikipedia.org/wiki/Trigonometric_interpolation"""
def lagrange_interpolate(x, y, precision=250, **kwargs):
n = len(x) - 1
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
for i in range(n + 1):
yield x[i], y[i]
if i == n or delta_x[i] == 0:
continue
for s in range(1, precision):
X = x[i] + s * delta_x[i] / precision
s = 0
for k in range(n + 1):
p = 1
for m in range(n + 1):
if m == k:
continue
p *= (X - x[m]) / (x[k] - x[m])
s += y[k] * p
yield X, s
def trigonometric_interpolate(x, y, precision=250, **kwargs):
"""As per http://en.wikipedia.org/wiki/Trigonometric_interpolation"""
n = len(x) - 1
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
for i in range(n + 1):
@ -147,11 +186,17 @@ def trigonometric_interpolate(x, y, precision=250):
s += y[k] * p
yield X, s
"""
These functions takes two lists of points x and y and
returns an iterator over the interpolation between all these points
with `precision` interpolated points between each of them
"""
INTERPOLATIONS = {
'quadratic': quadratic_interpolate,
'cubic': cubic_interpolate,
'hermite': hermite_interpolate,
'lagrange': lagrange_interpolate,
'trigonometric': trigonometric_interpolate
}

Loading…
Cancel
Save