Skip to content

Commit 5796a9e

Browse files
committed
added list_options to utils
1 parent 939a8ba commit 5796a9e

File tree

2 files changed

+194
-9
lines changed

2 files changed

+194
-9
lines changed

plotly/figure_factory/_bullet.py

Lines changed: 179 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,199 @@
11
from __future__ import absolute_import
22

3-
from plotly import colors, exceptions, optional_imports
3+
from plotly import exceptions, optional_imports
44
from plotly.figure_factory import utils
5-
from plotly.graph_objs import graph_objs
65

7-
import math
8-
import copy
6+
import plotly
7+
import plotly.graph_objs as go
8+
99
from numbers import Number
10+
import pandas as pd
1011

1112
pd = optional_imports.get_module('pandas')
1213

14+
DARK_BLUE = 'rgb(31, 119, 180)'
15+
LIGHT_BLUE = 'rgb(176, 196, 221)'
16+
BAD_COLOR ='rgb(204, 204, 204)'
17+
OK_COLOR = 'rgb(221, 221, 221)'
18+
GOOD_COLOR = 'rgb(238, 238, 238)'
19+
SUBPLOT_SPACING = 0.015
20+
VALID_KEYS = ['title', 'subtitle', 'ranges', 'measures', 'markers']
21+
22+
# add shapes
23+
def shape(color, x0, x1, y0, y1, xref, yref, layer):
24+
return {
25+
'fillcolor': color,
26+
'line': {'width': 0},
27+
'opacity': 1,
28+
'type': 'rect',
29+
'x0': x0,
30+
'x1': x1,
31+
'xref': xref,
32+
'y0': y0,
33+
'y1': y1,
34+
'yref': yref,
35+
'layer': layer
36+
}
37+
38+
39+
def _bullet_rows(df, marker_symbol):
40+
num_of_rows = len(df.columns)
41+
fig = plotly.tools.make_subplots(
42+
num_of_rows, 1, print_grid=False, horizontal_spacing=SUBPLOT_SPACING,
43+
vertical_spacing=SUBPLOT_SPACING
44+
)
45+
46+
fig['layout'].update(
47+
dict(shapes=[]),
48+
showlegend=False,
49+
annotations=[],
50+
margin=dict(l=120),
51+
)
52+
53+
# add marker symbol
54+
if num_of_rows <= 3:
55+
marker_size = 14
56+
else:
57+
marker_size = 10
58+
# marker
59+
for index in range(num_of_rows):
60+
fig['data'].append(
61+
go.Scatter(
62+
x=df.iloc[index]['markers'],
63+
y=[0.5],
64+
marker=dict(
65+
size=marker_size,
66+
color='rgb(0, 0, 0)',
67+
symbol=marker_symbol
68+
),
69+
xaxis='x{}'.format(index + 1),
70+
yaxis='y{}'.format(index + 1)
71+
)
72+
)
73+
74+
for key in fig['layout'].keys():
75+
if 'xaxis' in key:
76+
fig['layout'][key]['showgrid'] = False
77+
fig['layout'][key]['zeroline'] = False
78+
fig['layout'][key]['tickwidth'] = 1
79+
elif 'yaxis' in key:
80+
fig['layout'][key]['showgrid'] = False
81+
fig['layout'][key]['zeroline'] = False
82+
fig['layout'][key]['showticklabels'] = False
83+
fig['layout'][key]['range'] = [0, 1]
84+
85+
# ranges
86+
y0 = 0.35
87+
y1 = 0.65
88+
for axis_num in range(num_of_rows):
89+
fig['layout']['shapes'].append(
90+
shape(
91+
BAD_COLOR, 0, df.iloc[axis_num]['ranges'][0], y0, y1,
92+
'x{}'.format(axis_num + 1),
93+
'y{}'.format(axis_num + 1),
94+
'below'
95+
)
96+
)
97+
fig['layout']['shapes'].append(
98+
shape(
99+
OK_COLOR, df.iloc[axis_num]['ranges'][0],
100+
df.iloc[axis_num]['ranges'][1], y0, y1,
101+
'x{}'.format(axis_num + 1),
102+
'y{}'.format(axis_num + 1),
103+
'below'
104+
)
105+
)
106+
fig['layout']['shapes'].append(
107+
shape(
108+
GOOD_COLOR, df.iloc[axis_num]['ranges'][1],
109+
df.iloc[axis_num]['ranges'][2], y0, y1,
110+
'x{}'.format(axis_num + 1),
111+
'y{}'.format(axis_num + 1),
112+
'below'
113+
)
114+
)
115+
116+
# measures
117+
y0 = 0.45
118+
y1 = 0.55
119+
for axis_num in range(num_of_rows):
120+
darkblue_len = df.iloc[axis_num]['measures'][0]
121+
lightblue_len = df.iloc[axis_num]['measures'][1]
122+
fig['layout']['shapes'].append(
123+
shape(
124+
DARK_BLUE, 0, darkblue_len, y0, y1,
125+
'x{}'.format(axis_num + 1),
126+
'y{}'.format(axis_num + 1),
127+
'below'
128+
)
129+
)
130+
fig['layout']['shapes'].append(
131+
shape(
132+
LIGHT_BLUE, darkblue_len, lightblue_len, y0, y1,
133+
'x{}'.format(axis_num + 1),
134+
'y{}'.format(axis_num + 1),
135+
'below'
136+
)
137+
)
13138

