Package Bio :: Package Graphics :: Module Distribution
[hide private]
[frames] | no frames]

Source Code for Module Bio.Graphics.Distribution

  1  # This code is part of the Biopython distribution and governed by its 
  2  # license.  Please see the LICENSE file that should have been included 
  3  # as part of this package. 
  4  # 
  5   
  6  """Display information distributed across a Chromosome-like object. 
  7   
  8  These classes are meant to show the distribution of some kind of information 
  9  as it changes across any kind of segment. It was designed with chromosome 
 10  distributions in mind, but could also work for chromosome regions, BAC clones 
 11  or anything similar. 
 12   
 13  Reportlab is used for producing the graphical output. 
 14  """ 
 15  # standard library 
 16  import math 
 17   
 18  # reportlab 
 19  from reportlab.lib.pagesizes import letter 
 20  from reportlab.lib.units import inch 
 21  from reportlab.lib import colors 
 22   
 23  from reportlab.graphics.shapes import Drawing, String 
 24  from reportlab.graphics.charts.barcharts import VerticalBarChart 
 25  from reportlab.graphics.charts.barcharts import BarChartProperties 
 26  from reportlab.graphics.widgetbase import TypedPropertyCollection 
 27   
 28  from Bio.Graphics import _write 
 29   
 30   
