diff --git a/plotly/graph_objs/figure_factory.py b/plotly/graph_objs/figure_factory.py new file mode 100644 index 00000000000..1846f238de5 --- /dev/null +++ b/plotly/graph_objs/figure_factory.py @@ -0,0 +1,2020 @@ +# coding=utf-8 +""" +Helper methods for creating non-standardized Plotly traces/figures. + +""" +import math +from collections import OrderedDict + +from plotly import exceptions, optional_imports +from plotly.graph_objs.graph_objs import GraphObjectFactory + + +# Default colours for finance charts +_DEFAULT_INCREASING_COLOR = '#3D9970' # http://clrs.cc +_DEFAULT_DECREASING_COLOR = '#FF4136' + + +class FigureFactory(object): + """ + BETA functions to create specific chart types. + + This is beta as in: subject to change in a backwards incompatible way + without notice. + + Supported chart types include candlestick, open high low close, quiver, + and streamline. See FigureFactory.create_candlestick, + FigureFactory.create_ohlc, FigureFactory.create_quiver, or + FigureFactory.create_streamline for for more infomation and examples of a + specific chart type. + """ + + @staticmethod + def _validate_equal_length(*args): + """ + Validates that data lists or ndarrays are the same length. + + :raises: (PlotlyError) If any data lists are not the same length. + """ + length = len(args[0]) + if any(len(lst) != length for lst in args): + raise exceptions.PlotlyError("Oops! Your data lists or ndarrays " + "should be the same length.") + + @staticmethod + def _validate_ohlc(open, high, low, close, direction, **kwargs): + """ + ohlc and candlestick specific validations + + Specifically, this checks that the high value is the greatest value and + the low value is the lowest value in each unit. + + See FigureFactory.create_ohlc() or FigureFactory.create_candlestick() + for params + + :raises: (PlotlyError) If the high value is not the greatest value in + each unit. + :raises: (PlotlyError) If the low value is not the lowest value in each + unit. + :raises: (PlotlyError) If direction is not 'increasing' or 'decreasing' + """ + for lst in [open, low, close]: + for index in range(len(high)): + if high[index] < lst[index]: + raise exceptions.PlotlyError("Oops! Looks like some of " + "your high values are less " + "the corresponding open, " + "low, or close values. " + "Double check that your data " + "is entered in O-H-L-C order") + + for lst in [open, high, close]: + for index in range(len(low)): + if low[index] > lst[index]: + raise exceptions.PlotlyError("Oops! Looks like some of " + "your low values are greater " + "than the corresponding high" + ", open, or close values. " + "Double check that your data " + "is entered in O-H-L-C order") + + direction_opts = ('increasing', 'decreasing', 'both') + if direction not in direction_opts: + raise exceptions.PlotlyError("direction must be defined as " + "'increasing', 'decreasing', or " + "'both'") + + @staticmethod + def _validate_distplot(hist_data, curve_type): + """ + distplot specific validations + + :raises: (PlotlyError) If hist_data is not a list of lists + :raises: (PlotlyError) If curve_type is not valid (i.e. not 'kde' or + 'normal'). + """ + pd = optional_imports.get_module('pandas') + np = optional_imports.get_module('numpy') + + hist_data_types = (list,) + if np: + hist_data_types += (np.ndarray,) + if pd: + hist_data_types += (pd.core.series.Series,) + + if not isinstance(hist_data[0], hist_data_types): + raise exceptions.PlotlyError("Oops, this function was written " + "to handle multiple datasets, if " + "you want to plot just one, make " + "sure your hist_data variable is " + "still a list of lists, i.e. x = " + "[1, 2, 3] -> x = [[1, 2, 3]]") + + curve_opts = ('kde', 'normal') + if curve_type not in curve_opts: + raise exceptions.PlotlyError("curve_type must be defined as " + "'kde' or 'normal'") + + msg = 'FigureFactory.create_distplot requires scipy' + optional_imports.get_module('scipy', raise_exc=True, msg=msg) + + @staticmethod + def _validate_positive_scalars(**kwargs): + """ + Validates that all values given in key/val pairs are positive. + + Accepts kwargs to improve Exception messages. + + :raises: (PlotlyError) If any value is < 0 or raises. + """ + for key, val in kwargs.items(): + try: + if val <= 0: + raise ValueError('{} must be > 0, got {}'.format(key, val)) + except TypeError: + raise exceptions.PlotlyError('{} must be a number, got {}' + .format(key, val)) + + @staticmethod + def _validate_streamline(x, y): + """ + streamline specific validations + + Specifically, this checks that x and y are both evenly spaced, + and that the package numpy is available. + + See FigureFactory.create_streamline() for params + + :raises: (ImportError) If numpy is not available. + :raises: (PlotlyError) If x is not evenly spaced. + :raises: (PlotlyError) If y is not evenly spaced. + """ + msg = 'FigureFactory.create_streamline requires numpy' + optional_imports.get_module('numpy', raise_exc=True, msg=msg) + for index in range(len(x) - 1): + if ((x[index + 1] - x[index]) - (x[1] - x[0])) > .0001: + raise exceptions.PlotlyError("x must be a 1 dimensional, " + "evenly spaced array") + for index in range(len(y) - 1): + if ((y[index + 1] - y[index]) - + (y[1] - y[0])) > .0001: + raise exceptions.PlotlyError("y must be a 1 dimensional, " + "evenly spaced array") + + @staticmethod + def _flatten(array): + """ + Uses list comprehension to flatten array + + :param (array): An iterable to flatten + :raises (PlotlyError): If iterable is not nested. + :rtype (list): The flattened list. + """ + try: + return [item for sublist in array for item in sublist] + except TypeError: + raise exceptions.PlotlyError("Your data array could not be " + "flattened! Make sure your data is " + "entered as lists or ndarrays!") + + @staticmethod + def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, + angle=math.pi / 9, **kwargs): + """ + Returns data for a quiver plot. + + :param (list|ndarray) x: x coordinates of the arrow locations + :param (list|ndarray) y: y coordinates of the arrow locations + :param (list|ndarray) u: x components of the arrow vectors + :param (list|ndarray) v: y components of the arrow vectors + :param (float in [0,1]) scale: scales size of the arrows(ideally to + avoid overlap). Default = .1 + :param (float in [0,1]) arrow_scale: value multiplied to length of barb + to get length of arrowhead. Default = .3 + :param (angle in radians) angle: angle of arrowhead. Default = pi/9 + :param kwargs: kwargs passed through plotly.graph_objs.Scatter + for more information on valid kwargs call + help(plotly.graph_objs.Scatter) + + :rtype (dict): returns a representation of quiver figure. + + Example 1: Trivial Quiver + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + import math + + # 1 Arrow from (0,0) to (1,1) + fig = FF.create_quiver(x=[0], y=[0], + u=[1], v=[1], + scale=1) + + py.plot(fig, filename='quiver') + ``` + + Example 2: Quiver plot using meshgrid + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + import numpy as np + import math + + # Add data + x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2)) + u = np.cos(x)*y + v = np.sin(x)*y + + #Create quiver + fig = FF.create_quiver(x, y, u, v) + + # Plot + py.plot(fig, filename='quiver') + ``` + + Example 3: Styling the quiver plot + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + import numpy as np + import math + + # Add data + x, y = np.meshgrid(np.arange(-np.pi, math.pi, .5), + np.arange(-math.pi, math.pi, .5)) + u = np.cos(x)*y + v = np.sin(x)*y + + # Create quiver + fig = FF.create_quiver(x, y, u, v, scale=.2, + arrow_scale=.3, + angle=math.pi/6, + name='Wind Velocity', + line=Line(width=1)) + + # Add title to layout + fig['layout'].update(title='Quiver Plot') + + # Plot + py.plot(fig, filename='quiver') + ``` + """ + FigureFactory._validate_equal_length(x, y, u, v) + FigureFactory._validate_positive_scalars(arrow_scale=arrow_scale, + scale=scale) + + barb_x, barb_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle).get_barbs() + arrow_x, arrow_y = _Quiver(x, y, u, v, scale, + arrow_scale, angle).get_quiver_arrows() + quiver = dict(x=barb_x + arrow_x, y=barb_y + arrow_y, + mode='lines', **kwargs) + + data = [quiver] + layout = dict(hovermode='closest') + + return GraphObjectFactory.create('figure', data=data, layout=layout) + + @staticmethod + def create_streamline(x, y, u, v, + density=1, angle=math.pi / 9, + arrow_scale=.09, **kwargs): + """ + Returns data for a streamline plot. + + :param (list|ndarray) x: 1 dimensional, evenly spaced list or array + :param (list|ndarray) y: 1 dimensional, evenly spaced list or array + :param (ndarray) u: 2 dimensional array + :param (ndarray) v: 2 dimensional array + :param (float|int) density: controls the density of streamlines in + plot. This is multiplied by 30 to scale similiarly to other + available streamline functions such as matplotlib. + Default = 1 + :param (angle in radians) angle: angle of arrowhead. Default = pi/9 + :param (float in [0,1]) arrow_scale: value to scale length of arrowhead + Default = .09 + :param kwargs: kwargs passed through plotly.graph_objs.Scatter + for more information on valid kwargs call + help(plotly.graph_objs.Scatter) + + :rtype (dict): returns a representation of streamline figure. + + Example 1: Plot simple streamline and increase arrow size + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + import numpy as np + import math + + # Add data + x = np.linspace(-3, 3, 100) + y = np.linspace(-3, 3, 100) + Y, X = np.meshgrid(x, y) + u = -1 - X**2 + Y + v = 1 + X - Y**2 + u = u.T # Transpose + v = v.T # Transpose + + # Create streamline + fig = FF.create_streamline(x, y, u, v, + arrow_scale=.1) + + # Plot + py.plot(fig, filename='streamline') + ``` + + Example 2: from nbviewer.ipython.org/github/barbagroup/AeroPython + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + import numpy as np + import math + + # Add data + N = 50 + x_start, x_end = -2.0, 2.0 + y_start, y_end = -1.0, 1.0 + x = np.linspace(x_start, x_end, N) + y = np.linspace(y_start, y_end, N) + X, Y = np.meshgrid(x, y) + ss = 5.0 + x_s, y_s = -1.0, 0.0 + + # Compute the velocity field on the mesh grid + u_s = ss/(2*np.pi) * (X-x_s)/((X-x_s)**2 + (Y-y_s)**2) + v_s = ss/(2*np.pi) * (Y-y_s)/((X-x_s)**2 + (Y-y_s)**2) + + # Create streamline + fig = FF.create_streamline(x, y, u_s, v_s, + density=2, name='streamline') + + # Add source point + point = Scatter(x=[x_s], y=[y_s], mode='markers', + marker=Marker(size=14), name='source point') + + # Plot + fig['data'].append(point) + py.plot(fig, filename='streamline') + ``` + """ + FigureFactory._validate_equal_length(x, y) + FigureFactory._validate_equal_length(u, v) + FigureFactory._validate_streamline(x, y) + FigureFactory._validate_positive_scalars(density=density, + arrow_scale=arrow_scale) + + streamline_x, streamline_y = _Streamline(x, y, u, v, + density, angle, + arrow_scale).sum_streamlines() + arrow_x, arrow_y = _Streamline(x, y, u, v, + density, angle, + arrow_scale).get_streamline_arrows() + + streamline = dict(x=streamline_x + arrow_x, y=streamline_y + arrow_y, + mode='lines', **kwargs) + + data = [streamline] + layout = dict(hovermode='closest') + + return GraphObjectFactory.create('figure', data=data, layout=layout) + + @staticmethod + def _make_increasing_ohlc(open, high, low, close, dates, **kwargs): + """ + Makes increasing ohlc sticks + + _make_increasing_ohlc() and _make_decreasing_ohlc separate the + increasing trace from the decreasing trace so kwargs (such as + color) can be passed separately to increasing or decreasing traces + when direction is set to 'increasing' or 'decreasing' in + FigureFactory.create_candlestick() + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing values + :param (list) dates: list of datetime objects. Default: None + :param kwargs: kwargs to be passed to increasing trace via + plotly.graph_objs.Scatter. + + :rtype (trace) ohlc_incr_data: Scatter trace of all increasing ohlc + sticks. + """ + (flat_increase_x, + flat_increase_y, + text_increase) = _OHLC(open, high, low, close, dates).get_increase() + + if 'name' in kwargs: + showlegend = True + else: + kwargs.setdefault('name', 'Increasing') + showlegend = False + + kwargs.setdefault('line', dict(color=_DEFAULT_INCREASING_COLOR, + width=1)) + kwargs.setdefault('text', text_increase) + + ohlc_incr = dict(type='scatter', + x=flat_increase_x, + y=flat_increase_y, + mode='lines', + showlegend=showlegend, + **kwargs) + return ohlc_incr + + @staticmethod + def _make_decreasing_ohlc(open, high, low, close, dates, **kwargs): + """ + Makes decreasing ohlc sticks + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing values + :param (list) dates: list of datetime objects. Default: None + :param kwargs: kwargs to be passed to increasing trace via + plotly.graph_objs.Scatter. + + :rtype (trace) ohlc_decr_data: Scatter trace of all decreasing ohlc + sticks. + """ + (flat_decrease_x, + flat_decrease_y, + text_decrease) = _OHLC(open, high, low, close, dates).get_decrease() + + kwargs.setdefault('line', dict(color=_DEFAULT_DECREASING_COLOR, + width=1)) + kwargs.setdefault('text', text_decrease) + kwargs.setdefault('showlegend', False) + kwargs.setdefault('name', 'Decreasing') + + ohlc_decr = dict(type='scatter', + x=flat_decrease_x, + y=flat_decrease_y, + mode='lines', + **kwargs) + return ohlc_decr + + @staticmethod + def create_ohlc(open, high, low, close, + dates=None, direction='both', + **kwargs): + """ + BETA function that creates an ohlc chart + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing + :param (list) dates: list of datetime objects. Default: None + :param (string) direction: direction can be 'increasing', 'decreasing', + or 'both'. When the direction is 'increasing', the returned figure + consists of all units where the close value is greater than the + corresponding open value, and when the direction is 'decreasing', + the returned figure consists of all units where the close value is + less than or equal to the corresponding open value. When the + direction is 'both', both increasing and decreasing units are + returned. Default: 'both' + :param kwargs: kwargs passed through plotly.graph_objs.Scatter. + These kwargs describe other attributes about the ohlc Scatter trace + such as the color or the legend name. For more information on valid + kwargs call help(plotly.graph_objs.Scatter) + + :rtype (dict): returns a representation of an ohlc chart figure. + + Example 1: Simple OHLC chart from a Pandas DataFrame + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + from datetime import datetime + + import pandas.io.data as web + + df = web.DataReader("aapl", 'yahoo', datetime(2008, 8, 15), + datetime(2008, 10, 15)) + fig = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, + dates=df.index) + + py.plot(fig, filename='finance/aapl-ohlc') + ``` + + Example 2: Add text and annotations to the OHLC chart + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + from datetime import datetime + + import pandas.io.data as web + + df = web.DataReader("aapl", 'yahoo', datetime(2008, 8, 15), + datetime(2008, 10, 15)) + fig = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, + dates=df.index) + + # Update the fig - See https://plot.ly/python/reference/#Layout + fig['layout'].update({ + 'title': 'The Great Recession', + 'yaxis': {'title': 'AAPL Stock'}, + 'shapes': [{ + 'x0': '2008-09-15', 'x1': '2008-09-15', 'type': 'line', + 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper', + 'line': {'color': 'rgb(40,40,40)', 'width': 0.5} + }], + 'annotations': [{ + 'text': "the fall of Lehman Brothers", + 'x': '2008-09-15', 'y': 1.02, + 'xref': 'x', 'yref': 'paper', + 'showarrow': False, 'xanchor': 'left' + }] + }) + + py.plot(fig, filename='finance/aapl-recession-ohlc', validate=False) + ``` + + Example 3: Customize the OHLC colors + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + from plotly.graph_objs import Line, Marker + from datetime import datetime + + import pandas.io.data as web + + df = web.DataReader("aapl", 'yahoo', datetime(2008, 1, 1), + datetime(2009, 4, 1)) + + # Make increasing ohlc sticks and customize their color and name + fig_increasing = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, + dates=df.index, + direction='increasing', name='AAPL', + line=Line(color='rgb(150, 200, 250)')) + + # Make decreasing ohlc sticks and customize their color and name + fig_decreasing = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, + dates=df.index, + direction='decreasing', + line=Line(color='rgb(128, 128, 128)')) + + # Initialize the figure + fig = fig_increasing + + # Add decreasing data with .extend() + fig['data'].extend(fig_decreasing['data']) + + py.iplot(fig, filename='finance/aapl-ohlc-colors', validate=False) + ``` + + Example 4: OHLC chart with datetime objects + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + from datetime import datetime + + # Add data + open_data = [33.0, 33.3, 33.5, 33.0, 34.1] + high_data = [33.1, 33.3, 33.6, 33.2, 34.8] + low_data = [32.7, 32.7, 32.8, 32.6, 32.8] + close_data = [33.0, 32.9, 33.3, 33.1, 33.1] + dates = [datetime(year=2013, month=10, day=10), + datetime(year=2013, month=11, day=10), + datetime(year=2013, month=12, day=10), + datetime(year=2014, month=1, day=10), + datetime(year=2014, month=2, day=10)] + + # Create ohlc + fig = FF.create_ohlc(open_data, high_data, + low_data, close_data, dates=dates) + + py.iplot(fig, filename='finance/simple-ohlc', validate=False) + ``` + """ + if dates is not None: + FigureFactory._validate_equal_length(open, high, low, close, dates) + else: + FigureFactory._validate_equal_length(open, high, low, close) + FigureFactory._validate_ohlc(open, high, low, close, direction, + **kwargs) + + if direction is 'increasing': + ohlc_incr = FigureFactory._make_increasing_ohlc(open, high, + low, close, + dates, **kwargs) + data = [ohlc_incr] + elif direction is 'decreasing': + ohlc_decr = FigureFactory._make_decreasing_ohlc(open, high, + low, close, + dates, **kwargs) + data = [ohlc_decr] + else: + ohlc_incr = FigureFactory._make_increasing_ohlc(open, high, + low, close, + dates, **kwargs) + ohlc_decr = FigureFactory._make_decreasing_ohlc(open, high, + low, close, + dates, **kwargs) + data = [ohlc_incr, ohlc_decr] + + layout = dict(xaxis=dict(zeroline=False), hovermode='closest') + + return GraphObjectFactory.create('figure', data=data, layout=layout) + + @staticmethod + def _make_increasing_candle(open, high, low, close, dates, **kwargs): + """ + Makes boxplot trace for increasing candlesticks + + _make_increasing_candle() and _make_decreasing_candle separate the + increasing traces from the decreasing traces so kwargs (such as + color) can be passed separately to increasing or decreasing traces + when direction is set to 'increasing' or 'decreasing' in + FigureFactory.create_candlestick() + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing values + :param (list) dates: list of datetime objects. Default: None + :param kwargs: kwargs to be passed to increasing trace via + plotly.graph_objs.Scatter. + + :rtype (list) candle_incr_data: list of the box trace for + increasing candlesticks. + """ + increase_x, increase_y = _Candlestick( + open, high, low, close, dates, **kwargs).get_candle_increase() + + if 'line' in kwargs: + kwargs.setdefault('fillcolor', kwargs['line']['color']) + else: + kwargs.setdefault('fillcolor', _DEFAULT_INCREASING_COLOR) + if 'name' in kwargs: + kwargs.setdefault('showlegend', True) + else: + kwargs.setdefault('showlegend', False) + kwargs.setdefault('name', 'Increasing') + kwargs.setdefault('line', dict(color=_DEFAULT_INCREASING_COLOR)) + + candle_incr_data = dict(type='box', + x=increase_x, + y=increase_y, + whiskerwidth=0, + boxpoints=False, + **kwargs) + + return [candle_incr_data] + + @staticmethod + def _make_decreasing_candle(open, high, low, close, dates, **kwargs): + """ + Makes boxplot trace for decreasing candlesticks + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing values + :param (list) dates: list of datetime objects. Default: None + :param kwargs: kwargs to be passed to decreasing trace via + plotly.graph_objs.Scatter. + + :rtype (list) candle_decr_data: list of the box trace for + decreasing candlesticks. + """ + + decrease_x, decrease_y = _Candlestick( + open, high, low, close, dates, **kwargs).get_candle_decrease() + + if 'line' in kwargs: + kwargs.setdefault('fillcolor', kwargs['line']['color']) + else: + kwargs.setdefault('fillcolor', _DEFAULT_DECREASING_COLOR) + kwargs.setdefault('showlegend', False) + kwargs.setdefault('line', dict(color=_DEFAULT_DECREASING_COLOR)) + kwargs.setdefault('name', 'Decreasing') + + candle_decr_data = dict(type='box', + x=decrease_x, + y=decrease_y, + whiskerwidth=0, + boxpoints=False, + **kwargs) + + return [candle_decr_data] + + @staticmethod + def create_candlestick(open, high, low, close, + dates=None, direction='both', **kwargs): + """ + BETA function that creates a candlestick chart + + :param (list) open: opening values + :param (list) high: high values + :param (list) low: low values + :param (list) close: closing values + :param (list) dates: list of datetime objects. Default: None + :param (string) direction: direction can be 'increasing', 'decreasing', + or 'both'. When the direction is 'increasing', the returned figure + consists of all candlesticks where the close value is greater than + the corresponding open value, and when the direction is + 'decreasing', the returned figure consists of all candlesticks + where the close value is less than or equal to the corresponding + open value. When the direction is 'both', both increasing and + decreasing candlesticks are returned. Default: 'both' + :param kwargs: kwargs passed through plotly.graph_objs.Scatter. + These kwargs describe other attributes about the ohlc Scatter trace + such as the color or the legend name. For more information on valid + kwargs call help(plotly.graph_objs.Scatter) + + :rtype (dict): returns a representation of candlestick chart figure. + + Example 1: Simple candlestick chart from a Pandas DataFrame + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + from datetime import datetime + + import pandas.io.data as web + + df = web.DataReader("aapl", 'yahoo', datetime(2007, 10, 1), + datetime(2009, 4, 1)) + fig = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, + dates=df.index) + py.plot(fig, filename='finance/aapl-candlestick', validate=False) + ``` + + Example 2: Add text and annotations to the candlestick chart + ``` + fig = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, + dates=df.index) + # Update the fig - See https://plot.ly/python/reference/#Layout + fig['layout'].update({ + 'title': 'The Great Recession', + 'yaxis': {'title': 'AAPL Stock'}, + 'shapes': [{ + 'x0': '2007-12-01', 'x1': '2007-12-01', + 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper', + 'line': {'color': 'rgb(30,30,30)', 'width': 1} + }], + 'annotations': [{ + 'x': '2007-12-01', 'y': 0.05, 'xref': 'x', 'yref': 'paper', + 'showarrow': False, 'xanchor': 'left', + 'text': 'Official start of the recession' + }] + }) + py.plot(fig, filename='finance/aapl-recession-candlestick', + validate=False) + ``` + + Example 3: Customize the candlestick colors + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + from plotly.graph_objs import Line, Marker + from datetime import datetime + + import pandas.io.data as web + + df = web.DataReader("aapl", 'yahoo', datetime(2008, 1, 1), + datetime(2009, 4, 1)) + + # Make increasing candlesticks and customize their color and name + fig_increasing = FF.create_candlestick( + df.Open, df.High, df.Low, df.Close, dates=df.index, + direction='increasing', name='AAPL', + marker=Marker(color='rgb(150, 200, 250)'), + line=Line(color='rgb(150, 200, 250)') + ) + + # Make decreasing candlesticks and customize their color and name + fig_decreasing = FF.create_candlestick( + df.Open, df.High, df.Low, df.Close, dates=df.index, + direction='decreasing', marker=Marker(color='rgb(128, 128, 128)'), + line=Line(color='rgb(128, 128, 128)') + ) + + # Initialize the figure + fig = fig_increasing + + # Add decreasing data with .extend() + fig['data'].extend(fig_decreasing['data']) + + py.iplot(fig, filename='finance/aapl-candlestick-custom', + validate=False) + ``` + + Example 4: Candlestick chart with datetime objects + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + from datetime import datetime + + # Add data + open_data = [33.0, 33.3, 33.5, 33.0, 34.1] + high_data = [33.1, 33.3, 33.6, 33.2, 34.8] + low_data = [32.7, 32.7, 32.8, 32.6, 32.8] + close_data = [33.0, 32.9, 33.3, 33.1, 33.1] + dates = [datetime(year=2013, month=10, day=10), + datetime(year=2013, month=11, day=10), + datetime(year=2013, month=12, day=10), + datetime(year=2014, month=1, day=10), + datetime(year=2014, month=2, day=10)] + + # Create ohlc + fig = FF.create_candlestick(open_data, high_data, + low_data, close_data, dates=dates) + + py.iplot(fig, filename='finance/simple-candlestick', validate=False) + ``` + """ + if dates is not None: + FigureFactory._validate_equal_length(open, high, low, close, dates) + else: + FigureFactory._validate_equal_length(open, high, low, close) + FigureFactory._validate_ohlc(open, high, low, close, direction, + **kwargs) + + if direction is 'increasing': + candle_incr_data = FigureFactory._make_increasing_candle( + open, high, low, close, dates, **kwargs) + data = candle_incr_data + elif direction is 'decreasing': + candle_decr_data = FigureFactory._make_decreasing_candle( + open, high, low, close, dates, **kwargs) + data = candle_decr_data + else: + candle_incr_data = FigureFactory._make_increasing_candle( + open, high, low, close, dates, **kwargs) + candle_decr_data = FigureFactory._make_decreasing_candle( + open, high, low, close, dates, **kwargs) + data = candle_incr_data + candle_decr_data + + return GraphObjectFactory.create('figure', data=data, layout={}) + + @staticmethod + def create_distplot(hist_data, group_labels, + bin_size=1., curve_type='kde', + colors=None, rug_text=None, + show_hist=True, show_curve=True, + show_rug=True): + """ + BETA function that creates a distplot similar to seaborn.distplot + + The distplot can be composed of all or any combination of the following + 3 components: (1) histogram, (2) curve: (a) kernal density estimation + or (b) normal curve, and (3) rug plot. Additionally, multiple distplots + (from multiple datasets) can be created in the same plot. + + :param (list[list]) hist_data: Use list of lists to plot multiple data + sets on the same plot. + :param (list[str]) group_labels: Names for each data set. + :param (float) bin_size: Size of histogram bins. Default = 1. + :param (str) curve_type: 'kde' or 'normal'. Default = 'kde' + :param (bool) show_hist: Add histogram to distplot? Default = True + :param (bool) show_curve: Add curve to distplot? Default = True + :param (bool) show_rug: Add rug to distplot? Default = True + :param (list[str]) colors: Colors for traces. + :param (list[list]) rug_text: Hovertext values for rug_plot, + :return (dict): Representation of a distplot figure. + + Example 1: Simple distplot of 1 data set + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + hist_data = [[1.1, 1.1, 2.5, 3.0, 3.5, + 3.5, 4.1, 4.4, 4.5, 4.5, + 5.0, 5.0, 5.2, 5.5, 5.5, + 5.5, 5.5, 5.5, 6.1, 7.0]] + + group_labels = ['distplot example'] + + fig = FF.create_distplot(hist_data, group_labels) + + url = py.plot(fig, filename='Simple distplot', validate=False) + ``` + + Example 2: Two data sets and added rug text + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + # Add histogram data + hist1_x = [0.8, 1.2, 0.2, 0.6, 1.6, + -0.9, -0.07, 1.95, 0.9, -0.2, + -0.5, 0.3, 0.4, -0.37, 0.6] + hist2_x = [0.8, 1.5, 1.5, 0.6, 0.59, + 1.0, 0.8, 1.7, 0.5, 0.8, + -0.3, 1.2, 0.56, 0.3, 2.2] + + # Group data together + hist_data = [hist1_x, hist2_x] + + group_labels = ['2012', '2013'] + + # Add text + rug_text_1 = ['a1', 'b1', 'c1', 'd1', 'e1', + 'f1', 'g1', 'h1', 'i1', 'j1', + 'k1', 'l1', 'm1', 'n1', 'o1'] + + rug_text_2 = ['a2', 'b2', 'c2', 'd2', 'e2', + 'f2', 'g2', 'h2', 'i2', 'j2', + 'k2', 'l2', 'm2', 'n2', 'o2'] + + # Group text together + rug_text_all = [rug_text_1, rug_text_2] + + # Create distplot + fig = FF.create_distplot( + hist_data, group_labels, rug_text=rug_text_all, bin_size=.2) + + # Add title + fig['layout'].update(title='Dist Plot') + + # Plot! + url = py.plot(fig, filename='Distplot with rug text', validate=False) + ``` + + Example 3: Plot with normal curve and hide rug plot + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + import numpy as np + + x1 = np.random.randn(190) + x2 = np.random.randn(200)+1 + x3 = np.random.randn(200)-1 + x4 = np.random.randn(210)+2 + + hist_data = [x1, x2, x3, x4] + group_labels = ['2012', '2013', '2014', '2015'] + + fig = FF.create_distplot( + hist_data, group_labels, curve_type='normal', + show_rug=False, bin_size=.4) + + url = py.plot(fig, filename='hist and normal curve', validate=False) + + Example 4: Distplot with Pandas + ``` + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + import numpy as np + import pandas as pd + + df = pd.DataFrame({'2012': np.random.randn(200), + '2013': np.random.randn(200)+1}) + py.iplot(FF.create_distplot([df[c] for c in df.columns], df.columns), + filename='examples/distplot with pandas', + validate=False) + ``` + """ + colors = colors if colors is not None else [] + rug_text = rug_text if rug_text is not None else [] + FigureFactory._validate_distplot(hist_data, curve_type) + FigureFactory._validate_equal_length(hist_data, group_labels) + + hist = _Distplot( + hist_data, group_labels, bin_size, + curve_type, colors, rug_text, + show_hist, show_curve).make_hist() + + if curve_type == 'normal': + curve = _Distplot( + hist_data, group_labels, bin_size, + curve_type, colors, rug_text, + show_hist, show_curve).make_normal() + else: + curve = _Distplot( + hist_data, group_labels, bin_size, + curve_type, colors, rug_text, + show_hist, show_curve).make_kde() + + rug = _Distplot( + hist_data, group_labels, bin_size, + curve_type, colors, rug_text, + show_hist, show_curve).make_rug() + + data = [] + if show_hist: + data.append(hist) + if show_curve: + data.append(curve) + if show_rug: + data.append(rug) + layout = dict( + barmode='overlay', + hovermode='closest', + legend=dict(traceorder='reversed'), + xaxis1=dict(domain=[0.0, 1.0], + anchor='y2', + zeroline=False), + yaxis1=dict(domain=[0.35, 1], + anchor='free', + position=0.0), + yaxis2=dict(domain=[0, 0.25], + anchor='x1', + dtick=1, + showticklabels=False)) + else: + layout = dict( + barmode='overlay', + hovermode='closest', + legend=dict(traceorder='reversed'), + xaxis1=dict(domain=[0.0, 1.0], + anchor='y2', + zeroline=False), + yaxis1=dict(domain=[0., 1], + anchor='free', + position=0.0)) + + data = sum(data, []) + return GraphObjectFactory.create('figure', data=data, layout=layout) + + @staticmethod + def create_dendrogram(X, orientation="bottom", labels=None, + colorscale=None): + """ + BETA function that returns a dendrogram Plotly figure object. + + :param (ndarray) X: Matrix of observations as array of arrays + :param (str) orientation: 'top', 'right', 'bottom', or 'left' + :param (list) labels: List of axis category labels(observation labels) + :param (list) colorscale: Optional colorscale for dendrogram tree + clusters + + Example 1: Simple bottom oriented dendrogram + ``` + import numpy as np + + import plotly.plotly as py + from plotly.graph_objs.figure_factory import FigureFactory as FF + + X = np.random.rand(5,5) + dendro = FF.create_dendrogram(X) + py.iplot(dendro, validate=False, height=300, width=1000) + + ``` + + Example 2: Dendrogram to put on the left of the heatmap + ``` + X = np.random.rand(5,5) + names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] + dendro = FF.create_dendrogram(X, orientation='right', labels=names) + + py.iplot(dendro, validate=False, height=1000, width=300) + ``` + + """ + msg = ('FigureFactory.create_dendrogram requires scipy, scipy.spatial ' + 'and scipy.hierarchy') + for module in ['scipy', 'scipy.spatial', 'scipy.cluster.hierarchy']: + optional_imports.get_module(module, raise_exc=True, msg=msg) + s = X.shape + if len(s) != 2: + exceptions.PlotlyError("X should be 2-dimensional array.") + + dendrogram = _Dendrogram(X, orientation, labels, colorscale) + + return GraphObjectFactory.create('figure', data=dendrogram.data, + layout=dendrogram.layout) + + +class _Quiver(FigureFactory): + """ + Refer to FigureFactory.create_quiver() for docstring + """ + def __init__(self, x, y, u, v, + scale, arrow_scale, angle, **kwargs): + try: + x = FigureFactory._flatten(x) + except exceptions.PlotlyError: + pass + + try: + y = FigureFactory._flatten(y) + except exceptions.PlotlyError: + pass + + try: + u = FigureFactory._flatten(u) + except exceptions.PlotlyError: + pass + + try: + v = FigureFactory._flatten(v) + except exceptions.PlotlyError: + pass + + self.x = x + self.y = y + self.u = u + self.v = v + self.scale = scale + self.arrow_scale = arrow_scale + self.angle = angle + self.end_x = [] + self.end_y = [] + self.scale_uv() + self.get_barbs() + self.get_quiver_arrows() + + def scale_uv(self): + """ + Scales u and v to avoid overlap of the arrows. + + u and v are added to x and y to get the + endpoints of the arrows so a smaller scale value will + result in less overlap of arrows. + """ + self.u = [i * self.scale for i in self.u] + self.v = [i * self.scale for i in self.v] + + def get_barbs(self): + """ + Creates x and y startpoint and endpoint pairs + + After finding the endpoint of each barb this zips startpoint and + endpoint pairs to create 2 lists: x_values for barbs and y values + for barbs + + :rtype: (list, list) barb_x, barb_y: list of startpoint and endpoint + x_value pairs separated by a None to create the barb of the arrow, + and list of startpoint and endpoint y_value pairs separated by a + None to create the barb of the arrow. + """ + self.end_x = [i + j for i, j in zip(self.x, self.u)] + self.end_y = [i + j for i, j in zip(self.y, self.v)] + empty = [None] * len(self.x) + barb_x = FigureFactory._flatten(zip(self.x, self.end_x, empty)) + barb_y = FigureFactory._flatten(zip(self.y, self.end_y, empty)) + return barb_x, barb_y + + def get_quiver_arrows(self): + """ + Creates lists of x and y values to plot the arrows + + Gets length of each barb then calculates the length of each side of + the arrow. Gets angle of barb and applies angle to each side of the + arrowhead. Next uses arrow_scale to scale the length of arrowhead and + creates x and y values for arrowhead point1 and point2. Finally x and y + values for point1, endpoint and point2s for each arrowhead are + separated by a None and zipped to create lists of x and y values for + the arrows. + + :rtype: (list, list) arrow_x, arrow_y: list of point1, endpoint, point2 + x_values separated by a None to create the arrowhead and list of + point1, endpoint, point2 y_values separated by a None to create + the barb of the arrow. + """ + dif_x = [i - j for i, j in zip(self.end_x, self.x)] + dif_y = [i - j for i, j in zip(self.end_y, self.y)] + + # Get barb lengths(default arrow length = 30% barb length) + barb_len = [None] * len(self.x) + for index in range(len(barb_len)): + barb_len[index] = math.hypot(dif_x[index], dif_y[index]) + + # Make arrow lengths + arrow_len = [i * self.arrow_scale for i in barb_len] + + # Get barb angles + barb_ang = [None] * len(self.x) + for index in range(len(barb_ang)): + barb_ang[index] = math.atan2(dif_y[index], dif_x[index]) + + # Set angles to create arrow + ang1 = [i + self.angle for i in barb_ang] + ang2 = [i - self.angle for i in barb_ang] + + cos_ang1 = [None] * len(ang1) + for index in range(len(ang1)): + cos_ang1[index] = math.cos(ang1[index]) + seg1_x = [i * j for i, j in zip(arrow_len, cos_ang1)] + + sin_ang1 = [None] * len(ang1) + for index in range(len(ang1)): + sin_ang1[index] = math.sin(ang1[index]) + seg1_y = [i * j for i, j in zip(arrow_len, sin_ang1)] + + cos_ang2 = [None] * len(ang2) + for index in range(len(ang2)): + cos_ang2[index] = math.cos(ang2[index]) + seg2_x = [i * j for i, j in zip(arrow_len, cos_ang2)] + + sin_ang2 = [None] * len(ang2) + for index in range(len(ang2)): + sin_ang2[index] = math.sin(ang2[index]) + seg2_y = [i * j for i, j in zip(arrow_len, sin_ang2)] + + # Set coordinates to create arrow + for index in range(len(self.end_x)): + point1_x = [i - j for i, j in zip(self.end_x, seg1_x)] + point1_y = [i - j for i, j in zip(self.end_y, seg1_y)] + point2_x = [i - j for i, j in zip(self.end_x, seg2_x)] + point2_y = [i - j for i, j in zip(self.end_y, seg2_y)] + + # Combine lists to create arrow + empty = [None] * len(self.end_x) + arrow_x = FigureFactory._flatten(zip(point1_x, self.end_x, + point2_x, empty)) + arrow_y = FigureFactory._flatten(zip(point1_y, self.end_y, + point2_y, empty)) + return arrow_x, arrow_y + + +class _Streamline(FigureFactory): + """ + Refer to FigureFactory.create_streamline() for docstring + """ + def __init__(self, x, y, u, v, + density, angle, + arrow_scale, **kwargs): + np = optional_imports.get_module('numpy', raise_exc=True) + self.x = np.array(x) + self.y = np.array(y) + self.u = np.array(u) + self.v = np.array(v) + self.angle = angle + self.arrow_scale = arrow_scale + self.density = int(30 * density) # Scale similarly to other functions + self.delta_x = self.x[1] - self.x[0] + self.delta_y = self.y[1] - self.y[0] + self.val_x = self.x + self.val_y = self.y + + # Set up spacing + self.blank = np.zeros((self.density, self.density)) + self.spacing_x = len(self.x) / float(self.density - 1) + self.spacing_y = len(self.y) / float(self.density - 1) + self.trajectories = [] + + # Rescale speed onto axes-coordinates + self.u = self.u / (self.x[-1] - self.x[0]) + self.v = self.v / (self.y[-1] - self.y[0]) + self.speed = np.sqrt(self.u ** 2 + self.v ** 2) + + # Rescale u and v for integrations. + self.u *= len(self.x) + self.v *= len(self.y) + self.st_x = [] + self.st_y = [] + self.get_streamlines() + self.sum_streamlines() + self.get_streamline_arrows() + + def blank_pos(self, xi, yi): + """ + Set up positions for trajectories to be used with rk4 function. + """ + return (int((xi / self.spacing_x) + 0.5), + int((yi / self.spacing_y) + 0.5)) + + def value_at(self, a, xi, yi): + """ + Set up for RK4 function, based on Bokeh's streamline code + """ + np = optional_imports.get_module('numpy', raise_exc=True) + if isinstance(xi, np.ndarray): + self.x = xi.astype(np.int) + self.y = yi.astype(np.int) + else: + self.val_x = np.int(xi) + self.val_y = np.int(yi) + a00 = a[self.val_y, self.val_x] + a01 = a[self.val_y, self.val_x + 1] + a10 = a[self.val_y + 1, self.val_x] + a11 = a[self.val_y + 1, self.val_x + 1] + xt = xi - self.val_x + yt = yi - self.val_y + a0 = a00 * (1 - xt) + a01 * xt + a1 = a10 * (1 - xt) + a11 * xt + return a0 * (1 - yt) + a1 * yt + + def rk4_integrate(self, x0, y0): + """ + RK4 forward and back trajectories from the initial conditions. + + Adapted from Bokeh's streamline -uses Runge-Kutta method to fill + x and y trajectories then checks length of traj (s in units of axes) + """ + def f(xi, yi): + dt_ds = 1. / self.value_at(self.speed, xi, yi) + ui = self.value_at(self.u, xi, yi) + vi = self.value_at(self.v, xi, yi) + return ui * dt_ds, vi * dt_ds + + def g(xi, yi): + dt_ds = 1. / self.value_at(self.speed, xi, yi) + ui = self.value_at(self.u, xi, yi) + vi = self.value_at(self.v, xi, yi) + return -ui * dt_ds, -vi * dt_ds + + def check(xi, yi): + return 0 <= xi < len(self.x) - 1 and 0 <= yi < len(self.y) - 1 + + xb_changes = [] + yb_changes = [] + + def rk4(x0, y0, f): + ds = 0.01 + stotal = 0 + xi = x0 + yi = y0 + xb, yb = self.blank_pos(xi, yi) + xf_traj = [] + yf_traj = [] + while check(xi, yi): + xf_traj.append(xi) + yf_traj.append(yi) + try: + k1x, k1y = f(xi, yi) + k2x, k2y = f(xi + .5 * ds * k1x, yi + .5 * ds * k1y) + k3x, k3y = f(xi + .5 * ds * k2x, yi + .5 * ds * k2y) + k4x, k4y = f(xi + ds * k3x, yi + ds * k3y) + except IndexError: + break + xi += ds * (k1x + 2 * k2x + 2 * k3x + k4x) / 6. + yi += ds * (k1y + 2 * k2y + 2 * k3y + k4y) / 6. + if not check(xi, yi): + break + stotal += ds + new_xb, new_yb = self.blank_pos(xi, yi) + if new_xb != xb or new_yb != yb: + if self.blank[new_yb, new_xb] == 0: + self.blank[new_yb, new_xb] = 1 + xb_changes.append(new_xb) + yb_changes.append(new_yb) + xb = new_xb + yb = new_yb + else: + break + if stotal > 2: + break + return stotal, xf_traj, yf_traj + + sf, xf_traj, yf_traj = rk4(x0, y0, f) + sb, xb_traj, yb_traj = rk4(x0, y0, g) + stotal = sf + sb + x_traj = xb_traj[::-1] + xf_traj[1:] + y_traj = yb_traj[::-1] + yf_traj[1:] + + if len(x_traj) < 1: + return None + if stotal > .2: + initxb, inityb = self.blank_pos(x0, y0) + self.blank[inityb, initxb] = 1 + return x_traj, y_traj + else: + for xb, yb in zip(xb_changes, yb_changes): + self.blank[yb, xb] = 0 + return None + + def traj(self, xb, yb): + """ + Integrate trajectories + + :param (int) xb: results of passing xi through self.blank_pos + :param (int) yb: results of passing yi through self.blank_pos + + Calculate each trajectory based on rk4 integrate method. + """ + + if xb < 0 or xb >= self.density or yb < 0 or yb >= self.density: + return + if self.blank[yb, xb] == 0: + t = self.rk4_integrate(xb * self.spacing_x, yb * self.spacing_y) + if t is not None: + self.trajectories.append(t) + + def get_streamlines(self): + """ + Get streamlines by building trajectory set. + """ + np = optional_imports.get_module('numpy', raise_exc=True) + for indent in range(self.density // 2): + for xi in range(self.density - 2 * indent): + self.traj(xi + indent, indent) + self.traj(xi + indent, self.density - 1 - indent) + self.traj(indent, xi + indent) + self.traj(self.density - 1 - indent, xi + indent) + + self.st_x = [np.array(t[0]) * self.delta_x + self.x[0] for t in + self.trajectories] + self.st_y = [np.array(t[1]) * self.delta_y + self.y[0] for t in + self.trajectories] + + for index in range(len(self.st_x)): + self.st_x[index] = self.st_x[index].tolist() + self.st_x[index].append(np.nan) + + for index in range(len(self.st_y)): + self.st_y[index] = self.st_y[index].tolist() + self.st_y[index].append(np.nan) + + def get_streamline_arrows(self): + """ + Makes an arrow for each streamline. + + Gets angle of streamline at 1/3 mark and creates arrow coordinates + based off of user defined angle and arrow_scale. + + :rtype (list, list) arrows_x: x-values to create arrowhead and + arrows_y: y-values to create arrowhead + """ + np = optional_imports.get_module('numpy', raise_exc=True) + arrow_end_x = np.empty((len(self.st_x))) + arrow_end_y = np.empty((len(self.st_y))) + arrow_start_x = np.empty((len(self.st_x))) + arrow_start_y = np.empty((len(self.st_y))) + for index in range(len(self.st_x)): + arrow_end_x[index] = (self.st_x[index] + [int(len(self.st_x[index]) / 3)]) + arrow_start_x[index] = (self.st_x[index] + [(int(len(self.st_x[index]) / 3)) - 1]) + arrow_end_y[index] = (self.st_y[index] + [int(len(self.st_y[index]) / 3)]) + arrow_start_y[index] = (self.st_y[index] + [(int(len(self.st_y[index]) / 3)) - 1]) + + dif_x = arrow_end_x - arrow_start_x + dif_y = arrow_end_y - arrow_start_y + + streamline_ang = np.arctan(dif_y / dif_x) + + ang1 = streamline_ang + self.angle + ang2 = streamline_ang - self.angle + + seg1_x = np.cos(ang1) * self.arrow_scale + seg1_y = np.sin(ang1) * self.arrow_scale + seg2_x = np.cos(ang2) * self.arrow_scale + seg2_y = np.sin(ang2) * self.arrow_scale + + point1_x = np.empty((len(dif_x))) + point1_y = np.empty((len(dif_y))) + point2_x = np.empty((len(dif_x))) + point2_y = np.empty((len(dif_y))) + + for index in range(len(dif_x)): + if dif_x[index] >= 0: + point1_x[index] = arrow_end_x[index] - seg1_x[index] + point1_y[index] = arrow_end_y[index] - seg1_y[index] + point2_x[index] = arrow_end_x[index] - seg2_x[index] + point2_y[index] = arrow_end_y[index] - seg2_y[index] + else: + point1_x[index] = arrow_end_x[index] + seg1_x[index] + point1_y[index] = arrow_end_y[index] + seg1_y[index] + point2_x[index] = arrow_end_x[index] + seg2_x[index] + point2_y[index] = arrow_end_y[index] + seg2_y[index] + + space = np.empty((len(point1_x))) + space[:] = np.nan + + # Combine arrays into matrix + arrows_x = np.matrix([point1_x, arrow_end_x, point2_x, space]) + arrows_x = np.array(arrows_x) + arrows_x = arrows_x.flatten('F') + arrows_x = arrows_x.tolist() + + # Combine arrays into matrix + arrows_y = np.matrix([point1_y, arrow_end_y, point2_y, space]) + arrows_y = np.array(arrows_y) + arrows_y = arrows_y.flatten('F') + arrows_y = arrows_y.tolist() + + return arrows_x, arrows_y + + def sum_streamlines(self): + """ + Makes all streamlines readable as a single trace. + + :rtype (list, list): streamline_x: all x values for each streamline + combined into single list and streamline_y: all y values for each + streamline combined into single list + """ + streamline_x = sum(self.st_x, []) + streamline_y = sum(self.st_y, []) + return streamline_x, streamline_y + + +class _OHLC(FigureFactory): + """ + Refer to FigureFactory.create_ohlc_increase() for docstring. + """ + def __init__(self, open, high, low, close, dates, **kwargs): + self.open = open + self.high = high + self.low = low + self.close = close + self.empty = [None] * len(open) + self.dates = dates + + self.all_x = [] + self.all_y = [] + self.increase_x = [] + self.increase_y = [] + self.decrease_x = [] + self.decrease_y = [] + self.get_all_xy() + self.separate_increase_decrease() + + def get_all_xy(self): + """ + Zip data to create OHLC shape + + OHLC shape: low to high vertical bar with + horizontal branches for open and close values. + If dates were added, the smallest date difference is calculated and + multiplied by .2 to get the length of the open and close branches. + If no date data was provided, the x-axis is a list of integers and the + length of the open and close branches is .2. + """ + self.all_y = list(zip(self.open, self.open, self.high, + self.low, self.close, self.close, self.empty)) + if self.dates is not None: + date_dif = [] + for i in range(len(self.dates) - 1): + date_dif.append(self.dates[i + 1] - self.dates[i]) + date_dif_min = (min(date_dif)) / 5 + self.all_x = [[x - date_dif_min, x, x, x, x, x + + date_dif_min, None] for x in self.dates] + else: + self.all_x = [[x - .2, x, x, x, x, x + .2, None] + for x in range(len(self.open))] + + def separate_increase_decrease(self): + """ + Separate data into two groups: increase and decrease + + (1) Increase, where close > open and + (2) Decrease, where close <= open + """ + for index in range(len(self.open)): + if self.close[index] is None: + pass + elif self.close[index] > self.open[index]: + self.increase_x.append(self.all_x[index]) + self.increase_y.append(self.all_y[index]) + else: + self.decrease_x.append(self.all_x[index]) + self.decrease_y.append(self.all_y[index]) + + def get_increase(self): + """ + Flatten increase data and get increase text + + :rtype (list, list, list): flat_increase_x: x-values for the increasing + trace, flat_increase_y: y=values for the increasing trace and + text_increase: hovertext for the increasing trace + """ + flat_increase_x = FigureFactory._flatten(self.increase_x) + flat_increase_y = FigureFactory._flatten(self.increase_y) + text_increase = (("Open", "Open", "High", + "Low", "Close", "Close", '') * + (len(self.increase_x))) + + return flat_increase_x, flat_increase_y, text_increase + + def get_decrease(self): + """ + Flatten decrease data and get decrease text + + :rtype (list, list, list): flat_decrease_x: x-values for the decreasing + trace, flat_decrease_y: y=values for the decreasing trace and + text_decrease: hovertext for the decreasing trace + """ + flat_decrease_x = FigureFactory._flatten(self.decrease_x) + flat_decrease_y = FigureFactory._flatten(self.decrease_y) + text_decrease = (("Open", "Open", "High", + "Low", "Close", "Close", '') * + (len(self.decrease_x))) + + return flat_decrease_x, flat_decrease_y, text_decrease + + +class _Candlestick(FigureFactory): + """ + Refer to FigureFactory.create_candlestick() for docstring. + """ + def __init__(self, open, high, low, close, dates, **kwargs): + self.open = open + self.high = high + self.low = low + self.close = close + if dates is not None: + self.x = dates + else: + self.x = [x for x in range(len(self.open))] + self.get_candle_increase() + + def get_candle_increase(self): + """ + Separate increasing data from decreasing data. + + The data is increasing when close value > open value + and decreasing when the close value <= open value. + """ + increase_y = [] + increase_x = [] + for index in range(len(self.open)): + if self.close[index] > self.open[index]: + increase_y.append(self.low[index]) + increase_y.append(self.open[index]) + increase_y.append(self.close[index]) + increase_y.append(self.close[index]) + increase_y.append(self.close[index]) + increase_y.append(self.high[index]) + increase_x.append(self.x[index]) + + increase_x = [[x, x, x, x, x, x] for x in increase_x] + increase_x = FigureFactory._flatten(increase_x) + + return increase_x, increase_y + + def get_candle_decrease(self): + """ + Separate increasing data from decreasing data. + + The data is increasing when close value > open value + and decreasing when the close value <= open value. + """ + decrease_y = [] + decrease_x = [] + for index in range(len(self.open)): + if self.close[index] <= self.open[index]: + decrease_y.append(self.low[index]) + decrease_y.append(self.open[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.high[index]) + decrease_x.append(self.x[index]) + + decrease_x = [[x, x, x, x, x, x] for x in decrease_x] + decrease_x = FigureFactory._flatten(decrease_x) + + return decrease_x, decrease_y + + +class _Distplot(FigureFactory): + """ + Refer to TraceFactory.create_distplot() for docstring + """ + def __init__(self, hist_data, group_labels, + bin_size, curve_type, colors, + rug_text, show_hist, show_curve): + self.hist_data = hist_data + self.group_labels = group_labels + self.bin_size = bin_size + self.show_hist = show_hist + self.show_curve = show_curve + self.trace_number = len(hist_data) + if rug_text: + self.rug_text = rug_text + else: + self.rug_text = [None] * self.trace_number + + self.start = [] + self.end = [] + if colors: + self.colors = colors + else: + self.colors = [ + "rgb(31, 119, 180)", "rgb(255, 127, 14)", + "rgb(44, 160, 44)", "rgb(214, 39, 40)", + "rgb(148, 103, 189)", "rgb(140, 86, 75)", + "rgb(227, 119, 194)", "rgb(127, 127, 127)", + "rgb(188, 189, 34)", "rgb(23, 190, 207)"] + self.curve_x = [None] * self.trace_number + self.curve_y = [None] * self.trace_number + + for trace in self.hist_data: + self.start.append(min(trace) * 1.) + self.end.append(max(trace) * 1.) + + def make_hist(self): + """ + Makes the histogram(s) for FigureFactory.create_distplot(). + + :rtype (list) hist: list of histogram representations + """ + hist = [None] * self.trace_number + + for index in range(self.trace_number): + hist[index] = dict(type='histogram', + x=self.hist_data[index], + xaxis='x1', + yaxis='y1', + histnorm='probability', + name=self.group_labels[index], + legendgroup=self.group_labels[index], + marker=dict(color=self.colors[index]), + autobinx=False, + xbins=dict(start=self.start[index], + end=self.end[index], + size=self.bin_size), + opacity=.7) + return hist + + def make_kde(self): + """ + Makes the kernal density estimation(s) for create_distplot(). + + This is called when curve_type = 'kde' in create_distplot(). + + :rtype (list) curve: list of kde representations + """ + scipy__stats = optional_imports.get_module('scipy.stats', + raise_exc=True) + curve = [None] * self.trace_number + for index in range(self.trace_number): + self.curve_x[index] = [self.start[index] + + x * (self.end[index] - self.start[index]) / + 500 for x in range(500)] + self.curve_y[index] = (scipy__stats.gaussian_kde + (self.hist_data[index]) + (self.curve_x[index])) + self.curve_y[index] *= self.bin_size + + for index in range(self.trace_number): + curve[index] = dict(type='scatter', + x=self.curve_x[index], + y=self.curve_y[index], + xaxis='x1', + yaxis='y1', + mode='lines', + name=self.group_labels[index], + legendgroup=self.group_labels[index], + showlegend=False if self.show_hist else True, + marker=dict(color=self.colors[index])) + return curve + + def make_normal(self): + """ + Makes the normal curve(s) for create_distplot(). + + This is called when curve_type = 'normal' in create_distplot(). + + :rtype (list) curve: list of normal curve representations + """ + scipy = optional_imports.get_module('scipy', raise_exc=True) + curve = [None] * self.trace_number + mean = [None] * self.trace_number + sd = [None] * self.trace_number + + for index in range(self.trace_number): + mean[index], sd[index] = (scipy.stats.norm.fit + (self.hist_data[index])) + self.curve_x[index] = [self.start[index] + + x * (self.end[index] - self.start[index]) / + 500 for x in range(500)] + self.curve_y[index] = scipy.stats.norm.pdf( + self.curve_x[index], loc=mean[index], scale=sd[index]) + self.curve_y[index] *= self.bin_size + + for index in range(self.trace_number): + curve[index] = dict(type='scatter', + x=self.curve_x[index], + y=self.curve_y[index], + xaxis='x1', + yaxis='y1', + mode='lines', + name=self.group_labels[index], + legendgroup=self.group_labels[index], + showlegend=False if self.show_hist else True, + marker=dict(color=self.colors[index])) + return curve + + def make_rug(self): + """ + Makes the rug plot(s) for create_distplot(). + + :rtype (list) rug: list of rug plot representations + """ + rug = [None] * self.trace_number + for index in range(self.trace_number): + + rug[index] = dict(type='scatter', + x=self.hist_data[index], + y=([self.group_labels[index]] * + len(self.hist_data[index])), + xaxis='x1', + yaxis='y2', + mode='markers', + name=self.group_labels[index], + legendgroup=self.group_labels[index], + showlegend=(False if self.show_hist or + self.show_curve else True), + text=self.rug_text[index], + marker=dict(color=self.colors[index], + symbol='line-ns-open')) + return rug + + +class _Dendrogram(FigureFactory): + """Refer to FigureFactory.create_dendrogram() for docstring.""" + + def __init__(self, X, orientation='bottom', labels=None, colorscale=None, + width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): + self.orientation = orientation + self.labels = labels + self.xaxis = xaxis + self.yaxis = yaxis + self.data = [] + self.leaves = [] + self.sign = {self.xaxis: 1, self.yaxis: 1} + self.layout = {self.xaxis: {}, self.yaxis: {}} + + if self.orientation in ['left', 'bottom']: + self.sign[self.xaxis] = 1 + else: + self.sign[self.xaxis] = -1 + + if self.orientation in ['right', 'bottom']: + self.sign[self.yaxis] = 1 + else: + self.sign[self.yaxis] = -1 + + (dd_traces, xvals, yvals, + ordered_labels, leaves) = self.get_dendrogram_traces(X, colorscale) + + self.labels = ordered_labels + self.leaves = leaves + yvals_flat = yvals.flatten() + xvals_flat = xvals.flatten() + + self.zero_vals = [] + + for i in range(len(yvals_flat)): + if yvals_flat[i] == 0.0 and xvals_flat[i] not in self.zero_vals: + self.zero_vals.append(xvals_flat[i]) + + self.zero_vals.sort() + + self.layout = self.set_figure_layout(width, height) + self.data = list(dd_traces) + + def get_color_dict(self, colorscale): + """ + Returns colorscale used for dendrogram tree clusters. + + :param (list) colorscale: Colors to use for the plot in rgb format. + :rtype (dict): A dict of default colors mapped to the user colorscale. + + """ + + # These are the color codes returned for dendrograms + # We're replacing them with nicer colors + d = {'r': 'red', + 'g': 'green', + 'b': 'blue', + 'c': 'cyan', + 'm': 'magenta', + 'y': 'yellow', + 'k': 'black', + 'w': 'white'} + default_colors = OrderedDict(sorted(d.items(), key=lambda t: t[0])) + + if colorscale is None: + colorscale = [ + 'rgb(0,116,217)', # blue + 'rgb(35,205,205)', # cyan + 'rgb(61,153,112)', # green + 'rgb(40,35,35)', # black + 'rgb(133,20,75)', # magenta + 'rgb(255,65,54)', # red + 'rgb(255,255,255)', # white + 'rgb(255,220,0)'] # yellow + + for i in range(len(default_colors.keys())): + k = list(default_colors.keys())[i] # PY3 won't index keys + if i < len(colorscale): + default_colors[k] = colorscale[i] + + return default_colors + + def set_axis_layout(self, axis_key): + """ + Sets and returns default axis object for dendrogram figure. + + :param (str) axis_key: E.g., 'xaxis', 'xaxis1', 'yaxis', yaxis1', etc. + :rtype (dict): An axis_key dictionary with set parameters. + + """ + axis_defaults = { + 'type': 'linear', + 'ticks': 'outside', + 'mirror': 'allticks', + 'rangemode': 'tozero', + 'showticklabels': True, + 'zeroline': False, + 'showgrid': False, + 'showline': True, + } + + if len(self.labels) != 0: + axis_key_labels = self.xaxis + if self.orientation in ['left', 'right']: + axis_key_labels = self.yaxis + if axis_key_labels not in self.layout: + self.layout[axis_key_labels] = {} + self.layout[axis_key_labels]['tickvals'] = \ + [zv*self.sign[axis_key] for zv in self.zero_vals] + self.layout[axis_key_labels]['ticktext'] = self.labels + self.layout[axis_key_labels]['tickmode'] = 'array' + + self.layout[axis_key].update(axis_defaults) + + return self.layout[axis_key] + + def set_figure_layout(self, width, height): + """ + Sets and returns default layout object for dendrogram figure. + + """ + self.layout.update({ + 'showlegend': False, + 'autosize': False, + 'hovermode': 'closest', + 'width': width, + 'height': height + }) + + self.set_axis_layout(self.xaxis) + self.set_axis_layout(self.yaxis) + + return self.layout + + def get_dendrogram_traces(self, X, colorscale): + """ + Calculates all the elements needed for plotting a dendrogram. + + :param (ndarray) X: Matrix of observations as array of arrays + :param (list) colorscale: Color scale for dendrogram tree clusters + :rtype (tuple): Contains all the traces in the following order: + (a) trace_list: List of Plotly trace objects for dendrogram tree + (b) icoord: All X points of the dendrogram tree as array of arrays + with length 4 + (c) dcoord: All Y points of the dendrogram tree as array of arrays + with length 4 + (d) ordered_labels: leaf labels in the order they are going to + appear on the plot + (e) P['leaves']: left-to-right traversal of the leaves + + """ + np = optional_imports.get_module('numpy', raise_exc=True) + scp = optional_imports.get_module('scipy', raise_exc=True) + scs = optional_imports.get_module('scipy.spatial', raise_exc=True) + sch = optional_imports.get_module('scipy.cluster.hierarchy', + raise_exc=True) + d = scs.distance.pdist(X) + Z = sch.linkage(d, method='complete') + P = sch.dendrogram(Z, orientation=self.orientation, + labels=self.labels, no_plot=True) + + icoord = scp.array(P['icoord']) + dcoord = scp.array(P['dcoord']) + ordered_labels = scp.array(P['ivl']) + color_list = scp.array(P['color_list']) + colors = self.get_color_dict(colorscale) + + trace_list = [] + for i in range(len(icoord)): + # xs and ys are arrays of 4 points that make up the '∩' shapes + # of the dendrogram tree + if self.orientation in ['top', 'bottom']: + xs = icoord[i] + else: + xs = dcoord[i] + + if self.orientation in ['top', 'bottom']: + ys = dcoord[i] + else: + ys = icoord[i] + color_key = color_list[i] + trace = dict( + x=np.multiply(self.sign[self.xaxis], xs), + y=np.multiply(self.sign[self.yaxis], ys), + mode='lines', + marker=dict(color=colors[color_key]) + ) + + try: + x_index = int(self.xaxis[-1]) + except ValueError: + x_index = '' + + try: + y_index = int(self.yaxis[-1]) + except ValueError: + y_index = '' + + trace['xaxis'] = 'x' + x_index + trace['yaxis'] = 'y' + y_index + + trace_list.append(trace) + + return trace_list, icoord, dcoord, ordered_labels, P['leaves'] diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py index 021ef7e80da..005bdfc1b66 100644 --- a/plotly/offline/offline.py +++ b/plotly/offline/offline.py @@ -11,7 +11,7 @@ import requests -from plotly import session, tools, utils +from plotly import session, tools, utils, optional_imports from plotly.exceptions import PlotlyError PLOTLY_OFFLINE_DIRECTORY = plotlyjs_path = os.path.expanduser( @@ -51,9 +51,9 @@ def init_notebook_mode(): to load the necessary javascript files for creating Plotly graphs with plotly.offline.iplot. """ - if not tools._ipython_imported: - raise ImportError('`iplot` can only run inside an IPython Notebook.') - from IPython.display import HTML, display + msg = '`iplot` can only run inside an IPython Notebook.' + IPython__display = optional_imports.get_module('IPython.display', + raise_exc=True, msg=msg) if not os.path.exists(PLOTLY_OFFLINE_BUNDLE): raise PlotlyError('Plotly Offline source file at {source_path} ' @@ -68,8 +68,10 @@ def init_notebook_mode(): global __PLOTLY_OFFLINE_INITIALIZED __PLOTLY_OFFLINE_INITIALIZED = True - display(HTML('')) + IPython__display.display(IPython__display.HTML( + '' + )) def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly'): @@ -108,10 +110,10 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly'): 'plotly.offline.init_notebook_mode() ' '# run at the start of every ipython notebook', ])) - if not tools._ipython_imported: - raise ImportError('`iplot` can only run inside an IPython Notebook.') + msg = '`iplot` can only run inside an IPython Notebook.' + IPython__display = optional_imports.get_module('IPython.display', + raise_exc=True, msg=msg) - from IPython.display import HTML, display if isinstance(figure_or_data, dict): data = figure_or_data['data'] layout = figure_or_data.get('layout', {}) @@ -152,7 +154,7 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly'): .replace('http://', '') link_text = link_text.replace('plot.ly', link_domain) - display(HTML( + IPython__display.display(IPython__display.HTML( '' - ''.format(id=plotdivid, script=script, - height=height, width=width))) + IPython__display.display(IPython__display.HTML( + '
' + 'Drawing...
' + '
' + '
' + '' + ''.format(id=plotdivid, script=script, height=height, width=width) + )) def plot(): diff --git a/plotly/optional_imports.py b/plotly/optional_imports.py new file mode 100644 index 00000000000..6ec2f486895 --- /dev/null +++ b/plotly/optional_imports.py @@ -0,0 +1,41 @@ +""" +Stand-alone module to provide information about whether optional deps exist. + +""" +from __future__ import absolute_import + +import importlib + +__all__ = ['get_module'] + +import_cache = {} + + +def get_module(name, raise_exc=False, msg=None): + """ + Return the module if it's available. Raise if desired. + + A wrapper around importlib.import_module that only allows absolute import. + + :param (str) name: Dot-separated module path. E.g., 'scipy.stats'. + :param (bool) raise_exc: Raise ImportError if not found? + :param (str|None) msg: Optional message to raise with ImportError. + :raises: (ImportError) If `raise_exception` is True, this can happen. + :return: (module|None) If import succeeds, the module will be returned. + + """ + if name in import_cache: + return import_cache[name] + + try: + module = importlib.import_module(name) # only allow absolute imports + except ImportError: + if raise_exc: + if msg is None: + raise # just raise the current import error + else: + raise ImportError(msg) + else: + import_cache[name] = module + + return import_cache.get(name, None) # Don't store the None! diff --git a/plotly/tests/test_core/test_tools/test_figure_factory.py b/plotly/tests/test_core/test_tools/test_figure_factory.py index 3616eb1e9cf..0c244eeb57b 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -3,6 +3,7 @@ import datetime from nose.tools import raises +import plotly.graph_objs.figure_factory import plotly.tools as tls from plotly.exceptions import PlotlyError from plotly.graph_objs import graph_objs @@ -15,7 +16,7 @@ def test_unequal_xy_length(self): # check: PlotlyError if x and y are not the same length kwargs = {'x': [1, 2], 'y': [1], 'u': [1, 2], 'v': [1, 2]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_quiver, + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_quiver, **kwargs) def test_wrong_scale(self): @@ -25,13 +26,13 @@ def test_wrong_scale(self): kwargs = {'x': [1, 2], 'y': [1, 2], 'u': [1, 2], 'v': [1, 2], 'scale': -1} - self.assertRaises(ValueError, tls.FigureFactory.create_quiver, + self.assertRaises(ValueError, plotly.graph_objs.figure_factory.FigureFactory.create_quiver, **kwargs) kwargs = {'x': [1, 2], 'y': [1, 2], 'u': [1, 2], 'v': [1, 2], 'scale': 0} - self.assertRaises(ValueError, tls.FigureFactory.create_quiver, + self.assertRaises(ValueError, plotly.graph_objs.figure_factory.FigureFactory.create_quiver, **kwargs) def test_wrong_arrow_scale(self): @@ -41,20 +42,20 @@ def test_wrong_arrow_scale(self): kwargs = {'x': [1, 2], 'y': [1, 2], 'u': [1, 2], 'v': [1, 2], 'arrow_scale': -1} - self.assertRaises(ValueError, tls.FigureFactory.create_quiver, + self.assertRaises(ValueError, plotly.graph_objs.figure_factory.FigureFactory.create_quiver, **kwargs) kwargs = {'x': [1, 2], 'y': [1, 2], 'u': [1, 2], 'v': [1, 2], 'arrow_scale': 0} - self.assertRaises(ValueError, tls.FigureFactory.create_quiver, + self.assertRaises(ValueError, plotly.graph_objs.figure_factory.FigureFactory.create_quiver, **kwargs) def test_one_arrow(self): # we should be able to create a single arrow using create_quiver - quiver = tls.FigureFactory.create_quiver(x=[1], y=[1], + quiver = plotly.graph_objs.figure_factory.FigureFactory.create_quiver(x=[1], y=[1], u=[1], v=[1], scale=1) expected_quiver = { @@ -72,7 +73,7 @@ def test_more_kwargs(self): # we should be able to create 2 arrows and change the arrow_scale, # angle, and arrow using create_quiver - quiver = tls.FigureFactory.create_quiver(x=[1, 2], + quiver = plotly.graph_objs.figure_factory.FigureFactory.create_quiver(x=[1, 2], y=[1, 2], u=[math.cos(1), math.cos(2)], @@ -127,27 +128,27 @@ def test_unequal_ohlc_length(self): kwargs = {'open': [1], 'high': [1, 3], 'low': [1, 2], 'close': [1, 2], 'direction': ['increasing']} - self.assertRaises(PlotlyError, tls.FigureFactory.create_ohlc, **kwargs) - self.assertRaises(PlotlyError, tls.FigureFactory.create_candlestick, + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) kwargs = {'open': [1, 2], 'high': [1, 2, 3], 'low': [1, 2], 'close': [1, 2], 'direction': ['decreasing']} - self.assertRaises(PlotlyError, tls.FigureFactory.create_ohlc, **kwargs) - self.assertRaises(PlotlyError, tls.FigureFactory.create_candlestick, + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) kwargs = {'open': [1, 2], 'high': [2, 3], 'low': [0], 'close': [1, 3]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_ohlc, **kwargs) - self.assertRaises(PlotlyError, tls.FigureFactory.create_candlestick, + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) kwargs = {'open': [1, 2], 'high': [2, 3], 'low': [1, 2], 'close': [1]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_ohlc, **kwargs) - self.assertRaises(PlotlyError, tls.FigureFactory.create_candlestick, + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) + self.assertRaises(PlotlyError, plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) def test_direction_arg(self): @@ -162,11 +163,11 @@ def test_direction_arg(self): self.assertRaisesRegexp(PlotlyError, "direction must be defined as " "'increasing', 'decreasing', or 'both'", - tls.FigureFactory.create_ohlc, **kwargs) + plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) self.assertRaisesRegexp(PlotlyError, "direction must be defined as " "'increasing', 'decreasing', or 'both'", - tls.FigureFactory.create_candlestick, **kwargs) + plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) kwargs = {'open': [1, 2], 'high': [1, 3], 'low': [1, 2], 'close': [1, 2], @@ -174,11 +175,11 @@ def test_direction_arg(self): self.assertRaisesRegexp(PlotlyError, "direction must be defined as " "'increasing', 'decreasing', or 'both'", - tls.FigureFactory.create_ohlc, **kwargs) + plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) self.assertRaisesRegexp(PlotlyError, "direction must be defined as " "'increasing', 'decreasing', or 'both'", - tls.FigureFactory.create_candlestick, **kwargs) + plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) def test_high_highest_value(self): @@ -194,7 +195,7 @@ def test_high_highest_value(self): "low, or close values. " "Double check that your data " "is entered in O-H-L-C order", - tls.FigureFactory.create_ohlc, + plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) self.assertRaisesRegexp(PlotlyError, "Oops! Looks like some of " "your high values are less " @@ -202,7 +203,7 @@ def test_high_highest_value(self): "low, or close values. " "Double check that your data " "is entered in O-H-L-C order", - tls.FigureFactory.create_candlestick, + plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) def test_low_lowest_value(self): @@ -222,7 +223,7 @@ def test_low_lowest_value(self): ", open, or close values. " "Double check that your data " "is entered in O-H-L-C order", - tls.FigureFactory.create_ohlc, + plotly.graph_objs.figure_factory.FigureFactory.create_ohlc, **kwargs) self.assertRaisesRegexp(PlotlyError, "Oops! Looks like some of " @@ -231,14 +232,14 @@ def test_low_lowest_value(self): ", open, or close values. " "Double check that your data " "is entered in O-H-L-C order", - tls.FigureFactory.create_candlestick, + plotly.graph_objs.figure_factory.FigureFactory.create_candlestick, **kwargs) def test_one_ohlc(self): # This should create one "increase" (i.e. close > open) ohlc stick - ohlc = tls.FigureFactory.create_ohlc(open=[33.0], + ohlc = plotly.graph_objs.figure_factory.FigureFactory.create_ohlc(open=[33.0], high=[33.2], low=[32.7], close=[33.1]) @@ -268,7 +269,7 @@ def test_one_ohlc_increase(self): # This should create one "increase" (i.e. close > open) ohlc stick - ohlc_incr = tls.FigureFactory.create_ohlc(open=[33.0], + ohlc_incr = plotly.graph_objs.figure_factory.FigureFactory.create_ohlc(open=[33.0], high=[33.2], low=[32.7], close=[33.1], @@ -293,7 +294,7 @@ def test_one_ohlc_decrease(self): # This should create one "increase" (i.e. close > open) ohlc stick - ohlc_decr = tls.FigureFactory.create_ohlc(open=[33.0], + ohlc_decr = plotly.graph_objs.figure_factory.FigureFactory.create_ohlc(open=[33.0], high=[33.2], low=[30.7], close=[31.1], @@ -319,7 +320,7 @@ def test_one_candlestick(self): # This should create one "increase" (i.e. close > open) candlestick - can_inc = tls.FigureFactory.create_candlestick(open=[33.0], + can_inc = plotly.graph_objs.figure_factory.FigureFactory.create_candlestick(open=[33.0], high=[33.2], low=[32.7], close=[33.1]) @@ -364,7 +365,7 @@ def test_datetime_ohlc(self): datetime.datetime(year=2014, month=9, day=4), datetime.datetime(year=2014, month=12, day=5)] - ohlc_d = tls.FigureFactory.create_ohlc(open_data, high_data, + ohlc_d = plotly.graph_objs.figure_factory.FigureFactory.create_ohlc(open_data, high_data, low_data, close_data, dates=x) @@ -568,7 +569,7 @@ def test_datetime_candlestick(self): datetime.datetime(year=2014, month=9, day=4), datetime.datetime(year=2014, month=12, day=5)] - candle = tls.FigureFactory.create_candlestick(open_data, high_data, + candle = plotly.graph_objs.figure_factory.FigureFactory.create_candlestick(open_data, high_data, low_data, close_data, dates=x) exp_candle = {'data': [{'boxpoints': False, @@ -684,23 +685,3 @@ def test_datetime_candlestick(self): 'layout': {}} self.assertEqual(candle, exp_candle) - - -# class TestDistplot(TestCase): - -# def test_scipy_import_error(self): - -# # make sure Import Error is raised when _scipy_imported = False - -# hist_data = [[1.1, 1.1, 2.5, 3.0, 3.5, -# 3.5, 4.1, 4.4, 4.5, 4.5, -# 5.0, 5.0, 5.2, 5.5, 5.5, -# 5.5, 5.5, 5.5, 6.1, 7.0]] - -# group_labels = ['distplot example'] - -# self.assertRaisesRegexp(ImportError, -# "FigureFactory.create_distplot requires scipy", -# tls.FigureFactory.create_distplot, -# hist_data, group_labels) - diff --git a/plotly/tests/test_optional/test_figure_factory.py b/plotly/tests/test_optional/test_figure_factory.py index f9e107c5dca..08f5c27fe09 100644 --- a/plotly/tests/test_optional/test_figure_factory.py +++ b/plotly/tests/test_optional/test_figure_factory.py @@ -1,6 +1,7 @@ from unittest import TestCase from plotly.graph_objs import graph_objs as go from plotly.exceptions import PlotlyError +from plotly.graph_objs.figure_factory import FigureFactory import plotly.tools as tls from plotly.tests.test_optional.optional_utils import NumpyTestUtilsMixin @@ -21,7 +22,7 @@ def test_wrong_curve_type(self): 'curve_type': 'curve'} self.assertRaisesRegexp(PlotlyError, "curve_type must be defined as " "'kde' or 'normal'", - tls.FigureFactory.create_distplot, **kwargs) + FigureFactory.create_distplot, **kwargs) def test_wrong_histdata_format(self): @@ -30,16 +31,16 @@ def test_wrong_histdata_format(self): # will fail) kwargs = {'hist_data': [1, 2, 3], 'group_labels': ['group']} - self.assertRaises(PlotlyError, tls.FigureFactory.create_distplot, + self.assertRaises(PlotlyError, FigureFactory.create_distplot, **kwargs) def test_unequal_data_label_length(self): kwargs = {'hist_data': [[1, 2]], 'group_labels': ['group', 'group2']} - self.assertRaises(PlotlyError, tls.FigureFactory.create_distplot, + self.assertRaises(PlotlyError, FigureFactory.create_distplot, **kwargs) kwargs = {'hist_data': [[1, 2], [1, 2, 3]], 'group_labels': ['group']} - self.assertRaises(PlotlyError, tls.FigureFactory.create_distplot, + self.assertRaises(PlotlyError, FigureFactory.create_distplot, **kwargs) def test_simple_distplot(self): @@ -47,8 +48,8 @@ def test_simple_distplot(self): # we should be able to create a single distplot with a simple dataset # and default kwargs - dp = tls.FigureFactory.create_distplot(hist_data=[[1, 2, 2, 3]], - group_labels=['distplot']) + dp = FigureFactory.create_distplot(hist_data=[[1, 2, 2, 3]], + group_labels=['distplot']) expected_dp_layout = {'barmode': 'overlay', 'hovermode': 'closest', 'legend': {'traceorder': 'reversed'}, @@ -103,8 +104,8 @@ def test_distplot_more_args(self): hist_data = [hist1_x] + [hist2_x] group_labels = ['2012', '2013'] - dp = tls.FigureFactory.create_distplot(hist_data, group_labels, - show_rug=False, bin_size=.2) + dp = FigureFactory.create_distplot(hist_data, group_labels, + show_rug=False, bin_size=.2) dp['layout'].update(title='Dist Plot') expected_dp_layout = {'barmode': 'overlay', @@ -160,7 +161,7 @@ def test_wrong_arrow_scale(self): 'u': [[-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]], 'arrow_scale': 0} - self.assertRaises(ValueError, tls.FigureFactory.create_streamline, + self.assertRaises(ValueError, FigureFactory.create_streamline, **kwargs) def test_wrong_density(self): @@ -171,7 +172,7 @@ def test_wrong_density(self): 'u': [[-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]], 'density': 0} - self.assertRaises(ValueError, tls.FigureFactory.create_streamline, + self.assertRaises(ValueError, FigureFactory.create_streamline, **kwargs) def test_uneven_x(self): @@ -181,7 +182,7 @@ def test_uneven_x(self): kwargs = {'x': [0, 2, 7, 9], 'y': [0, 2, 4, 6], 'u': [[-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_streamline, + self.assertRaises(PlotlyError, FigureFactory.create_streamline, **kwargs) def test_uneven_y(self): @@ -191,7 +192,7 @@ def test_uneven_y(self): kwargs = {'x': [0, 2, 4, 6], 'y': [1.5, 2, 3, 3.5], 'u': [[-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_streamline, + self.assertRaises(PlotlyError, FigureFactory.create_streamline, **kwargs) def test_unequal_length_xy(self): @@ -201,7 +202,7 @@ def test_unequal_length_xy(self): kwargs = {'x': [0, 2, 4, 6], 'y': [1.5, 2, 3.5], 'u': [[-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_streamline, + self.assertRaises(PlotlyError, FigureFactory.create_streamline, **kwargs) def test_unequal_length_uv(self): @@ -211,7 +212,7 @@ def test_unequal_length_uv(self): kwargs = {'x': [0, 2, 4, 6], 'y': [1.5, 2, 3, 3.5], 'u': [[-1, -5], [-1, -5], [-1, -5]], 'v': [[1, 1], [-3, -3]]} - self.assertRaises(PlotlyError, tls.FigureFactory.create_streamline, + self.assertRaises(PlotlyError, FigureFactory.create_streamline, **kwargs) def test_simple_streamline(self): @@ -227,14 +228,14 @@ def test_simple_streamline(self): # u = u.T #transpose # v = v.T #transpose - strln = tls.FigureFactory.create_streamline(x=[-1., 0., 1.], - y=[-1., 0., 1.], - u=[[1., 0., 1.], - [1., 0., 1.], - [1., 0., 1.]], - v=[[1., 1., 1.], - [0., 0., 0.], - [1., 1., 1.]]) + strln = FigureFactory.create_streamline(x=[-1., 0., 1.], + y=[-1., 0., 1.], + u=[[1., 0., 1.], + [1., 0., 1.], + [1., 0., 1.]], + v=[[1., 1., 1.], + [0., 0., 0.], + [1., 1., 1.]]) expected_strln_0_100 = { 'y': [-1.0, -0.9788791845863757, -0.9579399744939614, -0.9371777642073374, -0.9165881396413338, -0.8961668671832106, -0.8759098835283448, -0.8558132862403048, -0.835873324973195, -0.8160863933003534, -0.7964490210989816, -0.7769578674451656, -0.7576097139780906, -0.7384014586961288, -0.7193301101509343, -0.7003927820087748, -0.681586687951103, -0.6629091368888596, -0.64435752846723, -0.6259293488396024, -0.6076221666912738, -0.5894336294951057, -0.5713614599827976, -0.5534034528167977, -0.5355574714490806, -0.5178214451541254, -0.5001933662244311, -0.4826712873178177, -0.4652533189465894, -0.44793762709939944, -0.4307224309873414, -0.4136060009064273, -0.39658665620919065, -0.3796627633786812, -0.3628327341986042, -0.34609502401380254, -0.3294481300756896, -0.31289058996761565, -0.2964209801054992, -0.28003791430937197, -0.2637400424417804, -0.24752604910925968, -0.23139465242334434, -0.21534460281781365, -0.19937468191908325, -0.18348370146685278, -0.1676705022823033, -0.15193395328130999, -0.13627295053029143, -0.1206864163424669, -0.10517329841242584, -0.08973256898704507, -0.07436322407090357, -0.05906428266445696, -0.04383478603333624, -0.028673797007230273, -0.013580399306900914, 0.0014484211645073852, 0.01648792568956914, 0.03159429687713278, 0.04676843461935776, 0.062011259175942746, 0.07732371182540754, 0.09270675554339824, 0.10816137570939799, 0.12368858084331191, 0.1392894033734846, 0.1549649004378033, 0.1707161547196483, 0.1865442753205595, 0.20245039867161063, 0.21843568948560943, 0.23450134175238246, 0.25064857977955146, 0.26687865928136767, 0.2831928685183458, 0.29959252949062387, 0.3160789991881776, 0.33265367090123643, 0.3493179755944802, 0.366073383348855, 0.3829214048751186, 0.39986359310352526, 0.41690154485438513, 0.4340369025945845, 0.4512713562855355, 0.46860664532844054, 0.4860445606132082, 0.5035869466778524, 0.5212357039857456, 0.5389927913286829, 0.5568602283643591, 0.5748400982975623, 0.5929345507151613, 0.6111458045858065, 0.6294761514361948, 0.6479279587167714, 0.6665036733708583, 0.6852058256224467, 0.704037032999252], 'x': [-1.0, -0.9788791845863756, -0.9579399744939614, -0.9371777642073374, -0.9165881396413338, -0.8961668671832106, -0.8759098835283448, -0.8558132862403048, -0.835873324973195, -0.8160863933003534, -0.7964490210989816, -0.7769578674451656, -0.7576097139780906, -0.7384014586961289, -0.7193301101509344, -0.7003927820087748, -0.6815866879511031, -0.6629091368888596, -0.6443575284672302, -0.6259293488396025, -0.6076221666912739, -0.5894336294951058, -0.5713614599827976, -0.5534034528167978, -0.5355574714490807, -0.5178214451541254, -0.5001933662244312, -0.4826712873178177, -0.4652533189465894, -0.44793762709939944, -0.4307224309873414, -0.4136060009064273, -0.39658665620919065, -0.3796627633786812, -0.3628327341986042, -0.34609502401380254, -0.3294481300756896, -0.31289058996761565, -0.2964209801054992, -0.28003791430937197, -0.2637400424417804, -0.24752604910925968, -0.23139465242334434, -0.21534460281781365, -0.19937468191908325, -0.18348370146685278, -0.1676705022823033, -0.15193395328130999, -0.13627295053029143, -0.1206864163424669, -0.10517329841242584, -0.08973256898704507, -0.07436322407090357, -0.05906428266445696, -0.04383478603333624, -0.028673797007230273, -0.013580399306900914, 0.0014484211645073852, 0.01648792568956914, 0.03159429687713278, 0.04676843461935776, 0.062011259175942746, 0.07732371182540754, 0.09270675554339824, 0.10816137570939799, 0.12368858084331191, 0.1392894033734846, 0.1549649004378033, 0.1707161547196483, 0.1865442753205595, 0.20245039867161063, 0.21843568948560943, 0.23450134175238246, 0.25064857977955146, 0.26687865928136767, 0.2831928685183458, 0.29959252949062387, 0.3160789991881776, 0.33265367090123643, 0.3493179755944802, 0.366073383348855, 0.3829214048751186, 0.39986359310352526, 0.41690154485438513, 0.4340369025945845, 0.4512713562855355, 0.46860664532844054, 0.4860445606132082, 0.5035869466778524, 0.5212357039857456, 0.5389927913286829, 0.5568602283643591, 0.5748400982975623, 0.5929345507151613, 0.6111458045858065, 0.6294761514361948, 0.6479279587167714, 0.6665036733708583, 0.6852058256224467, 0.704037032999252], @@ -251,7 +252,7 @@ class TestDendrogram(NumpyTestUtilsMixin, TestCase): def test_default_dendrogram(self): X = np.array([[1, 2, 3, 4], [1, 1, 3, 4], [1, 2, 1, 4], [1, 2, 3, 1]]) - dendro = tls.FigureFactory.create_dendrogram(X=X) + dendro = FigureFactory.create_dendrogram(X=X) expected_dendro = go.Figure( data=go.Data([ @@ -330,7 +331,7 @@ def test_dendrogram_random_matrix(self): X[2, :] = sum(X, 0) names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] - dendro = tls.FigureFactory.create_dendrogram(X, labels=names) + dendro = FigureFactory.create_dendrogram(X, labels=names) expected_dendro = go.Figure( data=go.Data([ @@ -424,18 +425,18 @@ def test_dendrogram_random_matrix(self): def test_dendrogram_orientation(self): X = np.random.rand(5, 5) - dendro_left = tls.FigureFactory.create_dendrogram( + dendro_left = FigureFactory.create_dendrogram( X, orientation='left') self.assertEqual(len(dendro_left['layout']['yaxis']['ticktext']), 5) tickvals_left = np.array(dendro_left['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_left <= 0).all()) - dendro_right = tls.FigureFactory.create_dendrogram( + dendro_right = FigureFactory.create_dendrogram( X, orientation='right') tickvals_right = np.array(dendro_right['layout']['yaxis']['tickvals']) self.assertTrue((tickvals_right >= 0).all()) - dendro_bottom = tls.FigureFactory.create_dendrogram( + dendro_bottom = FigureFactory.create_dendrogram( X, orientation='bottom') self.assertEqual(len(dendro_bottom['layout']['xaxis']['ticktext']), 5) tickvals_bottom = np.array( @@ -443,7 +444,7 @@ def test_dendrogram_orientation(self): ) self.assertTrue((tickvals_bottom >= 0).all()) - dendro_top = tls.FigureFactory.create_dendrogram(X, orientation='top') + dendro_top = FigureFactory.create_dendrogram(X, orientation='top') tickvals_top = np.array(dendro_top['layout']['xaxis']['tickvals']) self.assertTrue((tickvals_top <= 0).all()) @@ -462,7 +463,7 @@ def test_dendrogram_colorscale(self): 'rgb(220,220,220)', # gainsboro 'rgb(245,245,245)'] # white smoke - dendro = tls.FigureFactory.create_dendrogram(X, colorscale=greyscale) + dendro = FigureFactory.create_dendrogram(X, colorscale=greyscale) expected_dendro = go.Figure( data=go.Data([ diff --git a/plotly/tools.py b/plotly/tools.py index 9f8a7e8b452..3ce47bc693a 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -8,72 +8,25 @@ """ from __future__ import absolute_import -from collections import OrderedDict import warnings - import six -import math -from plotly import utils -from plotly import exceptions -from plotly import graph_reference -from plotly import session +from plotly import (exceptions, graph_reference, optional_imports, session, + utils) from plotly.files import (CONFIG_FILE, CREDENTIALS_FILE, FILE_CONTENT, GRAPH_REFERENCE_FILE, check_file_permissions) # Warning format + + def warning_on_one_line(message, category, filename, lineno, file=None, line=None): return '%s:%s: %s:\n\n%s\n\n' % (filename, lineno, category.__name__, message) warnings.formatwarning = warning_on_one_line -try: - from . import matplotlylib - _matplotlylib_imported = True -except ImportError: - _matplotlylib_imported = False - -try: - import IPython - import IPython.core.display - _ipython_imported = True -except ImportError: - _ipython_imported = False - -try: - import numpy as np - _numpy_imported = True -except ImportError: - _numpy_imported = False - -try: - import scipy as scp - _scipy_imported = True -except ImportError: - _scipy_imported = False - -try: - import scipy.spatial as scs - _scipy__spatial_imported = True -except ImportError: - _scipy__spatial_imported = False - -try: - import scipy.cluster.hierarchy as sch - _scipy__cluster__hierarchy_imported = True -except ImportError: - _scipy__cluster__hierarchy_imported = False - -try: - import scipy - import scipy.stats - _scipy_imported = True -except ImportError: - _scipy_imported = False - def get_config_defaults(): """ @@ -384,11 +337,12 @@ def embed(file_owner_or_url, file_id=None, width="100%", height=525): height=height) # see if we are in the SageMath Cloud - from sage_salvus import html - return html(s, hide=False) + sage_salvus = optional_imports.get_module('sage_salvus') + if sage_salvus: + return sage_salvus.html(s, hide=False) except: pass - if _ipython_imported: + if optional_imports.get_module('IPython.core.display'): if file_id: plotly_domain = ( session.get_session_config().get('plotly_domain') or @@ -467,7 +421,8 @@ def mpl_to_plotly(fig, resize=False, strip_style=False, verbose=False): {plotly_domain}/python/getting-started """ - if _matplotlylib_imported: + matplotlylib = optional_imports.get_module('plotly.matplotlylib') + if matplotlylib: renderer = matplotlylib.PlotlyRenderer() matplotlylib.Exporter(renderer).run(fig) if resize: @@ -1362,8 +1317,9 @@ def _replace_newline(obj): return obj # we return the actual reference... but DON'T mutate. -if _ipython_imported: - class PlotlyDisplay(IPython.core.display.HTML): +IPython__core__display = optional_imports.get_module('IPython.core.display') +if IPython__core__display: + class PlotlyDisplay(IPython__core__display.HTML): """An IPython display object for use with plotly urls PlotlyDisplay objects should be instantiated with a url for a plot. @@ -1415,2011 +1371,42 @@ def return_figure_from_figure_or_data(figure_or_data, validate_figure): return figure -# Default colours for finance charts -_DEFAULT_INCREASING_COLOR = '#3D9970' # http://clrs.cc -_DEFAULT_DECREASING_COLOR = '#FF4136' +FIGURE_FACTORY_DEPRECATION_WARNING = ( + 'DeprecationWarning: Oops, FigureFactory has been moved! Please import it ' + 'as follows:\n' + '`from plotly.graph_objs.figure_factory import FigureFactory`' +) +# Define this to raise a deprecation warning. Functionality has moved! class FigureFactory(object): - """ - BETA functions to create specific chart types. - - This is beta as in: subject to change in a backwards incompatible way - without notice. - - Supported chart types include candlestick, open high low close, quiver, - and streamline. See FigureFactory.create_candlestick, - FigureFactory.create_ohlc, FigureFactory.create_quiver, or - FigureFactory.create_streamline for for more infomation and examples of a - specific chart type. - """ - - @staticmethod - def _validate_equal_length(*args): - """ - Validates that data lists or ndarrays are the same length. - - :raises: (PlotlyError) If any data lists are not the same length. - """ - length = len(args[0]) - if any(len(lst) != length for lst in args): - raise exceptions.PlotlyError("Oops! Your data lists or ndarrays " - "should be the same length.") - - @staticmethod - def _validate_ohlc(open, high, low, close, direction, **kwargs): - """ - ohlc and candlestick specific validations - - Specifically, this checks that the high value is the greatest value and - the low value is the lowest value in each unit. - - See FigureFactory.create_ohlc() or FigureFactory.create_candlestick() - for params - - :raises: (PlotlyError) If the high value is not the greatest value in - each unit. - :raises: (PlotlyError) If the low value is not the lowest value in each - unit. - :raises: (PlotlyError) If direction is not 'increasing' or 'decreasing' - """ - for lst in [open, low, close]: - for index in range(len(high)): - if high[index] < lst[index]: - raise exceptions.PlotlyError("Oops! Looks like some of " - "your high values are less " - "the corresponding open, " - "low, or close values. " - "Double check that your data " - "is entered in O-H-L-C order") - - for lst in [open, high, close]: - for index in range(len(low)): - if low[index] > lst[index]: - raise exceptions.PlotlyError("Oops! Looks like some of " - "your low values are greater " - "than the corresponding high" - ", open, or close values. " - "Double check that your data " - "is entered in O-H-L-C order") - - direction_opts = ('increasing', 'decreasing', 'both') - if direction not in direction_opts: - raise exceptions.PlotlyError("direction must be defined as " - "'increasing', 'decreasing', or " - "'both'") - - @staticmethod - def _validate_distplot(hist_data, curve_type): - """ - distplot specific validations - - :raises: (PlotlyError) If hist_data is not a list of lists - :raises: (PlotlyError) If curve_type is not valid (i.e. not 'kde' or - 'normal'). - """ - try: - import pandas as pd - _pandas_imported = True - except ImportError: - _pandas_imported = False - - hist_data_types = (list,) - if _numpy_imported: - hist_data_types += (np.ndarray,) - if _pandas_imported: - hist_data_types += (pd.core.series.Series,) - - if not isinstance(hist_data[0], hist_data_types): - raise exceptions.PlotlyError("Oops, this function was written " - "to handle multiple datasets, if " - "you want to plot just one, make " - "sure your hist_data variable is " - "still a list of lists, i.e. x = " - "[1, 2, 3] -> x = [[1, 2, 3]]") - - curve_opts = ('kde', 'normal') - if curve_type not in curve_opts: - raise exceptions.PlotlyError("curve_type must be defined as " - "'kde' or 'normal'") - - if _scipy_imported is False: - raise ImportError("FigureFactory.create_distplot requires scipy") - - @staticmethod - def _validate_positive_scalars(**kwargs): - """ - Validates that all values given in key/val pairs are positive. - - Accepts kwargs to improve Exception messages. - - :raises: (PlotlyError) If any value is < 0 or raises. - """ - for key, val in kwargs.items(): - try: - if val <= 0: - raise ValueError('{} must be > 0, got {}'.format(key, val)) - except TypeError: - raise exceptions.PlotlyError('{} must be a number, got {}' - .format(key, val)) - - @staticmethod - def _validate_streamline(x, y): - """ - streamline specific validations - - Specifically, this checks that x and y are both evenly spaced, - and that the package numpy is available. - - See FigureFactory.create_streamline() for params - - :raises: (ImportError) If numpy is not available. - :raises: (PlotlyError) If x is not evenly spaced. - :raises: (PlotlyError) If y is not evenly spaced. - """ - if _numpy_imported is False: - raise ImportError("FigureFactory.create_streamline requires numpy") - for index in range(len(x) - 1): - if ((x[index + 1] - x[index]) - (x[1] - x[0])) > .0001: - raise exceptions.PlotlyError("x must be a 1 dimensional, " - "evenly spaced array") - for index in range(len(y) - 1): - if ((y[index + 1] - y[index]) - - (y[1] - y[0])) > .0001: - raise exceptions.PlotlyError("y must be a 1 dimensional, " - "evenly spaced array") - - @staticmethod - def _flatten(array): - """ - Uses list comprehension to flatten array - - :param (array): An iterable to flatten - :raises (PlotlyError): If iterable is not nested. - :rtype (list): The flattened list. - """ - try: - return [item for sublist in array for item in sublist] - except TypeError: - raise exceptions.PlotlyError("Your data array could not be " - "flattened! Make sure your data is " - "entered as lists or ndarrays!") - - @staticmethod - def create_quiver(x, y, u, v, scale=.1, arrow_scale=.3, - angle=math.pi / 9, **kwargs): - """ - Returns data for a quiver plot. - - :param (list|ndarray) x: x coordinates of the arrow locations - :param (list|ndarray) y: y coordinates of the arrow locations - :param (list|ndarray) u: x components of the arrow vectors - :param (list|ndarray) v: y components of the arrow vectors - :param (float in [0,1]) scale: scales size of the arrows(ideally to - avoid overlap). Default = .1 - :param (float in [0,1]) arrow_scale: value multiplied to length of barb - to get length of arrowhead. Default = .3 - :param (angle in radians) angle: angle of arrowhead. Default = pi/9 - :param kwargs: kwargs passed through plotly.graph_objs.Scatter - for more information on valid kwargs call - help(plotly.graph_objs.Scatter) - - :rtype (dict): returns a representation of quiver figure. - - Example 1: Trivial Quiver - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - import math - - # 1 Arrow from (0,0) to (1,1) - fig = FF.create_quiver(x=[0], y=[0], - u=[1], v=[1], - scale=1) - - py.plot(fig, filename='quiver') - ``` - - Example 2: Quiver plot using meshgrid - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - import numpy as np - import math - - # Add data - x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2)) - u = np.cos(x)*y - v = np.sin(x)*y - - #Create quiver - fig = FF.create_quiver(x, y, u, v) - - # Plot - py.plot(fig, filename='quiver') - ``` - - Example 3: Styling the quiver plot - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - import numpy as np - import math - - # Add data - x, y = np.meshgrid(np.arange(-np.pi, math.pi, .5), - np.arange(-math.pi, math.pi, .5)) - u = np.cos(x)*y - v = np.sin(x)*y - - # Create quiver - fig = FF.create_quiver(x, y, u, v, scale=.2, - arrow_scale=.3, - angle=math.pi/6, - name='Wind Velocity', - line=Line(width=1)) - - # Add title to layout - fig['layout'].update(title='Quiver Plot') - - # Plot - py.plot(fig, filename='quiver') - ``` - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - FigureFactory._validate_equal_length(x, y, u, v) - FigureFactory._validate_positive_scalars(arrow_scale=arrow_scale, - scale=scale) - - barb_x, barb_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_barbs() - arrow_x, arrow_y = _Quiver(x, y, u, v, scale, - arrow_scale, angle).get_quiver_arrows() - quiver = graph_objs.Scatter(x=barb_x + arrow_x, - y=barb_y + arrow_y, - mode='lines', **kwargs) - - data = [quiver] - layout = graph_objs.Layout(hovermode='closest') - - return graph_objs.Figure(data=data, layout=layout) - - @staticmethod - def create_streamline(x, y, u, v, - density=1, angle=math.pi / 9, - arrow_scale=.09, **kwargs): - """ - Returns data for a streamline plot. - - :param (list|ndarray) x: 1 dimensional, evenly spaced list or array - :param (list|ndarray) y: 1 dimensional, evenly spaced list or array - :param (ndarray) u: 2 dimensional array - :param (ndarray) v: 2 dimensional array - :param (float|int) density: controls the density of streamlines in - plot. This is multiplied by 30 to scale similiarly to other - available streamline functions such as matplotlib. - Default = 1 - :param (angle in radians) angle: angle of arrowhead. Default = pi/9 - :param (float in [0,1]) arrow_scale: value to scale length of arrowhead - Default = .09 - :param kwargs: kwargs passed through plotly.graph_objs.Scatter - for more information on valid kwargs call - help(plotly.graph_objs.Scatter) - - :rtype (dict): returns a representation of streamline figure. - - Example 1: Plot simple streamline and increase arrow size - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - import numpy as np - import math - - # Add data - x = np.linspace(-3, 3, 100) - y = np.linspace(-3, 3, 100) - Y, X = np.meshgrid(x, y) - u = -1 - X**2 + Y - v = 1 + X - Y**2 - u = u.T # Transpose - v = v.T # Transpose - - # Create streamline - fig = FF.create_streamline(x, y, u, v, - arrow_scale=.1) - - # Plot - py.plot(fig, filename='streamline') - ``` - - Example 2: from nbviewer.ipython.org/github/barbagroup/AeroPython - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - import numpy as np - import math - - # Add data - N = 50 - x_start, x_end = -2.0, 2.0 - y_start, y_end = -1.0, 1.0 - x = np.linspace(x_start, x_end, N) - y = np.linspace(y_start, y_end, N) - X, Y = np.meshgrid(x, y) - ss = 5.0 - x_s, y_s = -1.0, 0.0 - - # Compute the velocity field on the mesh grid - u_s = ss/(2*np.pi) * (X-x_s)/((X-x_s)**2 + (Y-y_s)**2) - v_s = ss/(2*np.pi) * (Y-y_s)/((X-x_s)**2 + (Y-y_s)**2) - - # Create streamline - fig = FF.create_streamline(x, y, u_s, v_s, - density=2, name='streamline') - - # Add source point - point = Scatter(x=[x_s], y=[y_s], mode='markers', - marker=Marker(size=14), name='source point') - - # Plot - fig['data'].append(point) - py.plot(fig, filename='streamline') - ``` - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - FigureFactory._validate_equal_length(x, y) - FigureFactory._validate_equal_length(u, v) - FigureFactory._validate_streamline(x, y) - FigureFactory._validate_positive_scalars(density=density, - arrow_scale=arrow_scale) - - streamline_x, streamline_y = _Streamline(x, y, u, v, - density, angle, - arrow_scale).sum_streamlines() - arrow_x, arrow_y = _Streamline(x, y, u, v, - density, angle, - arrow_scale).get_streamline_arrows() - - streamline = graph_objs.Scatter(x=streamline_x + arrow_x, - y=streamline_y + arrow_y, - mode='lines', **kwargs) - - data = [streamline] - layout = graph_objs.Layout(hovermode='closest') - - return graph_objs.Figure(data=data, layout=layout) - - @staticmethod - def _make_increasing_ohlc(open, high, low, close, dates, **kwargs): - """ - Makes increasing ohlc sticks - - _make_increasing_ohlc() and _make_decreasing_ohlc separate the - increasing trace from the decreasing trace so kwargs (such as - color) can be passed separately to increasing or decreasing traces - when direction is set to 'increasing' or 'decreasing' in - FigureFactory.create_candlestick() - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing values - :param (list) dates: list of datetime objects. Default: None - :param kwargs: kwargs to be passed to increasing trace via - plotly.graph_objs.Scatter. - - :rtype (trace) ohlc_incr_data: Scatter trace of all increasing ohlc - sticks. - """ - (flat_increase_x, - flat_increase_y, - text_increase) = _OHLC(open, high, low, close, dates).get_increase() - - if 'name' in kwargs: - showlegend = True - else: - kwargs.setdefault('name', 'Increasing') - showlegend = False - - kwargs.setdefault('line', dict(color=_DEFAULT_INCREASING_COLOR, - width=1)) - kwargs.setdefault('text', text_increase) - - ohlc_incr = dict(type='scatter', - x=flat_increase_x, - y=flat_increase_y, - mode='lines', - showlegend=showlegend, - **kwargs) - return ohlc_incr - - @staticmethod - def _make_decreasing_ohlc(open, high, low, close, dates, **kwargs): - """ - Makes decreasing ohlc sticks - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing values - :param (list) dates: list of datetime objects. Default: None - :param kwargs: kwargs to be passed to increasing trace via - plotly.graph_objs.Scatter. - - :rtype (trace) ohlc_decr_data: Scatter trace of all decreasing ohlc - sticks. - """ - (flat_decrease_x, - flat_decrease_y, - text_decrease) = _OHLC(open, high, low, close, dates).get_decrease() - - kwargs.setdefault('line', dict(color=_DEFAULT_DECREASING_COLOR, - width=1)) - kwargs.setdefault('text', text_decrease) - kwargs.setdefault('showlegend', False) - kwargs.setdefault('name', 'Decreasing') - - ohlc_decr = dict(type='scatter', - x=flat_decrease_x, - y=flat_decrease_y, - mode='lines', - **kwargs) - return ohlc_decr @staticmethod - def create_ohlc(open, high, low, close, - dates=None, direction='both', - **kwargs): - """ - BETA function that creates an ohlc chart - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing - :param (list) dates: list of datetime objects. Default: None - :param (string) direction: direction can be 'increasing', 'decreasing', - or 'both'. When the direction is 'increasing', the returned figure - consists of all units where the close value is greater than the - corresponding open value, and when the direction is 'decreasing', - the returned figure consists of all units where the close value is - less than or equal to the corresponding open value. When the - direction is 'both', both increasing and decreasing units are - returned. Default: 'both' - :param kwargs: kwargs passed through plotly.graph_objs.Scatter. - These kwargs describe other attributes about the ohlc Scatter trace - such as the color or the legend name. For more information on valid - kwargs call help(plotly.graph_objs.Scatter) - - :rtype (dict): returns a representation of an ohlc chart figure. - - Example 1: Simple OHLC chart from a Pandas DataFrame - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - from datetime import datetime - - import pandas.io.data as web - - df = web.DataReader("aapl", 'yahoo', datetime(2008, 8, 15), datetime(2008, 10, 15)) - fig = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, dates=df.index) - - py.plot(fig, filename='finance/aapl-ohlc') - ``` - - Example 2: Add text and annotations to the OHLC chart - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - from datetime import datetime - - import pandas.io.data as web - - df = web.DataReader("aapl", 'yahoo', datetime(2008, 8, 15), datetime(2008, 10, 15)) - fig = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, dates=df.index) - - # Update the fig - all options here: https://plot.ly/python/reference/#Layout - fig['layout'].update({ - 'title': 'The Great Recession', - 'yaxis': {'title': 'AAPL Stock'}, - 'shapes': [{ - 'x0': '2008-09-15', 'x1': '2008-09-15', 'type': 'line', - 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper', - 'line': {'color': 'rgb(40,40,40)', 'width': 0.5} - }], - 'annotations': [{ - 'text': "the fall of Lehman Brothers", - 'x': '2008-09-15', 'y': 1.02, - 'xref': 'x', 'yref': 'paper', - 'showarrow': False, 'xanchor': 'left' - }] - }) - - py.plot(fig, filename='finance/aapl-recession-ohlc', validate=False) - ``` - - Example 3: Customize the OHLC colors - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - from plotly.graph_objs import Line, Marker - from datetime import datetime - - import pandas.io.data as web - - df = web.DataReader("aapl", 'yahoo', datetime(2008, 1, 1), datetime(2009, 4, 1)) - - # Make increasing ohlc sticks and customize their color and name - fig_increasing = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, dates=df.index, - direction='increasing', name='AAPL', - line=Line(color='rgb(150, 200, 250)')) - - # Make decreasing ohlc sticks and customize their color and name - fig_decreasing = FF.create_ohlc(df.Open, df.High, df.Low, df.Close, dates=df.index, - direction='decreasing', - line=Line(color='rgb(128, 128, 128)')) - - # Initialize the figure - fig = fig_increasing - - # Add decreasing data with .extend() - fig['data'].extend(fig_decreasing['data']) - - py.iplot(fig, filename='finance/aapl-ohlc-colors', validate=False) - ``` - - Example 4: OHLC chart with datetime objects - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - from datetime import datetime - - # Add data - open_data = [33.0, 33.3, 33.5, 33.0, 34.1] - high_data = [33.1, 33.3, 33.6, 33.2, 34.8] - low_data = [32.7, 32.7, 32.8, 32.6, 32.8] - close_data = [33.0, 32.9, 33.3, 33.1, 33.1] - dates = [datetime(year=2013, month=10, day=10), - datetime(year=2013, month=11, day=10), - datetime(year=2013, month=12, day=10), - datetime(year=2014, month=1, day=10), - datetime(year=2014, month=2, day=10)] - - # Create ohlc - fig = FF.create_ohlc(open_data, high_data, - low_data, close_data, dates=dates) - - py.iplot(fig, filename='finance/simple-ohlc', validate=False) - ``` - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - if dates is not None: - FigureFactory._validate_equal_length(open, high, low, close, dates) - else: - FigureFactory._validate_equal_length(open, high, low, close) - FigureFactory._validate_ohlc(open, high, low, close, direction, - **kwargs) - - if direction is 'increasing': - ohlc_incr = FigureFactory._make_increasing_ohlc(open, high, - low, close, - dates, **kwargs) - data = [ohlc_incr] - elif direction is 'decreasing': - ohlc_decr = FigureFactory._make_decreasing_ohlc(open, high, - low, close, - dates, **kwargs) - data = [ohlc_decr] - else: - ohlc_incr = FigureFactory._make_increasing_ohlc(open, high, - low, close, - dates, **kwargs) - ohlc_decr = FigureFactory._make_decreasing_ohlc(open, high, - low, close, - dates, **kwargs) - data = [ohlc_incr, ohlc_decr] - - layout = graph_objs.Layout(xaxis=dict(zeroline=False), - hovermode='closest') - - return graph_objs.Figure(data=data, layout=layout) + def create_quiver(*args, **kwargs): + warnings.warn(FIGURE_FACTORY_DEPRECATION_WARNING) + from plotly.graph_objs.figure_factory import FigureFactory as FF + return FF.create_quiver(*args, **kwargs) @staticmethod - def _make_increasing_candle(open, high, low, close, dates, **kwargs): - """ - Makes boxplot trace for increasing candlesticks - - _make_increasing_candle() and _make_decreasing_candle separate the - increasing traces from the decreasing traces so kwargs (such as - color) can be passed separately to increasing or decreasing traces - when direction is set to 'increasing' or 'decreasing' in - FigureFactory.create_candlestick() - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing values - :param (list) dates: list of datetime objects. Default: None - :param kwargs: kwargs to be passed to increasing trace via - plotly.graph_objs.Scatter. - - :rtype (list) candle_incr_data: list of the box trace for - increasing candlesticks. - """ - increase_x, increase_y = _Candlestick( - open, high, low, close, dates, **kwargs).get_candle_increase() - - if 'line' in kwargs: - kwargs.setdefault('fillcolor', kwargs['line']['color']) - else: - kwargs.setdefault('fillcolor', _DEFAULT_INCREASING_COLOR) - if 'name' in kwargs: - kwargs.setdefault('showlegend', True) - else: - kwargs.setdefault('showlegend', False) - kwargs.setdefault('name', 'Increasing') - kwargs.setdefault('line', dict(color=_DEFAULT_INCREASING_COLOR)) - - candle_incr_data = dict(type='box', - x=increase_x, - y=increase_y, - whiskerwidth=0, - boxpoints=False, - **kwargs) - - return [candle_incr_data] - - @staticmethod - def _make_decreasing_candle(open, high, low, close, dates, **kwargs): - """ - Makes boxplot trace for decreasing candlesticks - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing values - :param (list) dates: list of datetime objects. Default: None - :param kwargs: kwargs to be passed to decreasing trace via - plotly.graph_objs.Scatter. - - :rtype (list) candle_decr_data: list of the box trace for - decreasing candlesticks. - """ - - decrease_x, decrease_y = _Candlestick( - open, high, low, close, dates, **kwargs).get_candle_decrease() - - if 'line' in kwargs: - kwargs.setdefault('fillcolor', kwargs['line']['color']) - else: - kwargs.setdefault('fillcolor', _DEFAULT_DECREASING_COLOR) - kwargs.setdefault('showlegend', False) - kwargs.setdefault('line', dict(color=_DEFAULT_DECREASING_COLOR)) - kwargs.setdefault('name', 'Decreasing') - - candle_decr_data = dict(type='box', - x=decrease_x, - y=decrease_y, - whiskerwidth=0, - boxpoints=False, - **kwargs) - - return [candle_decr_data] + def create_streamline(*args, **kwargs): + warnings.warn(FIGURE_FACTORY_DEPRECATION_WARNING) + from plotly.graph_objs.figure_factory import FigureFactory as FF + return FF.create_streamline(*args, **kwargs) @staticmethod - def create_candlestick(open, high, low, close, - dates=None, direction='both', **kwargs): - """ - BETA function that creates a candlestick chart - - :param (list) open: opening values - :param (list) high: high values - :param (list) low: low values - :param (list) close: closing values - :param (list) dates: list of datetime objects. Default: None - :param (string) direction: direction can be 'increasing', 'decreasing', - or 'both'. When the direction is 'increasing', the returned figure - consists of all candlesticks where the close value is greater than - the corresponding open value, and when the direction is - 'decreasing', the returned figure consists of all candlesticks - where the close value is less than or equal to the corresponding - open value. When the direction is 'both', both increasing and - decreasing candlesticks are returned. Default: 'both' - :param kwargs: kwargs passed through plotly.graph_objs.Scatter. - These kwargs describe other attributes about the ohlc Scatter trace - such as the color or the legend name. For more information on valid - kwargs call help(plotly.graph_objs.Scatter) - - :rtype (dict): returns a representation of candlestick chart figure. - - Example 1: Simple candlestick chart from a Pandas DataFrame - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - from datetime import datetime - - import pandas.io.data as web - - df = web.DataReader("aapl", 'yahoo', datetime(2007, 10, 1), datetime(2009, 4, 1)) - fig = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index) - py.plot(fig, filename='finance/aapl-candlestick', validate=False) - ``` - - Example 2: Add text and annotations to the candlestick chart - ``` - fig = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index) - # Update the fig - all options here: https://plot.ly/python/reference/#Layout - fig['layout'].update({ - 'title': 'The Great Recession', - 'yaxis': {'title': 'AAPL Stock'}, - 'shapes': [{ - 'x0': '2007-12-01', 'x1': '2007-12-01', - 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper', - 'line': {'color': 'rgb(30,30,30)', 'width': 1} - }], - 'annotations': [{ - 'x': '2007-12-01', 'y': 0.05, 'xref': 'x', 'yref': 'paper', - 'showarrow': False, 'xanchor': 'left', - 'text': 'Official start of the recession' - }] - }) - py.plot(fig, filename='finance/aapl-recession-candlestick', validate=False) - ``` - - Example 3: Customize the candlestick colors - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - from plotly.graph_objs import Line, Marker - from datetime import datetime - - import pandas.io.data as web - - df = web.DataReader("aapl", 'yahoo', datetime(2008, 1, 1), datetime(2009, 4, 1)) - - # Make increasing candlesticks and customize their color and name - fig_increasing = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index, - direction='increasing', name='AAPL', - marker=Marker(color='rgb(150, 200, 250)'), - line=Line(color='rgb(150, 200, 250)')) - - # Make decreasing candlesticks and customize their color and name - fig_decreasing = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index, - direction='decreasing', - marker=Marker(color='rgb(128, 128, 128)'), - line=Line(color='rgb(128, 128, 128)')) - - # Initialize the figure - fig = fig_increasing - - # Add decreasing data with .extend() - fig['data'].extend(fig_decreasing['data']) - - py.iplot(fig, filename='finance/aapl-candlestick-custom', validate=False) - ``` - - Example 4: Candlestick chart with datetime objects - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - from datetime import datetime - - # Add data - open_data = [33.0, 33.3, 33.5, 33.0, 34.1] - high_data = [33.1, 33.3, 33.6, 33.2, 34.8] - low_data = [32.7, 32.7, 32.8, 32.6, 32.8] - close_data = [33.0, 32.9, 33.3, 33.1, 33.1] - dates = [datetime(year=2013, month=10, day=10), - datetime(year=2013, month=11, day=10), - datetime(year=2013, month=12, day=10), - datetime(year=2014, month=1, day=10), - datetime(year=2014, month=2, day=10)] - - # Create ohlc - fig = FF.create_candlestick(open_data, high_data, - low_data, close_data, dates=dates) - - py.iplot(fig, filename='finance/simple-candlestick', validate=False) - ``` - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - if dates is not None: - FigureFactory._validate_equal_length(open, high, low, close, dates) - else: - FigureFactory._validate_equal_length(open, high, low, close) - FigureFactory._validate_ohlc(open, high, low, close, direction, - **kwargs) - - if direction is 'increasing': - candle_incr_data = FigureFactory._make_increasing_candle( - open, high, low, close, dates, **kwargs) - data = candle_incr_data - elif direction is 'decreasing': - candle_decr_data = FigureFactory._make_decreasing_candle( - open, high, low, close, dates, **kwargs) - data = candle_decr_data - else: - candle_incr_data = FigureFactory._make_increasing_candle( - open, high, low, close, dates, **kwargs) - candle_decr_data = FigureFactory._make_decreasing_candle( - open, high, low, close, dates, **kwargs) - data = candle_incr_data + candle_decr_data - - layout = graph_objs.Layout() - return graph_objs.Figure(data=data, layout=layout) + def create_candlestick(*args, **kwargs): + warnings.warn(FIGURE_FACTORY_DEPRECATION_WARNING) + from plotly.graph_objs.figure_factory import FigureFactory as FF + return FF.create_candlestick(*args, **kwargs) @staticmethod - def create_distplot(hist_data, group_labels, - bin_size=1., curve_type='kde', - colors=[], rug_text=[], - show_hist=True, show_curve=True, - show_rug=True): - """ - BETA function that creates a distplot similar to seaborn.distplot - - The distplot can be composed of all or any combination of the following - 3 components: (1) histogram, (2) curve: (a) kernal density estimation - or (b) normal curve, and (3) rug plot. Additionally, multiple distplots - (from multiple datasets) can be created in the same plot. - - :param (list[list]) hist_data: Use list of lists to plot multiple data - sets on the same plot. - :param (list[str]) group_labels: Names for each data set. - :param (float) bin_size: Size of histogram bins. Default = 1. - :param (str) curve_type: 'kde' or 'normal'. Default = 'kde' - :param (bool) show_hist: Add histogram to distplot? Default = True - :param (bool) show_curve: Add curve to distplot? Default = True - :param (bool) show_rug: Add rug to distplot? Default = True - :param (list[str]) colors: Colors for traces. - :param (list[list]) rug_text: Hovertext values for rug_plot, - :return (dict): Representation of a distplot figure. - - Example 1: Simple distplot of 1 data set - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - hist_data = [[1.1, 1.1, 2.5, 3.0, 3.5, - 3.5, 4.1, 4.4, 4.5, 4.5, - 5.0, 5.0, 5.2, 5.5, 5.5, - 5.5, 5.5, 5.5, 6.1, 7.0]] - - group_labels = ['distplot example'] - - fig = FF.create_distplot(hist_data, group_labels) - - url = py.plot(fig, filename='Simple distplot', validate=False) - ``` - - Example 2: Two data sets and added rug text - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - # Add histogram data - hist1_x = [0.8, 1.2, 0.2, 0.6, 1.6, - -0.9, -0.07, 1.95, 0.9, -0.2, - -0.5, 0.3, 0.4, -0.37, 0.6] - hist2_x = [0.8, 1.5, 1.5, 0.6, 0.59, - 1.0, 0.8, 1.7, 0.5, 0.8, - -0.3, 1.2, 0.56, 0.3, 2.2] - - # Group data together - hist_data = [hist1_x, hist2_x] - - group_labels = ['2012', '2013'] - - # Add text - rug_text_1 = ['a1', 'b1', 'c1', 'd1', 'e1', - 'f1', 'g1', 'h1', 'i1', 'j1', - 'k1', 'l1', 'm1', 'n1', 'o1'] - - rug_text_2 = ['a2', 'b2', 'c2', 'd2', 'e2', - 'f2', 'g2', 'h2', 'i2', 'j2', - 'k2', 'l2', 'm2', 'n2', 'o2'] - - # Group text together - rug_text_all = [rug_text_1, rug_text_2] - - # Create distplot - fig = FF.create_distplot( - hist_data, group_labels, rug_text=rug_text_all, bin_size=.2) - - # Add title - fig['layout'].update(title='Dist Plot') - - # Plot! - url = py.plot(fig, filename='Distplot with rug text', validate=False) - ``` - - Example 3: Plot with normal curve and hide rug plot - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - import numpy as np - - x1 = np.random.randn(190) - x2 = np.random.randn(200)+1 - x3 = np.random.randn(200)-1 - x4 = np.random.randn(210)+2 - - hist_data = [x1, x2, x3, x4] - group_labels = ['2012', '2013', '2014', '2015'] - - fig = FF.create_distplot( - hist_data, group_labels, curve_type='normal', - show_rug=False, bin_size=.4) - - url = py.plot(fig, filename='hist and normal curve', validate=False) - - Example 4: Distplot with Pandas - ``` - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - import numpy as np - import pandas as pd - - df = pd.DataFrame({'2012': np.random.randn(200), - '2013': np.random.randn(200)+1}) - py.iplot(FF.create_distplot([df[c] for c in df.columns], df.columns), - filename='examples/distplot with pandas', - validate=False) - ``` - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - FigureFactory._validate_distplot(hist_data, curve_type) - FigureFactory._validate_equal_length(hist_data, group_labels) - - hist = _Distplot( - hist_data, group_labels, bin_size, - curve_type, colors, rug_text, - show_hist, show_curve).make_hist() - - if curve_type == 'normal': - curve = _Distplot( - hist_data, group_labels, bin_size, - curve_type, colors, rug_text, - show_hist, show_curve).make_normal() - else: - curve = _Distplot( - hist_data, group_labels, bin_size, - curve_type, colors, rug_text, - show_hist, show_curve).make_kde() - - rug = _Distplot( - hist_data, group_labels, bin_size, - curve_type, colors, rug_text, - show_hist, show_curve).make_rug() - - data = [] - if show_hist: - data.append(hist) - if show_curve: - data.append(curve) - if show_rug: - data.append(rug) - layout = graph_objs.Layout( - barmode='overlay', - hovermode='closest', - legend=dict(traceorder='reversed'), - xaxis1=dict(domain=[0.0, 1.0], - anchor='y2', - zeroline=False), - yaxis1=dict(domain=[0.35, 1], - anchor='free', - position=0.0), - yaxis2=dict(domain=[0, 0.25], - anchor='x1', - dtick=1, - showticklabels=False)) - else: - layout = graph_objs.Layout( - barmode='overlay', - hovermode='closest', - legend=dict(traceorder='reversed'), - xaxis1=dict(domain=[0.0, 1.0], - anchor='y2', - zeroline=False), - yaxis1=dict(domain=[0., 1], - anchor='free', - position=0.0)) - - data = sum(data, []) - return graph_objs.Figure(data=data, layout=layout) - + def create_distplot(*args, **kwargs): + warnings.warn(FIGURE_FACTORY_DEPRECATION_WARNING) + from plotly.graph_objs.figure_factory import FigureFactory as FF + return FF.create_distplot(*args, **kwargs) @staticmethod - def create_dendrogram(X, orientation="bottom", labels=None, - colorscale=None): - """ - BETA function that returns a dendrogram Plotly figure object. - - :param (ndarray) X: Matrix of observations as array of arrays - :param (str) orientation: 'top', 'right', 'bottom', or 'left' - :param (list) labels: List of axis category labels(observation labels) - :param (list) colorscale: Optional colorscale for dendrogram tree - clusters - - Example 1: Simple bottom oriented dendrogram - ``` - import numpy as np - - import plotly.plotly as py - from plotly.tools import FigureFactory as FF - - X = np.random.rand(5,5) - dendro = FF.create_dendrogram(X) - py.iplot(dendro, validate=False, height=300, width=1000) - - ``` - - Example 2: Dendrogram to put on the left of the heatmap - ``` - X = np.random.rand(5,5) - names = ['Jack', 'Oxana', 'John', 'Chelsea', 'Mark'] - dendro = FF.create_dendrogram(X, orientation='right', labels=names) - - py.iplot(dendro, validate=False, height=1000, width=300) - ``` - - """ - dependencies = (_scipy_imported and _scipy__spatial_imported and - _scipy__cluster__hierarchy_imported) - - if dependencies is False: - raise ImportError("FigureFactory.create_dendrogram requires scipy, \ - scipy.spatial and scipy.hierarchy") - - s = X.shape - if len(s) != 2: - exceptions.PlotlyError("X should be 2-dimensional array.") - - dendrogram = _Dendrogram(X, orientation, labels, colorscale) - - return {'layout': dendrogram.layout, - 'data': dendrogram.data} - - -class _Quiver(FigureFactory): - """ - Refer to FigureFactory.create_quiver() for docstring - """ - def __init__(self, x, y, u, v, - scale, arrow_scale, angle, **kwargs): - try: - x = FigureFactory._flatten(x) - except exceptions.PlotlyError: - pass - - try: - y = FigureFactory._flatten(y) - except exceptions.PlotlyError: - pass - - try: - u = FigureFactory._flatten(u) - except exceptions.PlotlyError: - pass - - try: - v = FigureFactory._flatten(v) - except exceptions.PlotlyError: - pass - - self.x = x - self.y = y - self.u = u - self.v = v - self.scale = scale - self.arrow_scale = arrow_scale - self.angle = angle - self.end_x = [] - self.end_y = [] - self.scale_uv() - barb_x, barb_y = self.get_barbs() - arrow_x, arrow_y = self.get_quiver_arrows() - - def scale_uv(self): - """ - Scales u and v to avoid overlap of the arrows. - - u and v are added to x and y to get the - endpoints of the arrows so a smaller scale value will - result in less overlap of arrows. - """ - self.u = [i * self.scale for i in self.u] - self.v = [i * self.scale for i in self.v] - - def get_barbs(self): - """ - Creates x and y startpoint and endpoint pairs - - After finding the endpoint of each barb this zips startpoint and - endpoint pairs to create 2 lists: x_values for barbs and y values - for barbs - - :rtype: (list, list) barb_x, barb_y: list of startpoint and endpoint - x_value pairs separated by a None to create the barb of the arrow, - and list of startpoint and endpoint y_value pairs separated by a - None to create the barb of the arrow. - """ - self.end_x = [i + j for i, j in zip(self.x, self.u)] - self.end_y = [i + j for i, j in zip(self.y, self.v)] - empty = [None] * len(self.x) - barb_x = FigureFactory._flatten(zip(self.x, self.end_x, empty)) - barb_y = FigureFactory._flatten(zip(self.y, self.end_y, empty)) - return barb_x, barb_y - - def get_quiver_arrows(self): - """ - Creates lists of x and y values to plot the arrows - - Gets length of each barb then calculates the length of each side of - the arrow. Gets angle of barb and applies angle to each side of the - arrowhead. Next uses arrow_scale to scale the length of arrowhead and - creates x and y values for arrowhead point1 and point2. Finally x and y - values for point1, endpoint and point2s for each arrowhead are - separated by a None and zipped to create lists of x and y values for - the arrows. - - :rtype: (list, list) arrow_x, arrow_y: list of point1, endpoint, point2 - x_values separated by a None to create the arrowhead and list of - point1, endpoint, point2 y_values separated by a None to create - the barb of the arrow. - """ - dif_x = [i - j for i, j in zip(self.end_x, self.x)] - dif_y = [i - j for i, j in zip(self.end_y, self.y)] - - # Get barb lengths(default arrow length = 30% barb length) - barb_len = [None] * len(self.x) - for index in range(len(barb_len)): - barb_len[index] = math.hypot(dif_x[index], dif_y[index]) - - # Make arrow lengths - arrow_len = [None] * len(self.x) - arrow_len = [i * self.arrow_scale for i in barb_len] - - # Get barb angles - barb_ang = [None] * len(self.x) - for index in range(len(barb_ang)): - barb_ang[index] = math.atan2(dif_y[index], dif_x[index]) - - # Set angles to create arrow - ang1 = [i + self.angle for i in barb_ang] - ang2 = [i - self.angle for i in barb_ang] - - cos_ang1 = [None] * len(ang1) - for index in range(len(ang1)): - cos_ang1[index] = math.cos(ang1[index]) - seg1_x = [i * j for i, j in zip(arrow_len, cos_ang1)] - - sin_ang1 = [None] * len(ang1) - for index in range(len(ang1)): - sin_ang1[index] = math.sin(ang1[index]) - seg1_y = [i * j for i, j in zip(arrow_len, sin_ang1)] - - cos_ang2 = [None] * len(ang2) - for index in range(len(ang2)): - cos_ang2[index] = math.cos(ang2[index]) - seg2_x = [i * j for i, j in zip(arrow_len, cos_ang2)] - - sin_ang2 = [None] * len(ang2) - for index in range(len(ang2)): - sin_ang2[index] = math.sin(ang2[index]) - seg2_y = [i * j for i, j in zip(arrow_len, sin_ang2)] - - # Set coordinates to create arrow - for index in range(len(self.end_x)): - point1_x = [i - j for i, j in zip(self.end_x, seg1_x)] - point1_y = [i - j for i, j in zip(self.end_y, seg1_y)] - point2_x = [i - j for i, j in zip(self.end_x, seg2_x)] - point2_y = [i - j for i, j in zip(self.end_y, seg2_y)] - - # Combine lists to create arrow - empty = [None] * len(self.end_x) - arrow_x = FigureFactory._flatten(zip(point1_x, self.end_x, - point2_x, empty)) - arrow_y = FigureFactory._flatten(zip(point1_y, self.end_y, - point2_y, empty)) - return arrow_x, arrow_y - - -class _Streamline(FigureFactory): - """ - Refer to FigureFactory.create_streamline() for docstring - """ - def __init__(self, x, y, u, v, - density, angle, - arrow_scale, **kwargs): - self.x = np.array(x) - self.y = np.array(y) - self.u = np.array(u) - self.v = np.array(v) - self.angle = angle - self.arrow_scale = arrow_scale - self.density = int(30 * density) # Scale similarly to other functions - self.delta_x = self.x[1] - self.x[0] - self.delta_y = self.y[1] - self.y[0] - self.val_x = self.x - self.val_y = self.y - - # Set up spacing - self.blank = np.zeros((self.density, self.density)) - self.spacing_x = len(self.x) / float(self.density - 1) - self.spacing_y = len(self.y) / float(self.density - 1) - self.trajectories = [] - - # Rescale speed onto axes-coordinates - self.u = self.u / (self.x[-1] - self.x[0]) - self.v = self.v / (self.y[-1] - self.y[0]) - self.speed = np.sqrt(self.u ** 2 + self.v ** 2) - - # Rescale u and v for integrations. - self.u *= len(self.x) - self.v *= len(self.y) - self.st_x = [] - self.st_y = [] - self.get_streamlines() - streamline_x, streamline_y = self.sum_streamlines() - arrows_x, arrows_y = self.get_streamline_arrows() - - def blank_pos(self, xi, yi): - """ - Set up positions for trajectories to be used with rk4 function. - """ - return (int((xi / self.spacing_x) + 0.5), - int((yi / self.spacing_y) + 0.5)) - - def value_at(self, a, xi, yi): - """ - Set up for RK4 function, based on Bokeh's streamline code - """ - if isinstance(xi, np.ndarray): - self.x = xi.astype(np.int) - self.y = yi.astype(np.int) - else: - self.val_x = np.int(xi) - self.val_y = np.int(yi) - a00 = a[self.val_y, self.val_x] - a01 = a[self.val_y, self.val_x + 1] - a10 = a[self.val_y + 1, self.val_x] - a11 = a[self.val_y + 1, self.val_x + 1] - xt = xi - self.val_x - yt = yi - self.val_y - a0 = a00 * (1 - xt) + a01 * xt - a1 = a10 * (1 - xt) + a11 * xt - return a0 * (1 - yt) + a1 * yt - - def rk4_integrate(self, x0, y0): - """ - RK4 forward and back trajectories from the initial conditions. - - Adapted from Bokeh's streamline -uses Runge-Kutta method to fill - x and y trajectories then checks length of traj (s in units of axes) - """ - def f(xi, yi): - dt_ds = 1. / self.value_at(self.speed, xi, yi) - ui = self.value_at(self.u, xi, yi) - vi = self.value_at(self.v, xi, yi) - return ui * dt_ds, vi * dt_ds - - def g(xi, yi): - dt_ds = 1. / self.value_at(self.speed, xi, yi) - ui = self.value_at(self.u, xi, yi) - vi = self.value_at(self.v, xi, yi) - return -ui * dt_ds, -vi * dt_ds - - check = lambda xi, yi: (0 <= xi < len(self.x) - 1 and - 0 <= yi < len(self.y) - 1) - xb_changes = [] - yb_changes = [] - - def rk4(x0, y0, f): - ds = 0.01 - stotal = 0 - xi = x0 - yi = y0 - xb, yb = self.blank_pos(xi, yi) - xf_traj = [] - yf_traj = [] - while check(xi, yi): - xf_traj.append(xi) - yf_traj.append(yi) - try: - k1x, k1y = f(xi, yi) - k2x, k2y = f(xi + .5 * ds * k1x, yi + .5 * ds * k1y) - k3x, k3y = f(xi + .5 * ds * k2x, yi + .5 * ds * k2y) - k4x, k4y = f(xi + ds * k3x, yi + ds * k3y) - except IndexError: - break - xi += ds * (k1x + 2 * k2x + 2 * k3x + k4x) / 6. - yi += ds * (k1y + 2 * k2y + 2 * k3y + k4y) / 6. - if not check(xi, yi): - break - stotal += ds - new_xb, new_yb = self.blank_pos(xi, yi) - if new_xb != xb or new_yb != yb: - if self.blank[new_yb, new_xb] == 0: - self.blank[new_yb, new_xb] = 1 - xb_changes.append(new_xb) - yb_changes.append(new_yb) - xb = new_xb - yb = new_yb - else: - break - if stotal > 2: - break - return stotal, xf_traj, yf_traj - - sf, xf_traj, yf_traj = rk4(x0, y0, f) - sb, xb_traj, yb_traj = rk4(x0, y0, g) - stotal = sf + sb - x_traj = xb_traj[::-1] + xf_traj[1:] - y_traj = yb_traj[::-1] + yf_traj[1:] - - if len(x_traj) < 1: - return None - if stotal > .2: - initxb, inityb = self.blank_pos(x0, y0) - self.blank[inityb, initxb] = 1 - return x_traj, y_traj - else: - for xb, yb in zip(xb_changes, yb_changes): - self.blank[yb, xb] = 0 - return None - - def traj(self, xb, yb): - """ - Integrate trajectories - - :param (int) xb: results of passing xi through self.blank_pos - :param (int) xy: results of passing yi through self.blank_pos - - Calculate each trajectory based on rk4 integrate method. - """ - - if xb < 0 or xb >= self.density or yb < 0 or yb >= self.density: - return - if self.blank[yb, xb] == 0: - t = self.rk4_integrate(xb * self.spacing_x, yb * self.spacing_y) - if t is not None: - self.trajectories.append(t) - - def get_streamlines(self): - """ - Get streamlines by building trajectory set. - """ - for indent in range(self.density // 2): - for xi in range(self.density - 2 * indent): - self.traj(xi + indent, indent) - self.traj(xi + indent, self.density - 1 - indent) - self.traj(indent, xi + indent) - self.traj(self.density - 1 - indent, xi + indent) - - self.st_x = [np.array(t[0]) * self.delta_x + self.x[0] for t in - self.trajectories] - self.st_y = [np.array(t[1]) * self.delta_y + self.y[0] for t in - self.trajectories] - - for index in range(len(self.st_x)): - self.st_x[index] = self.st_x[index].tolist() - self.st_x[index].append(np.nan) - - for index in range(len(self.st_y)): - self.st_y[index] = self.st_y[index].tolist() - self.st_y[index].append(np.nan) - - def get_streamline_arrows(self): - """ - Makes an arrow for each streamline. - - Gets angle of streamline at 1/3 mark and creates arrow coordinates - based off of user defined angle and arrow_scale. - - :param (array) st_x: x-values for all streamlines - :param (array) st_y: y-values for all streamlines - :param (angle in radians) angle: angle of arrowhead. Default = pi/9 - :param (float in [0,1]) arrow_scale: value to scale length of arrowhead - Default = .09 - :rtype (list, list) arrows_x: x-values to create arrowhead and - arrows_y: y-values to create arrowhead - """ - arrow_end_x = np.empty((len(self.st_x))) - arrow_end_y = np.empty((len(self.st_y))) - arrow_start_x = np.empty((len(self.st_x))) - arrow_start_y = np.empty((len(self.st_y))) - for index in range(len(self.st_x)): - arrow_end_x[index] = (self.st_x[index] - [int(len(self.st_x[index]) / 3)]) - arrow_start_x[index] = (self.st_x[index] - [(int(len(self.st_x[index]) / 3)) - 1]) - arrow_end_y[index] = (self.st_y[index] - [int(len(self.st_y[index]) / 3)]) - arrow_start_y[index] = (self.st_y[index] - [(int(len(self.st_y[index]) / 3)) - 1]) - - dif_x = arrow_end_x - arrow_start_x - dif_y = arrow_end_y - arrow_start_y - - streamline_ang = np.arctan(dif_y / dif_x) - - ang1 = streamline_ang + (self.angle) - ang2 = streamline_ang - (self.angle) - - seg1_x = np.cos(ang1) * self.arrow_scale - seg1_y = np.sin(ang1) * self.arrow_scale - seg2_x = np.cos(ang2) * self.arrow_scale - seg2_y = np.sin(ang2) * self.arrow_scale - - point1_x = np.empty((len(dif_x))) - point1_y = np.empty((len(dif_y))) - point2_x = np.empty((len(dif_x))) - point2_y = np.empty((len(dif_y))) - - for index in range(len(dif_x)): - if dif_x[index] >= 0: - point1_x[index] = arrow_end_x[index] - seg1_x[index] - point1_y[index] = arrow_end_y[index] - seg1_y[index] - point2_x[index] = arrow_end_x[index] - seg2_x[index] - point2_y[index] = arrow_end_y[index] - seg2_y[index] - else: - point1_x[index] = arrow_end_x[index] + seg1_x[index] - point1_y[index] = arrow_end_y[index] + seg1_y[index] - point2_x[index] = arrow_end_x[index] + seg2_x[index] - point2_y[index] = arrow_end_y[index] + seg2_y[index] - - space = np.empty((len(point1_x))) - space[:] = np.nan - - # Combine arrays into matrix - arrows_x = np.matrix([point1_x, arrow_end_x, point2_x, space]) - arrows_x = np.array(arrows_x) - arrows_x = arrows_x.flatten('F') - arrows_x = arrows_x.tolist() - - # Combine arrays into matrix - arrows_y = np.matrix([point1_y, arrow_end_y, point2_y, space]) - arrows_y = np.array(arrows_y) - arrows_y = arrows_y.flatten('F') - arrows_y = arrows_y.tolist() - - return arrows_x, arrows_y - - def sum_streamlines(self): - """ - Makes all streamlines readable as a single trace. - - :rtype (list, list): streamline_x: all x values for each streamline - combined into single list and streamline_y: all y values for each - streamline combined into single list - """ - streamline_x = sum(self.st_x, []) - streamline_y = sum(self.st_y, []) - return streamline_x, streamline_y - -class _OHLC(FigureFactory): - """ - Refer to FigureFactory.create_ohlc_increase() for docstring. - """ - def __init__(self, open, high, low, close, dates, **kwargs): - self.open = open - self.high = high - self.low = low - self.close = close - self.empty = [None] * len(open) - self.dates = dates - - self.all_x = [] - self.all_y = [] - self.increase_x = [] - self.increase_y = [] - self.decrease_x = [] - self.decrease_y = [] - self.get_all_xy() - self.separate_increase_decrease() - - def get_all_xy(self): - """ - Zip data to create OHLC shape - - OHLC shape: low to high vertical bar with - horizontal branches for open and close values. - If dates were added, the smallest date difference is calculated and - multiplied by .2 to get the length of the open and close branches. - If no date data was provided, the x-axis is a list of integers and the - length of the open and close branches is .2. - """ - self.all_y = list(zip(self.open, self.open, self.high, - self.low, self.close, self.close, self.empty)) - if self.dates is not None: - date_dif = [] - for i in range(len(self.dates) - 1): - date_dif.append(self.dates[i + 1] - self.dates[i]) - date_dif_min = (min(date_dif)) / 5 - self.all_x = [[x - date_dif_min, x, x, x, x, x + - date_dif_min, None] for x in self.dates] - else: - self.all_x = [[x - .2, x, x, x, x, x + .2, None] - for x in range(len(self.open))] - - def separate_increase_decrease(self): - """ - Separate data into two groups: increase and decrease - - (1) Increase, where close > open and - (2) Decrease, where close <= open - """ - for index in range(len(self.open)): - if self.close[index] is None: - pass - elif self.close[index] > self.open[index]: - self.increase_x.append(self.all_x[index]) - self.increase_y.append(self.all_y[index]) - else: - self.decrease_x.append(self.all_x[index]) - self.decrease_y.append(self.all_y[index]) - - def get_increase(self): - """ - Flatten increase data and get increase text - - :rtype (list, list, list): flat_increase_x: x-values for the increasing - trace, flat_increase_y: y=values for the increasing trace and - text_increase: hovertext for the increasing trace - """ - flat_increase_x = FigureFactory._flatten(self.increase_x) - flat_increase_y = FigureFactory._flatten(self.increase_y) - text_increase = (("Open", "Open", "High", - "Low", "Close", "Close", '') - * (len(self.increase_x))) - - return flat_increase_x, flat_increase_y, text_increase - - def get_decrease(self): - """ - Flatten decrease data and get decrease text - - :rtype (list, list, list): flat_decrease_x: x-values for the decreasing - trace, flat_decrease_y: y=values for the decreasing trace and - text_decrease: hovertext for the decreasing trace - """ - flat_decrease_x = FigureFactory._flatten(self.decrease_x) - flat_decrease_y = FigureFactory._flatten(self.decrease_y) - text_decrease = (("Open", "Open", "High", - "Low", "Close", "Close", '') - * (len(self.decrease_x))) - - return flat_decrease_x, flat_decrease_y, text_decrease - - -class _Candlestick(FigureFactory): - """ - Refer to FigureFactory.create_candlestick() for docstring. - """ - def __init__(self, open, high, low, close, dates, **kwargs): - self.open = open - self.high = high - self.low = low - self.close = close - if dates is not None: - self.x = dates - else: - self.x = [x for x in range(len(self.open))] - self.get_candle_increase() - - def get_candle_increase(self): - """ - Separate increasing data from decreasing data. - - The data is increasing when close value > open value - and decreasing when the close value <= open value. - """ - increase_y = [] - increase_x = [] - for index in range(len(self.open)): - if self.close[index] > self.open[index]: - increase_y.append(self.low[index]) - increase_y.append(self.open[index]) - increase_y.append(self.close[index]) - increase_y.append(self.close[index]) - increase_y.append(self.close[index]) - increase_y.append(self.high[index]) - increase_x.append(self.x[index]) - - increase_x = [[x, x, x, x, x, x] for x in increase_x] - increase_x = FigureFactory._flatten(increase_x) - - return increase_x, increase_y - - def get_candle_decrease(self): - """ - Separate increasing data from decreasing data. - - The data is increasing when close value > open value - and decreasing when the close value <= open value. - """ - decrease_y = [] - decrease_x = [] - for index in range(len(self.open)): - if self.close[index] <= self.open[index]: - decrease_y.append(self.low[index]) - decrease_y.append(self.open[index]) - decrease_y.append(self.close[index]) - decrease_y.append(self.close[index]) - decrease_y.append(self.close[index]) - decrease_y.append(self.high[index]) - decrease_x.append(self.x[index]) - - decrease_x = [[x, x, x, x, x, x] for x in decrease_x] - decrease_x = FigureFactory._flatten(decrease_x) - - return decrease_x, decrease_y - - -class _Distplot(FigureFactory): - """ - Refer to TraceFactory.create_distplot() for docstring - """ - def __init__(self, hist_data, group_labels, - bin_size, curve_type, colors, - rug_text, show_hist, show_curve): - self.hist_data = hist_data - self.group_labels = group_labels - self.bin_size = bin_size - self.show_hist = show_hist - self.show_curve = show_curve - self.trace_number = len(hist_data) - if rug_text: - self.rug_text = rug_text - else: - self.rug_text = [None] * self.trace_number - - self.start = [] - self.end = [] - if colors: - self.colors = colors - else: - self.colors = [ - "rgb(31, 119, 180)", "rgb(255, 127, 14)", - "rgb(44, 160, 44)", "rgb(214, 39, 40)", - "rgb(148, 103, 189)", "rgb(140, 86, 75)", - "rgb(227, 119, 194)", "rgb(127, 127, 127)", - "rgb(188, 189, 34)", "rgb(23, 190, 207)"] - self.curve_x = [None] * self.trace_number - self.curve_y = [None] * self.trace_number - - for trace in self.hist_data: - self.start.append(min(trace) * 1.) - self.end.append(max(trace) * 1.) - - def make_hist(self): - """ - Makes the histogram(s) for FigureFactory.create_distplot(). - - :rtype (list) hist: list of histogram representations - """ - hist = [None] * self.trace_number - - for index in range(self.trace_number): - hist[index] = dict(type='histogram', - x=self.hist_data[index], - xaxis='x1', - yaxis='y1', - histnorm='probability', - name=self.group_labels[index], - legendgroup=self.group_labels[index], - marker=dict(color=self.colors[index]), - autobinx=False, - xbins=dict(start=self.start[index], - end=self.end[index], - size=self.bin_size), - opacity=.7) - return hist - - def make_kde(self): - """ - Makes the kernal density estimation(s) for create_distplot(). - - This is called when curve_type = 'kde' in create_distplot(). - - :rtype (list) curve: list of kde representations - """ - curve = [None] * self.trace_number - for index in range(self.trace_number): - self.curve_x[index] = [self.start[index] + - x * (self.end[index] - self.start[index]) - / 500 for x in range(500)] - self.curve_y[index] = (scipy.stats.gaussian_kde - (self.hist_data[index]) - (self.curve_x[index])) - self.curve_y[index] *= self.bin_size - - for index in range(self.trace_number): - curve[index] = dict(type='scatter', - x=self.curve_x[index], - y=self.curve_y[index], - xaxis='x1', - yaxis='y1', - mode='lines', - name=self.group_labels[index], - legendgroup=self.group_labels[index], - showlegend=False if self.show_hist else True, - marker=dict(color=self.colors[index])) - return curve - - def make_normal(self): - """ - Makes the normal curve(s) for create_distplot(). - - This is called when curve_type = 'normal' in create_distplot(). - - :rtype (list) curve: list of normal curve representations - """ - curve = [None] * self.trace_number - mean = [None] * self.trace_number - sd = [None] * self.trace_number - - for index in range(self.trace_number): - mean[index], sd[index] = (scipy.stats.norm.fit - (self.hist_data[index])) - self.curve_x[index] = [self.start[index] + - x * (self.end[index] - self.start[index]) - / 500 for x in range(500)] - self.curve_y[index] = scipy.stats.norm.pdf( - self.curve_x[index], loc=mean[index], scale=sd[index]) - self.curve_y[index] *= self.bin_size - - for index in range(self.trace_number): - curve[index] = dict(type='scatter', - x=self.curve_x[index], - y=self.curve_y[index], - xaxis='x1', - yaxis='y1', - mode='lines', - name=self.group_labels[index], - legendgroup=self.group_labels[index], - showlegend=False if self.show_hist else True, - marker=dict(color=self.colors[index])) - return curve - - def make_rug(self): - """ - Makes the rug plot(s) for create_distplot(). - - :rtype (list) rug: list of rug plot representations - """ - rug = [None] * self.trace_number - for index in range(self.trace_number): - - rug[index] = dict(type='scatter', - x=self.hist_data[index], - y=([self.group_labels[index]] * - len(self.hist_data[index])), - xaxis='x1', - yaxis='y2', - mode='markers', - name=self.group_labels[index], - legendgroup=self.group_labels[index], - showlegend=(False if self.show_hist or - self.show_curve else True), - text=self.rug_text[index], - marker=dict(color=self.colors[index], - symbol='line-ns-open')) - return rug - - -class _Dendrogram(FigureFactory): - """Refer to FigureFactory.create_dendrogram() for docstring.""" - - def __init__(self, X, orientation='bottom', labels=None, colorscale=None, - width="100%", height="100%", xaxis='xaxis', yaxis='yaxis'): - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - self.orientation = orientation - self.labels = labels - self.xaxis = xaxis - self.yaxis = yaxis - self.data = [] - self.leaves = [] - self.sign = {self.xaxis: 1, self.yaxis: 1} - self.layout = {self.xaxis: {}, self.yaxis: {}} - - if self.orientation in ['left', 'bottom']: - self.sign[self.xaxis] = 1 - else: - self.sign[self.xaxis] = -1 - - if self.orientation in ['right', 'bottom']: - self.sign[self.yaxis] = 1 - else: - self.sign[self.yaxis] = -1 - - (dd_traces, xvals, yvals, - ordered_labels, leaves) = self.get_dendrogram_traces(X, colorscale) - - self.labels = ordered_labels - self.leaves = leaves - yvals_flat = yvals.flatten() - xvals_flat = xvals.flatten() - - self.zero_vals = [] - - for i in range(len(yvals_flat)): - if yvals_flat[i] == 0.0 and xvals_flat[i] not in self.zero_vals: - self.zero_vals.append(xvals_flat[i]) - - self.zero_vals.sort() - - self.layout = self.set_figure_layout(width, height) - self.data = graph_objs.Data(dd_traces) - - def get_color_dict(self, colorscale): - """ - Returns colorscale used for dendrogram tree clusters. - - :param (list) colorscale: Colors to use for the plot in rgb format. - :rtype (dict): A dict of default colors mapped to the user colorscale. - - """ - - # These are the color codes returned for dendrograms - # We're replacing them with nicer colors - d = {'r': 'red', - 'g': 'green', - 'b': 'blue', - 'c': 'cyan', - 'm': 'magenta', - 'y': 'yellow', - 'k': 'black', - 'w': 'white'} - default_colors = OrderedDict(sorted(d.items(), key=lambda t: t[0])) - - if colorscale is None: - colorscale = [ - 'rgb(0,116,217)', # blue - 'rgb(35,205,205)', # cyan - 'rgb(61,153,112)', # green - 'rgb(40,35,35)', # black - 'rgb(133,20,75)', # magenta - 'rgb(255,65,54)', # red - 'rgb(255,255,255)', # white - 'rgb(255,220,0)'] # yellow - - for i in range(len(default_colors.keys())): - k = list(default_colors.keys())[i] # PY3 won't index keys - if i < len(colorscale): - default_colors[k] = colorscale[i] - - return default_colors - - def set_axis_layout(self, axis_key): - """ - Sets and returns default axis object for dendrogram figure. - - :param (str) axis_key: E.g., 'xaxis', 'xaxis1', 'yaxis', yaxis1', etc. - :rtype (dict): An axis_key dictionary with set parameters. - - """ - axis_defaults = { - 'type': 'linear', - 'ticks': 'outside', - 'mirror': 'allticks', - 'rangemode': 'tozero', - 'showticklabels': True, - 'zeroline': False, - 'showgrid': False, - 'showline': True, - } - - if len(self.labels) != 0: - axis_key_labels = self.xaxis - if self.orientation in ['left', 'right']: - axis_key_labels = self.yaxis - if axis_key_labels not in self.layout: - self.layout[axis_key_labels] = {} - self.layout[axis_key_labels]['tickvals'] = \ - [zv*self.sign[axis_key] for zv in self.zero_vals] - self.layout[axis_key_labels]['ticktext'] = self.labels - self.layout[axis_key_labels]['tickmode'] = 'array' - - self.layout[axis_key].update(axis_defaults) - - return self.layout[axis_key] - - def set_figure_layout(self, width, height): - """ - Sets and returns default layout object for dendrogram figure. - - """ - self.layout.update({ - 'showlegend': False, - 'autosize': False, - 'hovermode': 'closest', - 'width': width, - 'height': height - }) - - self.set_axis_layout(self.xaxis) - self.set_axis_layout(self.yaxis) - - return self.layout - - def get_dendrogram_traces(self, X, colorscale): - """ - Calculates all the elements needed for plotting a dendrogram. - - :param (ndarray) X: Matrix of observations as array of arrays - :param (list) colorscale: Color scale for dendrogram tree clusters - :rtype (tuple): Contains all the traces in the following order: - (a) trace_list: List of Plotly trace objects for dendrogram tree - (b) icoord: All X points of the dendrogram tree as array of arrays - with length 4 - (c) dcoord: All Y points of the dendrogram tree as array of arrays - with length 4 - (d) ordered_labels: leaf labels in the order they are going to - appear on the plot - (e) P['leaves']: left-to-right traversal of the leaves - - """ - # TODO: protected until #282 - from plotly.graph_objs import graph_objs - d = scs.distance.pdist(X) - Z = sch.linkage(d, method='complete') - P = sch.dendrogram(Z, orientation=self.orientation, - labels=self.labels, no_plot=True) - - icoord = scp.array(P['icoord']) - dcoord = scp.array(P['dcoord']) - ordered_labels = scp.array(P['ivl']) - color_list = scp.array(P['color_list']) - colors = self.get_color_dict(colorscale) - - trace_list = [] - - for i in range(len(icoord)): - # xs and ys are arrays of 4 points that make up the '∩' shapes - # of the dendrogram tree - if self.orientation in ['top', 'bottom']: - xs = icoord[i] - else: - xs = dcoord[i] - - if self.orientation in ['top', 'bottom']: - ys = dcoord[i] - else: - ys = icoord[i] - color_key = color_list[i] - trace = graph_objs.Scatter( - x=np.multiply(self.sign[self.xaxis], xs), - y=np.multiply(self.sign[self.yaxis], ys), - mode='lines', - marker=graph_objs.Marker(color=colors[color_key]) - ) - - try: - x_index = int(self.xaxis[-1]) - except ValueError: - x_index = '' - - try: - y_index = int(self.yaxis[-1]) - except ValueError: - y_index = '' - - trace['xaxis'] = 'x' + x_index - trace['yaxis'] = 'y' + y_index - - trace_list.append(trace) - - return trace_list, icoord, dcoord, ordered_labels, P['leaves'] + def create_dendrogram(*args, **kwargs): + warnings.warn(FIGURE_FACTORY_DEPRECATION_WARNING) + from plotly.graph_objs.figure_factory import FigureFactory as FF + return FF.create_dendrogram(*args, **kwargs) diff --git a/plotly/utils.py b/plotly/utils.py index 841d9a9d305..33b10300f19 100644 --- a/plotly/utils.py +++ b/plotly/utils.py @@ -15,26 +15,8 @@ import pytz - -from . exceptions import PlotlyError - -try: - import numpy - _numpy_imported = True -except ImportError: - _numpy_imported = False - -try: - import pandas - _pandas_imported = True -except ImportError: - _pandas_imported = False - -try: - import sage.all - _sage_imported = True -except ImportError: - _sage_imported = False +from plotly import optional_imports +from plotly.exceptions import PlotlyError ### incase people are using threading, we lock file reads @@ -229,7 +211,8 @@ def encode_as_list(obj): @staticmethod def encode_as_sage(obj): """Attempt to convert sage.all.RR to floats and sage.all.ZZ to ints""" - if not _sage_imported: + sage = optional_imports.get_module('sage') + if not sage: raise NotEncodable if obj in sage.all.RR: @@ -242,7 +225,8 @@ def encode_as_sage(obj): @staticmethod def encode_as_pandas(obj): """Attempt to convert pandas.NaT""" - if not _pandas_imported: + pandas = optional_imports.get_module('pandas') + if not pandas: raise NotEncodable if obj is pandas.NaT: @@ -253,7 +237,8 @@ def encode_as_pandas(obj): @staticmethod def encode_as_numpy(obj): """Attempt to convert numpy.ma.core.masked""" - if not _numpy_imported: + numpy = optional_imports.get_module('numpy') + if not numpy: raise NotEncodable if obj is numpy.ma.core.masked: