forked from luci/luci-py
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbar_chart.py
More file actions
171 lines (142 loc) · 5.63 KB
/
bar_chart.py
File metadata and controls
171 lines (142 loc) · 5.63 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/python2.4
#
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Code related to bar charts."""
import copy
import warnings
from graphy import common
from graphy import util
class BarsStyle(object):
"""Style of a series of bars in a BarChart
Object Attributes:
color: Hex string, like '00ff00' for green
"""
def __init__(self, color):
self.color = color
class BarChartStyle(object):
"""Represents the style for bars on a BarChart.
Any of the object attributes may be set to None, in which case the
value will be auto-calculated.
Object Attributes:
bar_thickness: The thickness of a bar, in pixels.
bar_gap: The gap between bars, in pixels, or as a fraction of bar thickness
if use_fractional_gap_spacing is True.
group_gap: The gap between groups of bars, in pixels, or as a fraction of
bar thickness if use_fractional_gap_spacing is True.
use_fractional_gap_spacing: if True, bar_gap and group_gap specify gap
sizes as a fraction of bar width. Default is False.
"""
_DEFAULT_GROUP_GAP = 8
_DEFAULT_BAR_GAP = 4
def __init__(self, bar_thickness=None,
bar_gap=_DEFAULT_BAR_GAP, group_gap=_DEFAULT_GROUP_GAP,
use_fractional_gap_spacing=False):
"""Create a new BarChartStyle.
Args:
bar_thickness: The thickness of a bar, in pixels. Set this to None if
you want the bar thickness to be auto-calculated (this is the default
behaviour).
bar_gap: The gap between bars, in pixels. Default is 4.
group_gap: The gap between groups of bars, in pixels. Default is 8.
"""
self.bar_thickness = bar_thickness
self.bar_gap = bar_gap
self.group_gap = group_gap
self.use_fractional_gap_spacing = use_fractional_gap_spacing
class BarStyle(BarChartStyle):
def __init__(self, *args, **kwargs):
warnings.warn('BarStyle is deprecated. Use BarChartStyle.',
DeprecationWarning, stacklevel=2)
super(BarStyle, self).__init__(*args, **kwargs)
class BarChart(common.BaseChart):
"""Represents a bar chart.
Object attributes:
vertical: if True, the bars will be vertical. Default is True.
stacked: if True, the bars will be stacked. Default is False.
style: The BarChartStyle for all bars on this chart, specifying bar
thickness and gaps between bars.
"""
def __init__(self, points=None):
"""Constructor for BarChart objects."""
super(BarChart, self).__init__()
if points is not None:
self.AddBars(points)
self.vertical = True
self.stacked = False
self.style = BarChartStyle(None, None, None) # full auto
def AddBars(self, points, label=None, color=None):
"""Add a series of bars to the chart.
points: List of y-values for the bars in this series
label: Name of the series (used in the legend)
color: Hex string, like '00ff00' for green
This is a convenience method which constructs & appends the DataSeries for
you.
"""
if label is not None and util._IsColor(label):
warnings.warn('Your code may be broken! '
'Label is a hex triplet. Maybe it is a color? The '
'old argument order (color before label) is deprecated.',
DeprecationWarning, stacklevel=2)
style = BarsStyle(color)
series = common.DataSeries(points, label=label, style=style)
self.data.append(series)
return series
def GetDependentAxes(self):
"""Get the dependendant axes, which depend on orientation."""
if self.vertical:
return (self._axes[common.AxisPosition.LEFT] +
self._axes[common.AxisPosition.RIGHT])
else:
return (self._axes[common.AxisPosition.TOP] +
self._axes[common.AxisPosition.BOTTOM])
def GetIndependentAxes(self):
"""Get the independendant axes, which depend on orientation."""
if self.vertical:
return (self._axes[common.AxisPosition.TOP] +
self._axes[common.AxisPosition.BOTTOM])
else:
return (self._axes[common.AxisPosition.LEFT] +
self._axes[common.AxisPosition.RIGHT])
def GetDependentAxis(self):
"""Get the main dependendant axis, which depends on orientation."""
if self.vertical:
return self.left
else:
return self.bottom
def GetIndependentAxis(self):
"""Get the main independendant axis, which depends on orientation."""
if self.vertical:
return self.bottom
else:
return self.left
def GetMinMaxValues(self):
"""Get the largest & smallest bar values as (min_value, max_value)."""
if not self.stacked:
return super(BarChart, self).GetMinMaxValues()
if not self.data:
return None, None # No data, nothing to do.
num_bars = max(len(series.data) for series in self.data)
positives = [0 for i in xrange(0, num_bars)]
negatives = list(positives)
for series in self.data:
for i, point in enumerate(series.data):
if point:
if point > 0:
positives[i] += point
else:
negatives[i] += point
min_value = min(min(positives), min(negatives))
max_value = max(max(positives), max(negatives))
return min_value, max_value