|
UserGuide
Explains how to use Graphy
GraphyGraphy is a simple Python library for generating charts. It tries to get out of the way and let you just work with your data. At the moment, it produces charts using the Google Chart API. Contents
BasicsQuick StartHere's a quick example, plotting average monthly rainfall for Sunnyvale, CA: from graphy.backends import google_chart_api monthly_rainfall = [3.2, 3.2, 2.7, 0.9, 0.4, 0.1, 0.0, 0.0, 0.2, 0.9, 1.8, 2.3] months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split() chart = google_chart_api.LineChart(monthly_rainfall) chart.bottom.labels = months print chart.display.Img(400, 100) That will print out an HTML img tag for this chart:
Creating ChartsThe basic approach to creating a chart is the same reglardless of what type of chart you are making:
Data SeriesNote: The Google Chart API assumes data is equally-spaced Y-values. That is, there is no concept of X/Y pairs. When you specify data, you are only specifying the Y-values, and they will be shown equally spaced along the X-axis. When you construct a new chart, you can provide a single series of data in the constructor. Charts also have methods to add more data series. For example, LineChart.AddLine or BarChart.AddBar. Generally, these methods take the same standard arguments, plus a few arguments specific to the type of chart:
If you have gaps in your data, they are represented with None. For example, google_chart_api.LineChart([10, 12, 13, 11, None, 8, 9, 7]).display.Url(100, 50)
(Behind the scenes, charts store their data in DataSeries objects, which hold the points in the series, the label for the legend, and a style object for the color & appearance of the series. Generally, you won't need to deal with DataSeries objects.) AxesAxis objects are used to specify label and scale information. Most charts support axes on all four sides of the chart (left, bottom, right, and top). In addition, multiple axes can be added at each position. For convenience, the chart exposes the first axis in each position though left, bottom, right, and top. Additional axes can be added and accessed using AddAxis, SetAxis, and GetAxis. You can use this to have 2 different scales presented, for example. Note that for the most part, chart.left and chart.bottom are the main axes used for many calculations (like data scaling) and the additional axes are used for attaching labels to the chart. There's an example of doing this in the Labels section. LabelsTo add labels to an axis, specify axis.labels. By default, the labels will be spread out evenly along the axis; specify axis.label_positions to place labels where you want them to go. label_positions are interpreted according to axis.min and axis.max, so you may want to explicitly set min & max on the axis instead of relying on the AutoScale formatter (see the section on scaling for more info). from graphy.backends import google_chart_api temperatures = [25, 31, 39, 50, 60, 70, 75, 74, 66, 55, 42, 30, 25] chart = google_chart_api.LineChart(temperatures) chart.bottom.min = 0 chart.bottom.max = 13 # 13, not 12, because we repeat Jan at end. chart.bottom.labels = ['Jan', 'Apr', 'Jul', 'Sep', 'Dec'] chart.bottom.label_positions = [0, 3, 6, 9, 12] chart.left.min = 0 chart.left.max = 80 chart.left.labels = [10, 32, 50, 70] chart.left.label_positions = [10, 32, 50, 70] print chart.display.Url(250, 100)
If you want to add a second set of labels along one of the edges, you'll want to add another axis: from graphy.backends import google_chart_api from graphy import common chart = google_chart_api.LineChart([9, 7, 5, 6, 3, 4, 5, 4, 2, 1]) chart.bottom.labels = ['Q1', 'Q2', 'Q3', 'Q4', 'Q1', 'Q2', 'Q3', 'Q4'] year_axis = chart.AddAxis(common.AxisPosition.BOTTOM, common.Axis()) year_axis.min = 1 # Line positions up with quarters year_axis.max = 8 year_axis.labels = [2007, 2008] year_axis.label_positions = [1, 5] print chart.display.Url(200, 100)
In situations where you are adding labels based on some data (like trying to label certain events in your graph) you can run into problems with labels overlapping each other. The LabelSeparator formatter attempts to separate labels which are too close together. See the section on Formatters for more details. ScalingData is scaled according to the values in chart.left.min and chart.left.max. So, for example, if min = 0 and max = 50, then a value of 0 will be at the bottom of the chart, 25 will be in the middle, and 50 will be at the top. Axis labels are also positioned according to min & max. If you don't specify min & max, then the AutoScale formatter will pick them such that your data will fill the entire chart vertically. This makes it easy to get a chart quickly, but it can be misleading. For example, this looks like a dramatic drop in traffic: traffic = [578, 579, 580, 550, 545, 552] chart = google_chart_api.LineChart(traffic) print chart.display.Img(100, 50)
If you specify a min/max on the dependent axis (the left axis for line charts), your data will be scaled to that range instead. By properly scaling the traffic data, you see that there is nothing to worry about: chart.left.min = 0 chart.left.max = 600 print chart.display.Img(100, 50)
Note: in version 0.9, you could add an Axis using AddAxis and leave min/max set to None on it. In this case, min/max used the Google Chart API's defaults of 0 and 100. Now, however, any additional axes with min/max unset will inherit the min/max from the other axis in their same position. LegendsIf you specify labels when you add data series to the chart, then the chart will have a legend. For example: from graphy.backends import google_chart_api temperature = [18, 24, 32, 42, 51, 61, 66, 65, 57, 46, 35, 24] heating_cost = [78, 75, 60, 30, 10, 7, 8, 6, 10, 25, 60, 75] chart = google_chart_api.LineChart() chart.AddLine(heating_cost, label="Heating Cost") chart.AddLine(temperature, label="Temperature") print chart.display.Img(250, 100)
Personally, I don't like legends. They disconnect the label from the data, making the chart harder to read. The InlineLegend formatter can help, by replacing the legend with labels on the right-hand axis: # Re-using chart from the last example... from graphy import formatters chart.AddFormatter(formatters.InlineLegend) print chart.display.Img(250, 100)
GridsThere are currently two approaches for making grids. The simplest is to set label_gridlines = True on either chart.left or chart.right. This draws a line across the chart at every label location. from graphy.backends import google_chart_api chart = google_chart_api.LineChart([12, 42, 57, 63, 55, 52, 31, 22]) chart.bottom.min = 0 chart.bottom.max = 30 chart.bottom.labels = [0, 5, 10, 15, 20, 25, 30] chart.bottom.label_positions = chart.bottom.labels chart.bottom.label_gridlines = True chart.left.min = 0 chart.left.max = 70 chart.left.labels = [0, 30, 60] chart.left.label_positions = chart.left.labels chart.left.label_gridlines = True print chart.display.Url(200, 100)
The other option is to set grid_spacing on an axis. This controls the amount of space between gridlines. Use grid_spacing instead of label_gridlines when you don't want the grid to match your labels. Note that min/max have to set to use this. # Reusing the same chart from the previous example chart.bottom.label_gridlines = False chart.left.label_gridlines = False chart.bottom.grid_spacing = 10 chart.left.grid_spacing = 15 print chart.display.Url(200, 100)
FormattersTODO: document formatters (default ones, how to remove them, other non-default formatters, how to write your own) Line Chartsfrom graphy.backends import google_chart_api chart = google_chart_api.LineChart() chart.AddLine([0, 4, 3, 10, 12, 29, 45, 51, 60, 71]) print chart.display.Url(200, 100)
Line patterns and widthLineChart.AddLine has 2 arguments for controling the appearance of the line:
from graphy.backends import google_chart_api
from graphy import line_chart
temperature = [18, 24, 32, 42, 51, 61, 66, 65, 57, 46, 35, 24]
heating_cost = [78, 75, 60, 30, 10, 7, 8, 6, 10, 25, 60, 75]
chart = google_chart_api.LineChart()
chart.AddLine(heating_cost, color='006F00',
pattern=line_chart.LineStyle.SOLID, width=line_chart.LineStyle.THICK)
chart.AddLine(temperature, color='0000FF',
pattern=line_chart.LineStyle.DASHED)
print chart.display.Img(250, 100)
Markers on LinesTODO: Document markers SparklinesIn addition to LineCharts, there's also a Sparkline class. This works like LineCharts, but doesn't have axes. from graphy.backends import google_chart_api prices = [78, 102, 175, 181, 160, 195, 138, 158, 179, 183, 222, 211, 215] chart= google_chart_api.Sparkline(prices) print '<img style="display:inline;" src="%s">' % chart.display.Url(40, 12) Which gives you a tiny graph Bar Chartsfrom graphy.backends import google_chart_api
cities = ('SJ', 'SF', 'Oak')
population = (900000, 780000, 400000)
chart = google_chart_api.BarChart(population)
chart.bottom.labels = cities
chart.left.min = 0
print chart.display.Img(400, 120)
You can add labels & grids the same as with line charts: # re-using the chart from the previous example chart.left.min = 0 chart.left.max = 1000000 chart.left.labels = ['0', '500K', '1000K'] chart.left.grid_spacing = 500000 print chart.display.Img(400, 120)
Vertical/Horizontal OrientationYou can change the horizontal/vertical orientation by setting chart.vertical to either True or False. This does not swap the axes around for you. The left axis labels will continue to be display on the left axis, for example. cities = ('San Jose', 'San Francisco', 'Oakland')
population = (900000, 780000, 400000)
chart = google_chart_api.BarChart(population)
chart.vertical = False
chart.left.labels = cities
chart.bottom.min = 0
print chart.display.Img(200, 120)
Multiple SeriesYou can add more series by calling chart.AddBars. cities = ('San Jose', 'San Francisco', 'Oakland')
pop_1960 = (200000, 740000, 370000)
pop_2000 = (900000, 780000, 400000)
chart = google_chart_api.BarChart()
chart.AddBars(pop_1960, label='1960', color='ccccff')
chart.AddBars(pop_2000, label='2000', color='0000aa')
chart.vertical = False
chart.left.labels = cities
chart.bottom.min = 0
print chart.display.Img(300, 180)
You can do stacked bar plots by setting chart.stacked = True. For example: # Reusing the same chart from previous example # Replace the 2000 data series with the incremental change from 1960 to 2000. chart.data[1].data = [pop_2000[i] - pop_1960[i] for i in range(len(pop_2000))] chart.stacked = True print chart.display.Img(300, 100)
Bar size and spacingYou can control the size & spacing of the individual bars by setting chart.display.style to a bar_chart.BarStyle object. BarStyle objects have 3 variables which control the style of the bars:
Here's an example showing how you could do a histogram plot by reducing bar_thickness and group_gap: from graphy import bar_chart from graphy.backends import google_chart_api data = [.3, 1.2, 2.3, 3.6, 4, 4.7, 4.5, 3.5 ,2.3, 1.4, 1] chart = google_chart_api.BarChart(data) chart.left.min = 0 chart.display.style = bar_chart.BarStyle() chart.display.style.bar_thickness = 10 chart.display.style.group_gap = 0 print chart.display.Img(400, 120)
Caveats/Bugs With Bar ChartsAlthough the length of your bars will be scaled to fit in your chart, the width of the bars is not scaled automatically at all. If you ask for a chart that's too narrow to fit all the bars, it will be cropped. This is a limitation of the Google Chart API which is difficult to work around in Graphy. I usually end up using trial and error to find the right size. For example, this crops the 4th bar: from graphy.backends import google_chart_api chart = google_chart_api.BarChart([1, 2, 3, 4]) print chart.display.Img(110, 110)
Google Chart API drops bars that are longer than your axis maximum. For example, the 4th bar doesn't show up here: from graphy.backends import google_chart_api chart = google_chart_api.BarChart([1, 2, 3, 4]) chart.left.max = 3 print chart.display.Img(130, 130)
Pie Chartsfrom graphy.backends import google_chart_api chart = google_chart_api.PieChart([200, 400, 50], ['Stock', 'Bonds', 'Cash']) print chart.display.Url(100, 200)
In addition to passing data to __init__ you can add segments using AddSegment. If chart.display.is3d is True, then the chart will be rendered in 3D: # reusing chart from last example chart.display.is3d = True print chart.display.Url(200, 100)
Bugs/Caveats for Pie ChartsCurrently you must provide labels for every segment. If you don't make the chart wide enough to display the segment labels, they will be cropped. Google Chart API specificsOptions which are specific to the Google Chart API live in the display object of a chart (i.e. chart.display) Data EncodingBy default, data is encoded using the simple encoding, which is terse but low-resolution. If you need more precision, you can enable enhanced encoding by setting chart.display.enhanced_encoding = True. URL EscapingIf you don't want the URL to be escaped, you can turn off escaping by setting chart.display.escape_url = False. Unescaped URLs might not be valid in HTML, but they are easier to read which can be useful when debugging. Extra ParamsYou can specify individual Google Chart API parameters. This is meant as an escape-hatch in case you either want to use a parameter which google_chart_api doesn't support, or in case you want to override one of the parameters google_chart_api is generating for you. To override parameters, add them to the chart.display.extra_params dict. Any parameters you add to this will show up in the final URL. There a list of non-abbreviated parameter names for your convenience (in google_chart_api.py). Keys in the extra_params dict can be either short Google Chart URL parameters (like chf), or the longer equivalent (like fill). For example, if you wanted to set a linear gradient background color on a chart, using 'chf', you could do it like this: prices = [78, 102, 175, 181, 160, 195, 138, 158, 179, 183, 222, 211, 215] chart = google_chart_api.LineChart(prices) chart.display.extra_params['chf'] = 'c,lg,90,ffffff,1,77aaff,0' print chart.display.Img(200, 100)
DeprecationsWe've been making a lot of changes to clean up the API before a 1.0 release. These APIs are deprecated in 1.0, and will be removed in version 2.0:
Developer's GuideIf you want to modify Graphy internally, instead of just using it, start with the DeveloperGuide. |
Sign in to add a comment
which you could inline in your webpage.