Skip to content

Commit 79c67c7

Browse files
committed
finished chris comments and updated tests
1 parent 6da599f commit 79c67c7

File tree

2 files changed

+114
-59
lines changed

2 files changed

+114
-59
lines changed

plotly/figure_factory/_bullet.py

Lines changed: 103 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010

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

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

15-
16-
def _bullet(df, markers, measures, ranges, subtitle, title, orientation,
14+
def _bullet(df, markers, measures, ranges, subtitles, titles, orientation,
1715
range_colors, measure_colors, horizontal_spacing,
18-
vertical_spacing, scatter_options):
16+
vertical_spacing, scatter_options, layout_options):
17+
1918
num_of_lanes = len(df)
2019
num_of_rows = num_of_lanes if orientation == 'h' else 1
2120
num_of_cols = 1 if orientation == 'h' else num_of_lanes
@@ -30,11 +29,18 @@ def _bullet(df, markers, measures, ranges, subtitle, title, orientation,
3029
# layout
3130
fig['layout'].update(
3231
dict(shapes=[]),
32+
title='Bullet Chart',
33+
height=600,
34+
width=1000,
3335
showlegend=False,
3436
barmode='stack',
37+
annotations=[],
3538
margin=dict(l=120 if orientation == 'h' else 80),
3639
)
3740

41+
# update layout
42+
fig['layout'].update(layout_options)
43+
3844
if orientation == 'h':
3945
width_axis = 'yaxis'
4046
length_axis = 'xaxis'
@@ -120,22 +126,23 @@ def _bullet(df, markers, measures, ranges, subtitle, title, orientation,
120126
markers = go.Scatter(
121127
x=x,
122128
y=y,
123-
marker=dict(
124-
color='rgb(0, 0, 0)',
125-
symbol=marker_symbol,
126-
size=marker_size
127-
),
129+
marker=scatter_options['marker'],
128130
name='markers',
129131
hoverinfo='x' if orientation == 'h' else 'y',
130132
xaxis='x{}'.format(row + 1),
131133
yaxis='y{}'.format(row + 1)
132134
)
135+
136+
for k in scatter_options:
137+
if k != 'marker':
138+
markers[k] = scatter_options[k]
139+
133140
fig['data'].append(markers)
134141

135-
# labels
136-
title = df.iloc[row]['title']
137-
if 'subtitle' in df:
138-
subtitle = '<br>{}'.format(df.iloc[row]['subtitle'])
142+
# titles and subtitles
143+
title = df.iloc[row]['titles']
144+
if 'subtitles' in df:
145+
subtitle = '<br>{}'.format(df.iloc[row]['subtitles'])
139146
else:
140147
subtitle = ''
141148
label = '<b>{}</b>'.format(title) + subtitle
@@ -154,16 +161,29 @@ def _bullet(df, markers, measures, ranges, subtitle, title, orientation,
154161

155162

156163
def create_bullet(data, markers=None, measures=None, ranges=None,
157-
subtitle=None, title=None, orientation='h',
158-
range_colors=None, measure_colors=None, horizontal_spacing=None,
159-
vertical_spacing=None, chart_title='Bullet Chart',
160-
height=600, width=1000, **scatter_options):
164+
subtitles=None, titles=None, orientation='h',
165+
range_colors=None, measure_colors=None,
166+
horizontal_spacing=None, vertical_spacing=None,
167+
scatter_options={}, **layout_options):
161168
"""
162169
Returns figure for bullet chart.
163170
164171
:param (pd.DataFrame | list) data: either a JSON list of dicts or a pandas
165-
DataFrame. All keys must be one of 'title', 'subtitle', 'ranges',
166-
'measures', and 'markers'.
172+
DataFrame.
173+
:param (str) markers: the column name or dictionary key for the markers in
174+
each subplot.
175+
:param (str) measures: the column name or dictionary key for the measure
176+
bars in each subplot. This bar usually represents the quantitative
177+
measure of performance, usually a list of two values [a, b] and are
178+
the blue bars in the foreground of each subplot by default.
179+
:param (str) ranges: the column name or dictionary key for the qualitative
180+
ranges of performance, usually a 3-item list [bad, okay, good]. They
181+
correspond to the grey bars in the background of each chart.
182+
:param (str) subtitles: the column name or dictionary key for the subtitle
183+
of each subplot chart. The subplots are displayed right underneath
184+
each title.
185+
:param (str) titles: the column name or dictionary key for the main label
186+
of each subplot chart.
167187
:param (bool) orientation: if 'h', the bars are placed horizontally as
168188
rows. If 'v' the bars are placed vertically in the chart.
169189
:param (int) marker_size: sets the size of the markers in the chart.
@@ -181,9 +201,55 @@ def create_bullet(data, markers=None, measures=None, ranges=None,
181201
plotly.tools.make_subplots. Ranges between 0 and 1.
182202
:param (float) vertical_spacing: see the 'vertical_spacing' param in
183203
plotly.tools.make_subplots. Ranges between 0 and 1.
184-
:param (str) title: title of the bullet chart.
185-
:param (float) height: height of the chart.
186-
:param (float) width width of the chart.
204+
:param (dict) scatter_options: describes attributes for the scatter trace
205+
in each subplot such as name and marker size. Call
206+
help(plotly.graph_objs.Scatter) for more information on valid params.
207+
:param layout_options: describes attributes for the layout of the figure
208+
such as title, height and width. Call help(plotly.graph_objs.Layout)
209+
for more information on valid params.
210+
211+
Example 1: Use a Dictionary
212+
```
213+
import plotly
214+
import plotly.plotly as py
215+
import plotly.figure_factory as ff
216+
217+
data = [
218+
{"e": "Revenue", "d": "US$, in thousands", "c": [150, 225, 300],
219+
"b": [220,270], "a": [250]},
220+
{"e": "Profit", "d": "%", "c": [20, 25, 30], "b": [21, 23], "a": [26]},
221+
{"e": "Order Size", "d":"US$, average","c": [350, 500, 600],
222+
"b": [100,320],"a": [550]},
223+
{"e": "New Customers", "d": "count", "c": [1400, 2000, 2500],
224+
"b": [1000,1650],"a": [2100]},
225+
{"e": "Satisfaction", "d": "out of 5","c": [3.5, 4.25, 5],
226+
"b": [3.2,4.7], "a": [4.4]}
227+
]
228+
229+
fig = ff.create_bullet(
230+
data, titles='e', subtitles='d', markers='a', measures='b',
231+
ranges='c', orientation='h', title='my simple bullet chart'
232+
)
233+
py.iplot(fig)
234+
```
235+
236+
Example 2: Use a DataFrame with Custom Colors
237+
```
238+
import plotly.plotly as py
239+
import plotly.figure_factory as ff
240+
241+
import pandas as pd
242+
243+
data = pd.read_json('https://cdn.rawgit.com/plotly/datasets/master/BulletData.json')
244+
245+
fig = ff.create_bullet(
246+
data, titles='title', markers='markers', measures='measures',
247+
orientation='v', measure_colors=['rgb(14, 52, 75)', 'rgb(31, 141, 127)'],
248+
scatter_options={'marker': {'symbol': 'circle'}}, width=700
249+
250+
)
251+
py.iplot(fig)
252+
```
187253
"""
188254
# validate df
189255
if not pd:
@@ -203,12 +269,12 @@ def create_bullet(data, markers=None, measures=None, ranges=None,
203269
)
204270

205271
# make DataFrame from data with correct column headers
206-
col_names = ['title', 'subtitle', 'markers', 'measures', 'ranges']
272+
col_names = ['titles', 'subtitle', 'markers', 'measures', 'ranges']
207273
if isinstance(data, list):
208274
df = pd.DataFrame(
209275
[
210-
[d[title] for d in data] if title else [''] * len(data),
211-
[d[subtitle] for d in data] if subtitle else [''] * len(data),
276+
[d[titles] for d in data] if titles else [''] * len(data),
277+
[d[subtitles] for d in data] if subtitles else [''] * len(data),
212278
[d[markers] for d in data] if markers else [[]] * len(data),
213279
[d[measures] for d in data] if measures else [[]] * len(data),
214280
[d[ranges] for d in data] if ranges else [[]] * len(data),
@@ -218,8 +284,8 @@ def create_bullet(data, markers=None, measures=None, ranges=None,
218284
elif isinstance(data, pd.DataFrame):
219285
df = pd.DataFrame(
220286
[
221-
data[title].tolist() if title else [''] * len(data),
222-
data[subtitle].tolist() if subtitle else [''] * len(data),
287+
data[titles].tolist() if titles else [''] * len(data),
288+
data[subtitles].tolist() if subtitles else [''] * len(data),
223289
data[markers].tolist() if markers else [[]] * len(data),
224290
data[measures].tolist() if measures else [[]] * len(data),
225291
data[ranges].tolist() if ranges else [[]] * len(data),
@@ -250,30 +316,25 @@ def create_bullet(data, markers=None, measures=None, ranges=None,
250316
colors_list = colors.convert_colors_to_same_type(colors_list,
251317
'rgb')[0]
252318

253-
# scatter options
254-
default_scatter_options = {
319+
# default scatter options
320+
default_scatter = {
255321
'marker': {'size': 12,
256322
'symbol': 'diamond-tall',
257323
'color': 'rgb(0, 0, 0)'}
258324
}
325+
259326
if scatter_options == {}:
260-
scatter_options.update(default_scatter_options)
327+
scatter_options.update(default_scatter)
261328
else:
262-
263-
264-
265-
329+
# add default options to scatter_options if they are not present
330+
for k in default_scatter['marker']:
331+
if k not in scatter_options['marker']:
332+
scatter_options['marker'][k] = default_scatter['marker'][k]
266333

267334
fig = _bullet(
268-
df, markers, measures, ranges, subtitle, title, orientation,
335+
df, markers, measures, ranges, subtitles, titles, orientation,
269336
range_colors, measure_colors, horizontal_spacing, vertical_spacing,
270-
scatter_options
271-
)
272-
273-
fig['layout'].update(
274-
title=chart_title,
275-
height=height,
276-
width=width,
337+
scatter_options, layout_options,
277338
)
278339

279340
return fig

plotly/tests/test_optional/test_figure_factory.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,7 +2189,7 @@ class TestBullet(NumpyTestUtilsMixin, TestCase):
21892189

21902190
def test_df_as_list(self):
21912191
df = [
2192-
{'title': 'Revenue'},
2192+
{'titles': 'Revenue'},
21932193
'foo'
21942194
]
21952195

@@ -2202,15 +2202,6 @@ def test_not_df_or_list(self):
22022202
pattern = ('You must input a pandas DataFrame or a list of dictionaries.')
22032203
self.assertRaisesRegexp(PlotlyError, pattern, ff.create_bullet, df)
22042204

2205-
def test_valid_keys(self):
2206-
df = [{'title': 'Revenue', 'foo': 'bar'}]
2207-
VALID_KEYS = ['title', 'subtitle', 'ranges', 'measures', 'markers']
2208-
2209-
pattern = (
2210-
'Your headers/dict keys must be either {}'
2211-
).format(utils.list_of_options(VALID_KEYS, 'or'))
2212-
self.assertRaisesRegexp(PlotlyError, pattern, ff.create_bullet, df)
2213-
22142205
def test_valid_color_lists_of_2_rgb_colors(self):
22152206
df = [
22162207
{'title': 'Revenue'}
@@ -2238,9 +2229,12 @@ def test_full_bullet(self):
22382229
range_colors = ['rgb(255, 127, 14)', 'rgb(44, 160, 44)']
22392230

22402231
fig = ff.create_bullet(
2241-
df, as_rows=False, marker_size=30, marker_symbol='hourglass',
2232+
df, orientation='v', markers='markers', measures='measures',
2233+
ranges='ranges', subtitles='subtitle', titles='title',
22422234
range_colors=range_colors, measure_colors=measure_colors,
2243-
title='new title'
2235+
title='new title',
2236+
scatter_options={'marker': {'size': 30,
2237+
'symbol': 'hourglass'}}
22442238
)
22452239

22462240
exp_fig = {
@@ -2571,7 +2565,7 @@ def test_full_bullet(self):
25712565
'yaxis': 'y5'}],
25722566
'layout': {'annotations': [{'font': {'color': '#0f0f0f', 'size': 13},
25732567
'showarrow': False,
2574-
'text': '<b>Revenue</b><br>US$, in thousands',
2568+
'text': '<b>Revenue</b>',
25752569
'textangle': 0,
25762570
'x': 0.019999999999999997,
25772571
'xanchor': 'center',
@@ -2581,7 +2575,7 @@ def test_full_bullet(self):
25812575
'yref': 'paper'},
25822576
{'font': {'color': '#0f0f0f', 'size': 13},
25832577
'showarrow': False,
2584-
'text': '<b>Profit</b><br>%',
2578+
'text': '<b>Profit</b>',
25852579
'textangle': 0,
25862580
'x': 0.26,
25872581
'xanchor': 'center',
@@ -2591,7 +2585,7 @@ def test_full_bullet(self):
25912585
'yref': 'paper'},
25922586
{'font': {'color': '#0f0f0f', 'size': 13},
25932587
'showarrow': False,
2594-
'text': '<b>Order Size</b><br>US$, average',
2588+
'text': '<b>Order Size</b>',
25952589
'textangle': 0,
25962590
'x': 0.5,
25972591
'xanchor': 'center',
@@ -2601,7 +2595,7 @@ def test_full_bullet(self):
26012595
'yref': 'paper'},
26022596
{'font': {'color': '#0f0f0f', 'size': 13},
26032597
'showarrow': False,
2604-
'text': '<b>New Customers</b><br>count',
2598+
'text': '<b>New Customers</b>',
26052599
'textangle': 0,
26062600
'x': 0.74,
26072601
'xanchor': 'center',
@@ -2611,7 +2605,7 @@ def test_full_bullet(self):
26112605
'yref': 'paper'},
26122606
{'font': {'color': '#0f0f0f', 'size': 13},
26132607
'showarrow': False,
2614-
'text': '<b>Satisfaction</b><br>out of 5',
2608+
'text': '<b>Satisfaction</b>',
26152609
'textangle': 0,
26162610
'x': 0.98,
26172611
'xanchor': 'center',

0 commit comments

Comments
 (0)