31 -class DistributionPage(object):
32 """Display a grouping of distributions on a page. 33 34 This organizes Distributions, and will display them nicely 35 on a single page. 36 """
37 - def __init__(self, output_format = 'pdf'):
38 self.distributions = [] 39 40 # customizable attributes 41 self.number_of_columns = 1 42 self.page_size = letter 43 self.title_size = 20 44 45 self.output_format = output_format
46
47 - def draw(self, output_file, title):
48 """Draw out the distribution information. 49 50 Arguments: 51 52 o output_file - The name of the file to output the information to, 53 or a handle to write to. 54 55 o title - A title to display on the graphic. 56 """ 57 width, height = self.page_size 58 cur_drawing = Drawing(width, height) 59 60 self._draw_title(cur_drawing, title, width, height) 61 62 # calculate the x and y position changes for each distribution 63 cur_x_pos = inch * .5 64 end_x_pos = width - inch * .5 65 cur_y_pos = height - 1.5 * inch 66 end_y_pos = .5 * inch 67 x_pos_change = ((end_x_pos - cur_x_pos) / 68 float(self.number_of_columns)) 69 num_y_rows = math.ceil(float(len(self.distributions)) 70 / float(self.number_of_columns)) 71 y_pos_change = (cur_y_pos - end_y_pos) / num_y_rows 72 73 self._draw_distributions(cur_drawing, cur_x_pos, x_pos_change, 74 cur_y_pos, y_pos_change, num_y_rows) 75 self._draw_legend(cur_drawing, 2.5 * inch, width) 76 77 return _write(cur_drawing, output_file, self.output_format)
78
79 - def _draw_title(self, cur_drawing, title, width, height):
80 """Add the title of the figure to the drawing. 81 """ 82 title_string = String(width / 2, height - inch, title) 83 title_string.fontName = 'Helvetica-Bold' 84 title_string.fontSize = self.title_size 85 title_string.textAnchor = "middle" 86 87 cur_drawing.add(title_string)
88
89 - def _draw_distributions(self, cur_drawing, start_x_pos, x_pos_change, 90 start_y_pos, y_pos_change, num_y_drawings):
91 """Draw all of the distributions on the page. 92 93 Arguments: 94 95 o cur_drawing - The drawing we are working with. 96 97 o start_x_pos - The x position on the page to start drawing at. 98 99 o x_pos_change - The change in x position between each figure. 100 101 o start_y_pos - The y position on the page to start drawing at. 102 103 o y_pos_change - The change in y position between each figure. 104 105 o num_y_drawings - The number of drawings we'll have in the y 106 (up/down) direction. 107 """ 108 for y_drawing in range(int(num_y_drawings)): 109 # if we are on the last y position, we may not be able 110 # to fill all of the x columns 111 if (y_drawing + 1) * self.number_of_columns > \ 112 len(self.distributions): 113 num_x_drawings = len(self.distributions) - \ 114 y_drawing * self.number_of_columns 115 else: 116 num_x_drawings = self.number_of_columns 117 for x_drawing in range(num_x_drawings): 118 dist_num = y_drawing * self.number_of_columns + x_drawing 119 cur_distribution = self.distributions[dist_num] 120 121 # find the x and y boundaries of the distribution 122 x_pos = start_x_pos + x_drawing * x_pos_change 123 end_x_pos = x_pos + x_pos_change 124 end_y_pos = start_y_pos - y_drawing * y_pos_change 125 y_pos = end_y_pos - y_pos_change 126 127 # draw the distribution 128 cur_distribution.draw(cur_drawing, x_pos, y_pos, end_x_pos, 129 end_y_pos)
130
131 - def _draw_legend(self, cur_drawing, start_y, width):
132 """Add a legend to the figure. 133 134 Subclasses can implement to provide a specialized legend. 135 """ 136 pass
137 138
139 -class BarChartDistribution(object):
140 """Display the distribution of values as a bunch of bars. 141 """
142 - def __init__(self, display_info = []):
143 """Initialize a Bar Chart display of distribution info. 144 145 Class attributes: 146 147 o display_info - the information to be displayed in the distribution. 148 This should be ordered as a list of lists, where each internal list 149 is a data set to display in the bar chart. 150 """ 151 self.display_info = display_info 152 153 self.x_axis_title = "" 154 self.y_axis_title = "" 155 self.chart_title = "" 156 self.chart_title_size = 10 157 158 self.padding_percent = 0.15
159
160 - def draw(self, cur_drawing, start_x, start_y, end_x, end_y):
161 """Draw a bar chart with the info in the specified range. 162 """ 163 bar_chart = VerticalBarChart() 164 if self.chart_title: 165 self._draw_title(cur_drawing, self.chart_title, 166 start_x, start_y, end_x, end_y) 167 # set the position of the bar chart 168 x_start, x_end, y_start, y_end = \ 169 self._determine_position(start_x, start_y, end_x, end_y) 170 171 bar_chart.x = x_start 172 bar_chart.y = y_start 173 bar_chart.width = abs(x_start - x_end) 174 bar_chart.height = abs(y_start - y_end) 175 176 # set the information in the bar chart 177 bar_chart.data = self.display_info 178 bar_chart.valueAxis.valueMin = min(self.display_info[0]) 179 bar_chart.valueAxis.valueMax = max(self.display_info[0]) 180 for data_set in self.display_info[1:]: 181 if min(data_set) < bar_chart.valueAxis.valueMin: 182 bar_chart.valueAxis.valueMin = min(data_set) 183 if max(data_set) > bar_chart.valueAxis.valueMax: 184 bar_chart.valueAxis.valueMax = max(data_set) 185 186 # set other formatting options 187 if len(self.display_info) == 1: 188 bar_chart.groupSpacing = 0 189 style = TypedPropertyCollection(BarChartProperties) 190 style.strokeWidth = 0 191 style.strokeColor = colors.green 192 style[0].fillColor = colors.green 193 194 bar_chart.bars = style 195 196 # set the labels 197 # XXX labels don't work yet 198 # bar_chart.valueAxis.title = self.x_axis_title 199 # bar_chart.categoryAxis.title = self.y_axis_title 200 201 cur_drawing.add(bar_chart)
202
203 - def _draw_title(self, cur_drawing, title, start_x, start_y, end_x, end_y):
204 """Add the title of the figure to the drawing. 205 """ 206 x_center = start_x + (end_x - start_x) / 2 207 y_pos = end_y + (self.padding_percent * (start_y - end_y)) / 2 208 title_string = String(x_center, y_pos, title) 209 title_string.fontName = 'Helvetica-Bold' 210 title_string.fontSize = self.chart_title_size 211 title_string.textAnchor = "middle" 212 213 cur_drawing.add(title_string)
214
215 - def _determine_position(self, start_x, start_y, end_x, end_y):
216 """Calculate the position of the chart with blank space. 217 218 This uses some padding around the chart, and takes into account 219 whether the chart has a title. It returns 4 values, which are, 220 in order, the x_start, x_end, y_start and y_end of the chart 221 itself. 222 """ 223 x_padding = self.padding_percent * (end_x - start_x) 224 y_padding = self.padding_percent * (start_y - end_y) 225 226 new_x_start = start_x + x_padding 227 new_x_end = end_x - x_padding 228 229 if self.chart_title: 230 new_y_start = start_y - y_padding - self.chart_title_size 231 else: 232 new_y_start = start_y - y_padding 233 234 new_y_end = end_y + y_padding 235 236 return new_x_start, new_x_end, new_y_start, new_y_end
237 238
239 -class LineDistribution(object):
240 """Display the distribution of values as connected lines. 241 242 This distribution displays the change in values across the object as 243 lines. This also allows multiple distributions to be displayed on a 244 single graph. 245 """
246 - def __init__(self):
247 pass
248
249 - def draw(self, cur_drawing, start_x, start_y, end_x, end_y):
250 pass
251