Skip to content

Commit 69bd30d

Browse files
committed
reworked bullet charts with bar traces
1 parent 8ba62a2 commit 69bd30d

File tree

2 files changed

+392
-396
lines changed

2 files changed

+392
-396
lines changed

plotly/figure_factory/_bullet.py

Lines changed: 89 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,33 @@
1010

1111
pd = optional_imports.get_module('pandas')
1212

13-
SUBPLOT_SPACING = 0.015
1413
VALID_KEYS = ['title', 'subtitle', 'ranges', 'measures', 'markers']
1514

1615

17-
# add shapes
18-
def rectangle(color, x0, x1, y0, y1, xref, yref, layer):
19-
return {
20-
'fillcolor': color,
21-
'line': {'width': 0},
22-
'opacity': 1,
23-
'type': 'rect',
24-
'x0': x0,
25-
'x1': x1,
26-
'xref': xref,
27-
'y0': y0,
28-
'y1': y1,
29-
'yref': yref,
30-
'layer': layer
31-
}
32-
33-
3416
def _bullet(df, as_rows, marker_size, marker_symbol, range_colors,
35-
measure_colors):
17+
measure_colors, subplot_spacing):
3618
num_of_lanes = len(df)
3719
num_of_rows = num_of_lanes if as_rows else 1
3820
num_of_cols = 1 if as_rows else num_of_lanes
21+
if not subplot_spacing:
22+
subplot_spacing = 1./num_of_lanes
3923
fig = plotly.tools.make_subplots(
4024
num_of_rows, num_of_cols, print_grid=False,
41-
horizontal_spacing=SUBPLOT_SPACING,
42-
vertical_spacing=SUBPLOT_SPACING
25+
horizontal_spacing=subplot_spacing,
26+
vertical_spacing=subplot_spacing
4327
)
4428

4529
# layout
4630
fig['layout'].update(
4731
dict(shapes=[]),
4832
showlegend=False,
49-
annotations=[],
33+
barmode='stack',
5034
margin=dict(l=120 if as_rows else 80),
5135
)
5236

