# Let's begin by importing some libraries we'll need
import numpy as np
from __future__ import print_function # So that this notebook becomes both Python 2 and Python 3 compatible

# And creating some random data
size = 100
np.random.seed(0)
x_data = np.arange(size)
y_data = np.cumsum(np.random.randn(size)  * 100.0)

"""
Explanation: bqplot
This notebook is meant to guide you through the first stages of using the bqplot visualization library. bqplot is a Grammar of Graphics based interactive visualization library for the Jupyter notebook where every single component of a plot is an interactive iPython widget. What this means is that even after a plot is drawn, you can change almost any aspect of it. This makes the creation of advanced Graphical User Interfaces attainable through just a few simple lines of Python code.
End of explanation
"""


from bqplot import pyplot as plt

"""
Explanation: Your First Plot
Let's start by creating a simple Line chart. bqplot has two different APIs, the first one is a matplotlib inspired simple API called pyplot. So let's import that.
End of explanation
"""


plt.figure(title='My First Plot')
plt.plot(x_data, y_data)
plt.show()

"""
Explanation: Let's plot y_data against x_data, and then show the plot.
End of explanation
"""


# Creating a new Figure and setting it's title
plt.figure(title='My Second Chart')

# Let's assign the scatter plot to a variable
scatter_plot = plt.scatter(x_data, y_data)

# Let's show the plot
plt.show()

"""
Explanation: Use the buttons above to Pan (or Zoom), Reset or save the Figure.
Using bqplot's interactive elements
Now, let's try creating a new plot. First, we create a brand new Figure. The Figure is the final element of any plot that is eventually displayed. You can think of it as a Canvas on which we put all of our other plots.
End of explanation
"""


scatter_plot.y = np.cumsum(np.random.randn(size)  * 100.0)

"""
Explanation: Since both the x and the y attributes of a bqplot chart are interactive widgets, we can change them. So, let's 
change the y attribute of the chart.
End of explanation
"""


# Say, the color
scatter_plot.colors = ['Red']

# Or, the marker style
scatter_plot.marker = 'diamond'

"""
Explanation: Re-run the above cell a few times, the same plot should update everytime. But, thats not the only thing that can be changed once a plot has been rendered. Let's try changing some of the other attributes.
End of explanation
"""


def foo(change):
    print('This is a trait change. Foo was called by the fact that we moved the Scatter')
    print('In fact, the Scatter plot sent us all the new data: ')
    print('To access the data, try modifying the function and printing the data variable')

"""
Explanation: It's important to remember that an interactive widget means that the JavaScript and the Python communicate. So, the plot can be changed through a single line of python code, or a piece of python code can be triggered by a change in the plot. Let's go through a simple example. Say we have a function foo:
End of explanation
"""


# First, we hook up our function `foo` to the colors attribute (or Trait) of the scatter plot
scatter_plot.observe(foo, 'y')

"""
Explanation: We can call foo everytime any attribute of our scatter is changed. Say, the y values:
End of explanation
"""


scatter_plot.enable_move = True

"""
Explanation: To allow the points in the Scatter to be moved interactively, we set the enable_move attribute to True
End of explanation
"""


# First, we import the scales
from bqplot import LinearScale

# Let's create a scale for the x attribute, and a scale for the y attribute
x_sc = LinearScale()
y_sc = LinearScale()

"""
Explanation: Go ahead, head over to the chart and move any point in some way. This move (which happens on the JavaScript side should trigger our Python function foo.
Understanding how bqplot uses the Grammar of Graphics paradigm
bqplot has two different APIs. One is the matplotlib inspired pyplot which we used above (you can think of it as similar to qplot in ggplot2). The other one, the verbose API, is meant to expose every element of a plot individually, so that their attriutes can be controlled in an atomic way. In order to truly use bqplot to build complex and feature-rich GUIs, it pays to understand the underlying theory that is used to create a plot.
To understand this verbose API, it helps to revisit what exactly the components of a plot are. The first thing we need is a Scale.
A Scale is a mapping from (function that converts) data coordinates to figure coordinates. What this means is that, a Scale takes a set of values in any arbitrary unit (say number of people, or $, or litres) and converts it to pixels (or colors for a ColorScale).
End of explanation
"""


from bqplot import Scatter

scatter_chart = Scatter(x=x_data, y=y_data, scales={'x': x_sc, 'y': y_sc})

"""
Explanation: Now, we need to create the actual Mark that will visually represent the data. Let's pick a Scatter chart to start.
End of explanation
"""


from bqplot import Axis

x_ax = Axis(label='X', scale=x_sc)
y_ax = Axis(label='Y', scale=y_sc, orientation='vertical')

"""
Explanation: Most of the time, the actual Figure co-ordinates don't really mean anything to us. So, what we need is the visual representation of our Scale, which is called an Axis.
End of explanation
"""


from bqplot import Figure

fig = Figure(marks=[scatter_chart], title='A Figure', axes=[x_ax, y_ax])
fig

"""
Explanation: And finally, we put it all together on a canvas, which is called a Figure.
End of explanation
"""


from IPython.display import display

display(fig)

"""
Explanation: The IPython display machinery displays the last returned value of a cell. If you wish to explicitly display a widget, you can call IPython.display.display.
End of explanation
"""


# First, we generate some random color data.
color_data = np.random.randint(0, 2, size=100)

"""
Explanation: Now, that the plot has been generated, we can control every single attribute of it. Let's say we wanted to color the chart based on some other data.
End of explanation
"""


from bqplot import ColorScale

# The colors trait controls the actual colors we want to map to. It can also take a min, mid, max list of
# colors to be interpolated between for continuous data.
col_sc = ColorScale(colors=['MediumSeaGreen', 'Red'])

scatter_chart.scales = {'x': x_sc, 'y': y_sc, 'color': col_sc}
# We pass the color data to the Scatter Chart through it's color attribute
scatter_chart.color = color_data

"""
Explanation: Now, we define a ColorScale to map the color_data to actual colors
End of explanation
"""


from bqplot import Bars

new_size = 50
scale = 100.
x_data_new = np.arange(new_size)
y_data_new = np.cumsum(np.random.randn(new_size)  * scale)

# All we need to do to add a bar chart to the Figure is pass the same scales to the Mark
bar_chart = Bars(x=x_data_new, y=y_data_new, scales={'x': x_sc, 'y': y_sc})

"""
Explanation: The grammar of graphics framework allows us to overlay multiple visualizations on a single Figure by having the visualization share the Scales. So, for example, if we had a Bar chart that we would like to plot alongside the Scatter plot, we just pass it the same Scales.
End of explanation
"""


fig.marks = [scatter_chart, bar_chart]

"""
Explanation: Finally, we add the new Mark to the Figure to update the plot!
End of explanation
"""


