public
Last active — forked from mnot/sparkogram.py

sparkogram.py: Sparkline histogram generator

  • Download Gist
sparkogram.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
#!/usr/bin/env python
 
"""
sparkogram.py - Sparkline histogram generator
 
This is quick and dirty. Based on Joe Gregorio's sparkline thoughts;
<http://bitworking.org/news/Sparklines_in_data_URIs_in_Python>.
"""
 
__license__ = """
Copyright (c) 2006 Mark Nottingham <mnot@pobox.com>
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
 
__version__ = '0.23'
 
import base64
import StringIO
from PIL import Image, ImageDraw # <http://www.pythonware.com/products/pil/>
 
def sparkogram(data, width=80, height=20, start=None, finish=None,
color=(32,32,32,255), bg_color=(255,255,255,0), median_color=(0,0,255,255)):
"""
Given a data set, return a IMG tag suitable for insertion into HTML.
The image will be a histogram of the data, with the median value
highlighted.
The ends of the histogram can be overridden with "start" and "finish" (which
default to the min and the max of the data); if any values fall outside of
these, the ends will represent these values, and be highlighted in red.
data: list of numbers (int or float)
width: width of sparkline, in pixels
height: height of sparkline, in pixels
start: left-most value
finish: right-most value
color: color of the graph
bg_color: background color
median_color: color that median value will be highlighted with
Note that colors can be specified in a variety of ways, depending on
the version of PIL that you have installed. See:
<http://www.pythonware.com/library/pil/handbook/imagedraw.htm>
"""
if data == []:
return ""
data.sort()
if start is None:
start = min(data)
if finish is None:
finish = max(data)
median = data[int(len(data)/2)]
num_buckets = width - 2
bucket_width = (finish - start) / float(num_buckets - 1)
if bucket_width == 0: # hack for single-value datasets
bucket_width = 1
bd = dict([(n * bucket_width, 0) for n in xrange(num_buckets)])
over_items = under_items = 0
median_bucket = median_x = None
for item in data:
if item < start:
under_items -= 1
continue
if item > finish:
over_items += 1
continue
m_item = item - start
bn = m_item - (m_item % bucket_width)
if item == median:
median_bucket = bn
bd[bn] += 1
bl = bd.keys()
bl.sort()
if median_bucket is not None:
median_x = bl.index(median_bucket)
max_value = float(max(bd.values() + [over_items, under_items]))
height -= 1
coords = [(i, height - (height * (bd[bl[i]] / max_value)))
for i in xrange(num_buckets)]
 
im = Image.new("RGBA", (width, height + 1), bg_color)
draw = ImageDraw.Draw(im)
if under_items > 0:
draw.line([(0, height), (0, height - (height * (under_items / max_value)))], fill=(255,0,0,255))
for x, y in coords:
this_color = (x == median_x) and median_color or color
draw.line([(x, height), (x, y)], fill=this_color)
if over_items > 0:
draw.line([(width - 1, height), (width -1, height - (height * (over_items / max_value)))], fill=(255,0,0,255))
del draw
 
f = StringIO.StringIO()
im.save(f, "PNG")
return """\
<img src="data:image/png;base64,%s" title="min: %s median: %s max: %s"/>""" % (
base64.encodestring(f.getvalue()).replace('\n', ''),
min(data),
median,
max(data))

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.