Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 01b7c67

Browse files
committed
Merge pull request plotly#208 from plotly/dataframeable_figures
Dataframeable figures
2 parents c102140 + 16f80d6 commit 01b7c67

File tree

7 files changed

+420
-127
lines changed

7 files changed

+420
-127
lines changed

plotly/graph_objs/graph_objs.py

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,36 @@ def strip_style(self):
158158
for plotly_dict in self:
159159
plotly_dict.strip_style()
160160

161-
def get_data(self):
162-
"""Returns the JSON for the plot with non-data elements stripped."""
161+
def get_data(self, flatten=False):
162+
"""
163+
Returns the JSON for the plot with non-data elements stripped.
164+
165+
Flattening may increase the utility of the result.
166+
167+
:param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''}
168+
:returns: (dict|list) Depending on (flat|unflat)
169+
170+
"""
163171
self.to_graph_objs()
164172
l = list()
165173
for _plotlydict in self:
166-
l += [_plotlydict.get_data()]
174+
l += [_plotlydict.get_data(flatten=flatten)]
167175
del_indicies = [index for index, item in enumerate(self)
168176
if len(item) == 0]
169177
del_ct = 0
170178
for index in del_indicies:
171179
del self[index - del_ct]
172180
del_ct += 1
173-
return l
181+
182+
if flatten:
183+
d = {}
184+
for i, e in enumerate(l):
185+
for k, v in e.items():
186+
key = "{0}.{1}".format(i, k)
187+
d[key] = v
188+
return d
189+
else:
190+
return l
174191

175192
def validate(self, caller=True):
176193
"""Recursively check the validity of the entries in a PlotlyList.
@@ -425,6 +442,8 @@ def strip_style(self):
425442
else:
426443
try:
427444
if INFO[obj_key]['keymeta'][key]['key_type'] == 'style':
445+
446+
# TODO: use graph_objs_tools.value_is_data
428447
if isinstance(self[key], six.string_types):
429448
del self[key]
430449
elif not hasattr(self[key], '__iter__'):
@@ -433,19 +452,25 @@ def strip_style(self):
433452
# print("'type' not in {0} for {1}".format(obj_key, key))
434453
pass
435454

436-
def get_data(self):
455+
def get_data(self, flatten=False):
437456
"""Returns the JSON for the plot with non-data elements stripped."""
438457
self.to_graph_objs()
439458
class_name = self.__class__.__name__
440459
obj_key = NAME_TO_KEY[class_name]
441460
d = dict()
442461
for key, val in list(self.items()):
443462
if isinstance(val, (PlotlyDict, PlotlyList)):
444-
d[key] = val.get_data()
463+
sub_data = val.get_data(flatten=flatten)
464+
if flatten:
465+
for sub_key, sub_val in sub_data.items():
466+
key_string = "{0}.{1}".format(key, sub_key)
467+
d[key_string] = sub_val
468+
else:
469+
d[key] = sub_data
445470
else:
446471
try:
447472
# TODO: Update the JSON
448-
if INFO[obj_key]['keymeta'][key]['key_type'] == 'data':
473+
if graph_objs_tools.value_is_data(obj_key, key, val):
449474
d[key] = val
450475
except KeyError:
451476
pass
@@ -454,8 +479,6 @@ def get_data(self):
454479
if isinstance(d[key], (dict, list)):
455480
if len(d[key]) == 0:
456481
del d[key]
457-
if len(d) == 1:
458-
d = list(d.values())[0]
459482
return d
460483

461484
def to_graph_objs(self, caller=True):
@@ -860,6 +883,42 @@ def to_graph_objs(self, caller=True): # TODO TODO TODO! check logic!
860883
)
861884
super(Data, self).to_graph_objs(caller=caller)
862885
Data.to_graph_objs = to_graph_objs # override method!
886+
887+
def get_data(self, flatten=False):
888+
"""
889+
890+
:param flatten:
891+
:return:
892+
893+
"""
894+
if flatten:
895+
self.to_graph_objs()
896+
data = [v.get_data(flatten=flatten) for v in self]
897+
d = {}
898+
taken_names = []
899+
for i, trace in enumerate(data):
900+
901+
# we want to give the traces helpful names
902+
# however, we need to be sure they're unique too...
903+
trace_name = trace.pop('name', 'trace_{0}'.format(i))
904+
if trace_name in taken_names:
905+
j = 1
906+
new_trace_name = "{0}_{1}".format(trace_name, j)
907+
while new_trace_name in taken_names:
908+
new_trace_name = "{0}_{1}".format(trace_name, j)
909+
j += 1
910+
trace_name = new_trace_name
911+
taken_names.append(trace_name)
912+
913+
# finish up the dot-concatenation
914+
for k, v in trace.items():
915+
key = "{0}.{1}".format(trace_name, k)
916+
d[key] = v
917+
return d
918+
else:
919+
return super(Data, self).get_data(flatten=flatten)
920+
Data.get_data = get_data
921+
863922
return Data
864923

865924
Data = get_patched_data_class(Data)
@@ -934,6 +993,26 @@ def print_grid(self):
934993
print(grid_str)
935994
Figure.print_grid = print_grid
936995

996+
def get_data(self, flatten=False):
997+
"""
998+
Returns the JSON for the plot with non-data elements stripped.
999+
1000+
Flattening may increase the utility of the result.
1001+
1002+
:param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''}
1003+
:returns: (dict|list) Depending on (flat|unflat)
1004+
1005+
"""
1006+
self.to_graph_objs()
1007+
return self['data'].get_data(flatten=flatten)
1008+
Figure.get_data = get_data
1009+
1010+
def to_dataframe(self):
1011+
data = self.get_data(flatten=True)
1012+
from pandas import DataFrame, Series
1013+
return DataFrame(dict([(k, Series(v)) for k, v in data.items()]))
1014+
Figure.to_dataframe = to_dataframe
1015+
9371016
def append_trace(self, trace, row, col):
9381017
""" Helper function to add a data traces to your figure
9391018
that is bound to axes at the row, col index.

plotly/graph_objs/graph_objs_tools.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,36 @@ def curtail_val_repr(val, max_chars, add_delim=False):
226226
if add_delim:
227227
r += delim
228228
return r
229+
230+
231+
def value_is_data(obj_name, key, value):
232+
"""
233+
Values have types associated with them based on graph_reference.
234+
235+
'data' type values are always kept
236+
'style' values are kept if they're sequences (but not strings)
237+
238+
:param (str) obj_name: E.g., 'scatter', 'figure'
239+
:param (str) key: E.g., 'x', 'y', 'text'
240+
:param (*) value:
241+
:returns: (bool)
242+
243+
"""
244+
try:
245+
key_type = INFO[obj_name]['keymeta'][key]['key_type']
246+
except KeyError:
247+
return False
248+
249+
if key_type not in ['data', 'style']:
250+
return False
251+
252+
if key_type == 'data':
253+
return True
254+
255+
if key_type == 'style':
256+
iterable = hasattr(value, '__iter__')
257+
stringy = isinstance(value, six.string_types)
258+
dicty = isinstance(value, dict)
259+
return iterable and not stringy and not dicty
260+
261+
return False

0 commit comments

Comments
 (0)