14-
def create_bullet(df):
139+
# labels
140+
fig['layout']['annotations'] = []
141+
for k in range(num_of_rows):
142+
title = df.iloc[k]['title']
143+
subtitle = df.iloc[k]['subtitle']
144+
145+
label = '<b>{}</b><br>{}'.format(title, subtitle)
146+
annot = utils.annotation_dict_for_label(
147+
label, k + 1, num_of_rows, SUBPLOT_SPACING, 'row', True, False
148+
)
149+
fig['layout']['annotations'].append(annot)
150+
return fig
151+
152+
def create_bullet(df, as_rows=True, marker_symbol='diamond-tall',
153+
title='Bullet Chart', height=600, width=1000):
15154
"""
16155
Returns figure for bullet chart.
17156
18-
:param (pd.DataFrame) df:
19-
157+
:param (pd.DataFrame | list) df: either a JSON list of dicts or a pandas
158+
DataFrame. Must contain the keys 'title', 'subtitle', 'ranges',
159+
'measures', and 'markers'.
160+
:param (str) title: title of the bullet chart.
20161
"""
162+
# validate df
21163
if not pd:
22164
raise exceptions.ImportError(
23165
"'pandas' must be imported for this figure_factory."
24166
)
25167

26-
if not isinstance(df, pd.DataFrame):
168+
if isinstance(df, list):
169+
if not all(isinstance(item, dict) for item in df):
170+
raise exceptions.PlotlyError(
171+
"If your data is a list, all entries must be dictionaries."
172+
)
173+
df = pd.DataFrame(df)
174+
175+
elif not isinstance(df, pd.DataFrame):
27176
raise exceptions.PlotlyError(
28-
"You must input a pandas DataFrame."
177+
"You must input a pandas DataFrame or a list of dictionaries."
29178
)
179+
180+
# check for all keys
181+
#for df_keys in list(df.columns):
182+
if any(key not in VALID_KEYS for key in df.columns):
183+
raise exceptions.PlotlyError(
184+
"The valid keys you need are"
185+
)
186+
#utils.list_of_options
187+
188+
if as_rows:
189+
fig = _bullet_rows(df, marker_symbol)
190+
else:
191+
fig = _bullet_cols()
192+
193+
fig['layout'].update(
194+
title=title,
195+
height=height,
196+
width=width,
197+
)
198+
199+
return fig

plotly/figure_factory/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,18 @@ def annotation_dict_for_label(text, lane, num_of_lanes, subplot_spacing,
555555
)
556556
)
557557
return annotation_dict
558+
559+
560+
def list_of_options(iterable, conj='and', period=True):
561+
"""
562+
Returns an English listing of objects seperated by commas ','
563+
564+
For example, ['foo', 'bar', 'baz'] becomes 'foo, bar and baz'
565+
if the conjunction 'and' is selected.
566+
"""
567+
if len(iterable) < 2:
568+
raise exceptions.PlotlyError(
569+
'Your list or tuple must contain at least 2 items.'
570+
)
571+
template = (len(iterable) - 2)*'{}, ' + '{} ' + conj + ' {}' + period*'.'
572+
return template.format(*iterable)

0 commit comments

Comments
 (0)