Skip to content

added histogram to facet_grid #776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 1, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).


## [Unreleased]
### Added
- `figure_factory.create_facet_grid` now supports histogram traces.

## [2.0.11] - 2017-06-20
### Updated
- Updated `plotly.min.js` to version 1.28.1 for `plotly.offline`.
Expand Down
111 changes: 76 additions & 35 deletions plotly/figure_factory/_facet_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
THRES_FOR_FLIPPED_FACET_TITLES = 10
GRID_WIDTH = 1

VALID_TRACE_TYPES = ['scatter', 'scattergl', 'histogram']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically other types too though, right? I think that box, histogram2d, and bar might work now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember not liking the look of box or bar - it was very squashed up, but perhaps it was the dataset


CUSTOM_LABEL_ERROR = (
"If you are using a dictionary for custom labels for the facet row/col, "
Expand Down Expand Up @@ -189,18 +190,22 @@ def _facet_grid_color_categorical(df, x, y, facet_row, facet_col, color_name,
if not facet_row and not facet_col:
color_groups = list(df.groupby(color_name))
for group in color_groups:
trace = graph_objs.Scatter(
trace = dict(
x=group[1][x],
y=group[1][y],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time understanding what happens when the histogram mode sets both x and y here. Shouldn't only one of them be set?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is just the other variable ignored?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, or the user just doesn't supply the y variable when setting it? Like

FF.facet_grid(df, x='categorial-column', facet_col='another-column', trace_type='histogram')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You found an oversight in my design.

Originally this was meant for just scatter, so I was forcing x and y. I'm going to try not forcing x and y and putting df[x] and df[y] in the trace dictionary depending on what the user provides

mode='markers',
type=trace_type,
name=group[0],
marker=dict(
color=colormap[group[0]],
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=colormap[group[0]], **kwargs_marker
)

fig.append_trace(trace, 1, 1)

elif (facet_row and not facet_col) or (not facet_row and facet_col):
Expand All @@ -210,18 +215,22 @@ def _facet_grid_color_categorical(df, x, y, facet_row, facet_col, color_name,
for j, group in enumerate(groups_by_facet):
for color_val in df[color_name].unique():
data_by_color = group[1][group[1][color_name] == color_val]
trace = graph_objs.Scatter(
trace = dict(
x=data_by_color[x],
y=data_by_color[y],
mode='markers',
type=trace_type,
name=color_val,
marker=dict(
color=colormap[color_val],
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=colormap[group[0]], **kwargs_marker
)

fig.append_trace(trace,
j + 1 if facet_row else 1,
1 if facet_row else j + 1)
Expand Down Expand Up @@ -261,32 +270,41 @@ def _facet_grid_color_categorical(df, x, y, facet_row, facet_col, color_name,
if group.values.tolist() != [[None, None, None]]:
group_filtered = group[group[color_name] == color_val]

trace = graph_objs.Scatter(
trace = dict(
x=group_filtered[x],
y=group_filtered[y],
mode='markers',
type=trace_type,
name=color_val,
marker=dict(
color=colormap[color_val],
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=colormap[color_val], **kwargs_marker
)

else:
trace = graph_objs.Scatter(
trace = dict(
x=group[x],
y=group[y],
mode='markers',
type=trace_type,
name=color_val,
marker=dict(
color=colormap[color_val],
**kwargs_marker
),
showlegend=False,
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=colormap[color_val], **kwargs_marker
)


fig.append_trace(trace, row_count + 1, col_count + 1)
if row_count == 0:
label = _return_label(col_values[col_count],
Expand Down Expand Up @@ -322,40 +340,48 @@ def _facet_grid_color_numerical(df, x, y, facet_row, facet_col, color_name,

annotations = []
if not facet_row and not facet_col:
trace = graph_objs.Scatter(
trace = dict(
x=df[x],
y=df[y],
mode='markers',
type=trace_type,
marker=dict(
color=df[color_name],
colorscale=colormap,
showscale=True,
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=df[color_name], **kwargs_marker
)

fig.append_trace(trace, 1, 1)

if (facet_row and not facet_col) or (not facet_row and facet_col):
groups_by_facet = list(
df.groupby(facet_row if facet_row else facet_col)
)
for j, group in enumerate(groups_by_facet):
trace = graph_objs.Scatter(
trace = dict(
x=group[1][x],
y=group[1][y],
mode='markers',
type=trace_type,
marker=dict(
color=df[color_name],
colorscale=colormap,
showscale=True,
colorbar=dict(x=1.15),
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=df[color_name], **kwargs_marker
)

fig.append_trace(
trace,
j + 1 if facet_row else 1,
Expand Down Expand Up @@ -392,29 +418,33 @@ def _facet_grid_color_numerical(df, x, y, facet_row, facet_col, color_name,
columns=[x, y, color_name])

if group.values.tolist() != [[None, None, None]]:
trace = graph_objs.Scatter(
trace = dict(
x=group[x],
y=group[y],
mode='markers',
type=trace_type,
marker=dict(
color=df[color_name],
colorscale=colormap,
showscale=(row_count == 0),
colorbar=dict(x=1.15),
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=df[color_name], **kwargs_marker
)

else:
trace = graph_objs.Scatter(
trace = dict(
x=group[x],
y=group[y],
mode='markers',
type=trace_type,
showlegend=False,
**kwargs
)

fig.append_trace(trace, row_count + 1, col_count + 1)
if row_count == 0:
label = _return_label(col_values[col_count],
Expand Down Expand Up @@ -448,35 +478,39 @@ def _facet_grid(df, x, y, facet_row, facet_col, num_of_rows,
vertical_spacing=SUBPLOT_SPACING, print_grid=False)
annotations = []
if not facet_row and not facet_col:
trace = graph_objs.Scatter(
trace = dict(
x=df[x],
y=df[y],
mode='markers',
type=trace_type,
marker=dict(
color=marker_color,
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(color=marker_color, **kwargs_marker)

fig.append_trace(trace, 1, 1)

elif (facet_row and not facet_col) or (not facet_row and facet_col):
groups_by_facet = list(
df.groupby(facet_row if facet_row else facet_col)
)
for j, group in enumerate(groups_by_facet):
trace = graph_objs.Scatter(
trace = dict(
x=group[1][x],
y=group[1][y],
mode='markers',
type=trace_type,
marker=dict(
color=marker_color,
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(color=marker_color, **kwargs_marker)

fig.append_trace(trace,
j + 1 if facet_row else 1,
1 if facet_row else j + 1)
Expand Down Expand Up @@ -510,17 +544,21 @@ def _facet_grid(df, x, y, facet_row, facet_col, num_of_rows,
group = tuple_to_facet_group[(x_val, y_val)]
except KeyError:
group = pd.DataFrame([[None, None]], columns=[x, y])
trace = graph_objs.Scatter(
trace = dict(
x=group[x],
y=group[y],
mode='markers',
type=trace_type,
marker=dict(
color=marker_color,
**kwargs_marker
),
**kwargs_trace
)
if trace_type in ['scatter', 'scattergl']:
trace['mode'] = 'markers'
trace['marker'] = dict(
color=marker_color, **kwargs_marker
)

fig.append_trace(trace, row_count + 1, col_count + 1)
if row_count == 0:
label = _return_label(col_values[col_count],
Expand Down Expand Up @@ -581,7 +619,7 @@ def create_facet_grid(df, x, y, facet_row=None, facet_col=None,
:param (int) height: the height of the facet grid figure.
:param (int) width: the width of the facet grid figure.
:param (str) trace_type: decides the type of plot to appear in the
facet grid. The options are 'scatter' and 'scattergl'.
facet grid. The options are 'scatter', 'scattergl' and 'histogram'.
Default = 'scatter'.
:param (str) scales: determines if axes have fixed ranges or not. Valid
settings are 'fixed' (all axes fixed), 'free_x' (x axis free only),
Expand Down Expand Up @@ -719,16 +757,19 @@ def create_facet_grid(df, x, y, facet_row=None, facet_col=None,
"x, y, facet_row, facet_col and color_name must be keys "
"in your dataframe."
)
# autoscale histogram bars
if trace_type == 'histogram':
scales = 'free'

# validate scales
if scales not in ['fixed', 'free_x', 'free_y', 'free']:
raise exceptions.PlotlyError(
"'scales' must be set to 'fixed', 'free_x', 'free_y' and 'free'."
)

if trace_type not in ['scatter', 'scattergl']:
if trace_type not in VALID_TRACE_TYPES:
raise exceptions.PlotlyError(
"'trace_type' must be 'scatter' or 'scattergl'."
"'trace_type' must be in {}".format(VALID_TRACE_TYPES)
)

# seperate kwargs for marker and else
Expand Down
7 changes: 2 additions & 5 deletions plotly/tests/test_optional/test_figure_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1950,11 +1950,8 @@ def test_valid_col_selection(self):
def test_valid_trace_type(self):
data = pd.DataFrame([[0, 0], [1, 1]], columns=['a', 'b'])

pattern = "'trace_type' must be 'scatter' or 'scattergl'."

self.assertRaisesRegexp(PlotlyError, pattern,
ff.create_facet_grid,
data, 'a', 'b', trace_type='bar')
self.assertRaises(PlotlyError, ff.create_facet_grid,
data, 'a', 'b', trace_type='foo')

def test_valid_scales(self):
data = pd.DataFrame([[0, 0], [1, 1]], columns=['a', 'b'])
Expand Down