5337
if as_rows:
54-
length_axis = 'xaxis'
5538
width_axis = 'yaxis'
39+
length_axis = 'xaxis'
5640
else:
5741
width_axis = 'xaxis'
5842
length_axis = 'yaxis'
@@ -67,105 +51,102 @@ def _bullet(df, as_rows, marker_size, marker_symbol, range_colors,
6751
fig['layout'][key]['showticklabels'] = False
6852
fig['layout'][key]['range'] = [0, 1]
6953

54+
# narrow ___domain if 1 bar
55+
if num_of_lanes <= 1:
56+
fig['layout'][width_axis + '1']['___domain'] = [0.4, 0.6]
57+
7058
# marker symbol size
7159
if not marker_size:
72-
if num_of_lanes <= 3:
60+
if num_of_lanes <= 4:
7361
marker_size = 18
7462
else:
75-
marker_size = 12
76-
for idx in range(num_of_lanes):
77-
# marker
78-
x = df.iloc[idx]['markers'] if as_rows else [0.5]
79-
y = [0.5] if as_rows else df.iloc[idx]['markers']
80-
fig['data'].append(
81-
go.Scatter(
63+
marker_size = 8
64+
65+
if not range_colors:
66+
range_colors = ['rgb(200, 200, 200)', 'rgb(245, 245, 245)']
67+
if not measure_colors:
68+
measure_colors = ['rgb(31, 119, 180)', 'rgb(176, 196, 221)']
69+
70+
for row in range(num_of_lanes):
71+
# ranges bars
72+
for idx in range(len(df.iloc[row]['ranges'])):
73+
inter_colors = colors.n_colors(
74+
range_colors[0], range_colors[1],
75+
len(df.iloc[row]['ranges']), 'rgb'
76+
)
77+
x = [sorted(df.iloc[row]['ranges'])[-1 - idx]] if as_rows else [0]
78+
y = [0] if as_rows else [sorted(df.iloc[row]['ranges'])[-1 - idx]]
79+
bar = go.Bar(
8280
x=x,
8381
y=y,
8482
marker=dict(
85-
size=marker_size,
86-
color='rgb(0, 0, 0)',
87-
symbol=marker_symbol
83+
color=inter_colors[-1 - idx]
8884
),
89-
xaxis='x{}'.format(idx + 1),
90-
yaxis='y{}'.format(idx + 1)
85+
name='ranges',
86+
hoverinfo='x' if as_rows else 'y',
87+
orientation='h' if as_rows else 'v',
88+
width=2,
89+
base=0,
90+
xaxis='x{}'.format(row + 1),
91+
yaxis='y{}'.format(row + 1)
9192
)
92-
)
93+
fig['data'].append(bar)
9394

94-
# ranges
95-
y0_ranges = 0.35
96-
y1_ranges = 0.65
97-
if not range_colors:
98-
range_colors = ['rgb(200, 200, 200)', 'rgb(245, 245, 245)']
99-
ranges_len = len(df.iloc[idx]['ranges'])
100-
if ranges_len <= 1:
101-
inter_colors = [range_colors[0]]
102-
else:
95+
# measures bars
96+
for idx in range(len(df.iloc[row]['measures'])):
10397
inter_colors = colors.n_colors(
104-
range_colors[0], range_colors[1], ranges_len, 'rgb'
105-
)
106-
for range_idx in range(ranges_len):
107-
color = inter_colors[range_idx]
108-
if range_idx == 0:
109-
start_range = 0
110-
else:
111-
start_range = df.iloc[idx]['ranges'][range_idx - 1]
112-
end_range = df.iloc[idx]['ranges'][range_idx]
113-
114-
x0 = start_range if as_rows else y0_ranges
115-
x1 = end_range if as_rows else y1_ranges
116-
y0 = y0_ranges if as_rows else start_range
117-
y1 = y1_ranges if as_rows else end_range
118-
fig['layout']['shapes'].append(
119-
rectangle(
120-
color, x0, x1, y0, y1,
121-
'x{}'.format(idx + 1),
122-
'y{}'.format(idx + 1),
123-
'below'
124-
)
98+
measure_colors[0], measure_colors[1],
99+
len(df.iloc[row]['measures']), 'rgb'
125100
)
126-
127-
# measures
128-
y0_measures = 0.45
129-
y1_measures = 0.55
130-
if not measure_colors:
131-
measure_colors = ['rgb(31, 119, 180)', 'rgb(176, 196, 221)']
132-
measures_len = len(df.iloc[idx]['measures'])
133-
if measures_len <= 1:
134-
inter_colors = [measure_colors[0]]
135-
else:
136-
inter_colors = colors.n_colors(
137-
measure_colors[0], measure_colors[1], measures_len, 'rgb'
101+
x = ([sorted(df.iloc[row]['measures'])[-1 - idx]] if as_rows
102+
else [0.5])
103+
y = ([0.5] if as_rows
104+
else [sorted(df.iloc[row]['measures'])[-1 - idx]])
105+
bar = go.Bar(
106+
x=x,
107+
y=y,
108+
marker=dict(
109+
color=inter_colors[-1 - idx]
110+
),
111+
name='measures',
112+
hoverinfo='x' if as_rows else 'y',
113+
orientation='h' if as_rows else 'v',
114+
width=0.4,
115+
base=0,
116+
xaxis='x{}'.format(row + 1),
117+
yaxis='y{}'.format(row + 1)
138118
)
139-
for range_idx in range(measures_len):
140-
color = inter_colors[range_idx]
141-
if range_idx == 0:
142-
start_range = 0
143-
else:
144-
start_range = df.iloc[idx]['measures'][range_idx - 1]
145-
end_range = df.iloc[idx]['measures'][range_idx]
119+
fig['data'].append(bar)
146120

147-
x0 = start_range if as_rows else y0_measures
148-
x1 = end_range if as_rows else y1_measures
149-
y0 = y0_measures if as_rows else start_range
150-
y1 = y1_measures if as_rows else end_range
151-
fig['layout']['shapes'].append(
152-
rectangle(
153-
color, x0, x1, y0, y1,
154-
'x{}'.format(idx + 1),
155-
'y{}'.format(idx + 1),
156-
'below'
157-
)
158-
)
121+
# markers
122+
x = df.iloc[row]['markers'] if as_rows else [0.5]
123+
y = [0.5] if as_rows else df.iloc[row]['markers']
124+
markers = go.Scatter(
125+
x=x,
126+
y=y,
127+
marker=dict(
128+
color='rgb(0, 0, 0)',
129+
symbol=marker_symbol,
130+
size=marker_size
131+
),
132+
name='markers',
133+
hoverinfo='x' if as_rows else 'y',
134+
xaxis='x{}'.format(row + 1),
135+
yaxis='y{}'.format(row + 1)
136+
)
137+
fig['data'].append(markers)
159138

160139
# labels
161-
title = df.iloc[idx]['title']
140+
title = df.iloc[row]['title']
162141
if 'subtitle' in df:
163-
subtitle = '<br>{}'.format(df.iloc[idx]['subtitle'])
142+
subtitle = '<br>{}'.format(df.iloc[row]['subtitle'])
164143
else:
165144
subtitle = ''
166145
label = '<b>{}</b>'.format(title) + subtitle
167146
annot = utils.annotation_dict_for_label(
168-
label, num_of_lanes - idx, num_of_lanes, SUBPLOT_SPACING,
147+
label,
148+
(num_of_lanes - row if as_rows else row + 1),
149+
num_of_lanes, subplot_spacing,
169150
'row' if as_rows else 'col',
170151
True if as_rows else False,
171152
False
@@ -177,8 +158,8 @@ def _bullet(df, as_rows, marker_size, marker_symbol, range_colors,
177158

178159
def create_bullet(df, as_rows=True, marker_size=16,
179160
marker_symbol='diamond-tall', range_colors=None,
180-
measure_colors=None, title='Bullet Chart', height=600,
181-
width=1000):
161+
measure_colors=None, subplot_spacing=None,
162+
title='Bullet Chart', height=600, width=1000):
182163
"""
183164
Returns figure for bullet chart.
184165
@@ -198,6 +179,9 @@ def create_bullet(df, as_rows=True, marker_size=16,
198179
:param (list) measure_colors: a list of two colors which is used to color
199180
the thin quantitative bars in the bullet chart.
200181
Default=['rgb(31, 119, 180)', 'rgb(176, 196, 221)']
182+
:param (float) subplot_spacing: set the distance between each bar chart.
183+
If not specified an automatic spacing is assigned based on the number
184+
of bars to be plotted.
201185
:param (str) title: title of the bullet chart.
202186
:param (float) height: height of the chart.
203187
:param (float) width width of the chart.
@@ -260,7 +244,8 @@ def create_bullet(df, as_rows=True, marker_size=16,
260244
'rgb')[0]
261245

262246
fig = _bullet(
263-
df, as_rows, marker_size, marker_symbol, range_colors, measure_colors
247+
df, as_rows, marker_size, marker_symbol, range_colors, measure_colors,
248+
subplot_spacing
264249
)
265250

266251
fig['layout'].update(

0 commit comments

Comments
 (0)