Skip to content

Commit

Permalink
Matplotlib rendering option (#198)
Browse files Browse the repository at this point in the history
* update export tutorial to add explanation for standalone argument

* minor fixes and remove cell output in notebooks

* added contributing doc

* fix bugs and uncomment some tests

* remove raise warning

* remove unnecessary import

* split up rename test into two parts

* fix setting warning, fix data_type bugs and add relevant tests

* remove ordinal data type

* add test for small dataframe resetting index

* add loc and iloc tests

* fix attribute access directly to dataframe

* add small changes to code

* added test for qcut and cut

* add check if dtype is Interval

* added qcut test

* fix Record KeyError

* add tests

* take care of reset_index case

* small edits

* add data_model to column_group Clause

* small edits for row_group

* fixes to row group

* basic matplotlib chart example

* add config for start and cap for samples

* finish sampling config and tests

* black formatting

* add documentation for sampling config

* remove small added issues

* minor changes to docs

* migrate register default action to init

* config class

* oop png

* implement heatmap flag and add tests

* black formatting and documentation edits

* all except hist

* hist works

* heatmap

* only axis

* added code

* heatmap works

* heatmap works

* bar chart lim

* everything works

* add global var

* ui refine

* adjust comments

* black reformat

* add pd.io equalities for DataFrames

* black reformat

* matplotlib tests

* reformat and req

* add one default test

* black format

* removed .coverage; fixed typos; add nan import; add fig display statement

* doc and font size

* remove ipy

* color and check code not yet

* remove print

* all outputs

* update docs

* color channels work

* add titles

* rm

* all exports work

* add tests

* increase size

* leg size

* reformat

* abbrev axis

* remove bin

* no warning

* improve ui

* init for runtime

* merge

* plot config matplotlib

* add documentation

* close fig to save mem

* small change

* mem

* split tests

* reformat

* font

* ui changes

* make scatter narrow

* matplotlib abbrev

* matplotlib abbrev

Co-authored-by: Kunal Agarwal <kagarwal2@berkeley.edu>
Co-authored-by: Doris Lee <dorisjunglinlee@gmail.com>
Co-authored-by: Kunal Agarwal <32151899+westernguy2@users.noreply.github.com>
Co-authored-by: Caitlyn Chen <caitlynachen@berkeley.edu>
Co-authored-by: Ujjaini Mukhopadhyay <ujjaini@berkeley.edu>
  • Loading branch information
6 people committed Jan 21, 2021
1 parent dd43cc9 commit 9ade6dd
Show file tree
Hide file tree
Showing 23 changed files with 1,360 additions and 19 deletions.
14 changes: 14 additions & 0 deletions doc/source/guide/FAQ.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ How do I set the Lux widgets to show up on default?
.. code-block:: python
lux.config.default_display = "pandas"
How do I change the plotting library used for visualization?
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
By default, we make use of `Altair <https://altair-viz.github.io/>`__ to generate `Vega-Lite <https://vega.github.io/vega-lite>`__ visualizations. We can modify the :code:`plotting_backend` config property to use `Matplotlib <https://matplotlib.org/>`__ as the plotting library instead:

.. code-block:: python
lux.config.plotting_backend = "matplotlib"
To switch back to Vega-Lite:

.. code-block:: python
lux.config.plotting_backend = "vegalite"
I want to change the opacity of my chart, add title, change chart font size, etc. How do I modify chart settings?
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Expand Down
23 changes: 22 additions & 1 deletion doc/source/guide/style.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ To change the plot configuration in Altair, we need to specify a function that t
Let's say that we want to change all the graphical marks of the charts to green and add a custom title. We can define this `change_color_add_title` function, which configures the chart's mark as green and adds a custom title to the chart.

.. code-block:: python
lux.config.plotting_backend = "altair" # or 'vegalite'
def change_color_add_title(chart):
chart = chart.configure_mark(color="green") # change mark color to green
chart.title = "Custom Title" # add title to chart
Expand All @@ -43,6 +44,26 @@ We now see that the displayed visualizations adopt these new imported settings.
:width: 700
:align: center

Similarly, we can change the plot configurations for Matplotlib charts as well.
The plot_config attribute for Matplotlib charts takes in both the figure and axis as parameters.
.. code-block:: python
lux.config.plotting_backend = "matplotlib" # or 'matplotlib_code'
def add_title(fig, ax):
ax.set_title("Test Title")
return fig, ax
.. code-block:: python
lux.config.plot_config = add_title
We now see that the displayed visualizations adopt these new imported settings.

.. image:: ../img/style-7.png
:width: 700
:align: center

If we click on the visualization for `Displacement` v.s. `Weight` and export it. We see that the exported chart now contains code with these additional plot settings at the every end.

.. code-block:: python
Expand Down
Binary file added doc/source/img/style-7.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 62 additions & 14 deletions doc/source/reference/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,45 @@ If you try to set the default_display to anything other than 'lux' or 'pandas,'
:width: 700
:align: center

Change plotting backend for rendering visualizations in Lux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can set the :code:`plotting_backend` config to change the plotting library used for rendering the visualizations in Lux.
This is often useful not just for stylizing plot aesthetics, but also to change the code generated when `exporting a visualization <https://lux-api.readthedocs.io/en/latest/source/guide/export.html>`__.
For example, if you are more familiar with `matplotlib <https://matplotlib.org/>`__ , you may want to use a matplotlib plotting backend so that you can make use of the exported visualization code. In the following code, we set the plotting backend to 'matplotlib', and Lux will display the Matplotlib rendered charts.

.. code-block:: python
lux.config.plotting_backend = "matplotlib"
df
.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/vislib-1.png?raw=true
:width: 700
:align: center

We can set the vislib back to the default 'vegalite,' which uses Vega-Lite to render the displayed chart.

.. code-block:: python
lux.config.plotting_backend = "vegalite"
df
.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/display-1.png?raw=true
:width: 700
:align: center

Lux currently only support Vega-Lite and matplotlib, and we plan to add support for other plotting libraries in the future. If you try to set the :code:`plotting_backend` to anything other than 'matplotlib' or 'vegalite', a warning will be shown, and the display will default to the previous setting.

.. code-block:: python
lux.config.plotting_backend = "notvegalite" # Throw an warning
df
.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/vislib-2.png?raw=true

:width: 700
:align: center

Change the sampling parameters of Lux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -91,20 +130,8 @@ We can disable this feature and revert back to using a scatter plot by running t
lux.config.heatmap = False
Default Renderer
~~~~~~~~~~~~~~~~~

Charts in Lux are rendered using `Altair <https://altair-viz.github.io/>`__. We are working on supporting plotting via `matplotlib <https://matplotlib.org/>`__ and other plotting libraries.

To change the default renderer, run the following code block:

.. code-block:: python
lux.config.renderer = "matplotlib"
Plot Configurations
~~~~~~~~~~~~~~~~~~~
Changing the plot styling
~~~~~~~~~~~~~~~~~~~~~~~~~~

Altair supports plot configurations to be applied on top of the generated graphs. To set a default plot configuration, first write a function that can take in a `chart` and returns a `chart`. For example:

Expand All @@ -129,6 +156,27 @@ The above results in the following changes:

See `this page <https://lux-api.readthedocs.io/en/latest/source/guide/style.html>`__ for more details.

Matplotlib also supports plot configurations to be applied on top of the generated graphs. To set a default plot configuration, first write a function that can take in a `fig` and 'ax' and returns a `fig` and 'ax. For example:

.. code-block:: python
def add_title(fig, ax):
ax.set_title("Test Title")
return fig, ax
.. code-block:: python
lux.config.plot_config = add_title
The above results in the following changes:

.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/style-7.png?raw=true
:width: 600
:align: center

See `this page <https://lux-api.readthedocs.io/en/latest/source/guide/style.html>`__ for more details.


Modify Sorting and Ranking in Recommendations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions doc/source/reference/gen/lux._config.config.Config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ lux.\_config.config.Config

~Config.default_display
~Config.heatmap
~Config.plotting_backend
~Config.sampling
~Config.sampling_cap
~Config.sampling_start
Expand Down
2 changes: 2 additions & 0 deletions doc/source/reference/gen/lux.vis.Vis.Vis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ lux.vis.Vis.Vis
~Vis.to_Altair
~Vis.to_VegaLite
~Vis.to_code
~Vis.to_matplotlib
~Vis.to_matplotlib_code



Expand Down
70 changes: 70 additions & 0 deletions doc/source/reference/lux.vislib.matplotlib.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
lux.vislib.matplotlib package
=============================

Submodules
----------

lux.vislib.matplotlib.BarChart module
-------------------------------------

.. automodule:: lux.vislib.matplotlib.BarChart
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.Heatmap module
------------------------------------

.. automodule:: lux.vislib.matplotlib.Heatmap
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.Histogram module
--------------------------------------

.. automodule:: lux.vislib.matplotlib.Histogram
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.LineChart module
--------------------------------------

.. automodule:: lux.vislib.matplotlib.LineChart
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.MatplotlibChart module
--------------------------------------------

.. automodule:: lux.vislib.matplotlib.MatplotlibChart
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.MatplotlibRenderer module
-----------------------------------------------

.. automodule:: lux.vislib.matplotlib.MatplotlibRenderer
:members:
:undoc-members:
:show-inheritance:

lux.vislib.matplotlib.ScatterChart module
-----------------------------------------

.. automodule:: lux.vislib.matplotlib.ScatterChart
:members:
:undoc-members:
:show-inheritance:


Module contents
---------------

.. automodule:: lux.vislib.matplotlib
:members:
:undoc-members:
:show-inheritance:
25 changes: 24 additions & 1 deletion lux/_config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class Config:

def __init__(self):
self._default_display = "pandas"
self.renderer = "altair"
self.plot_config = None
self.SQLconnection = ""
self.executor = None
Expand All @@ -30,6 +29,7 @@ def __init__(self):
self._sampling_cap = 30000
self._sampling_flag = True
self._heatmap_flag = True
self._plotting_backend = "vegalite"
self._topk = 15
self._sort = "descending"
self._pandas_fallback = True
Expand Down Expand Up @@ -261,6 +261,29 @@ def default_display(self, type: str) -> None:
stacklevel=2,
)

@property
def plotting_backend(self):
return self._plotting_backend

@plotting_backend.setter
def plotting_backend(self, type: str) -> None:
"""
Set the widget display to show Vegalite by default or Matplotlib by default
Parameters
----------
type : str
Default display type, can take either the string `vegalite` or `matplotlib` (regardless of capitalization)
"""
if type.lower() == "vegalite" or type.lower() == "altair":
self._plotting_backend = "vegalite"
elif type.lower() == "matplotlib":
self._plotting_backend = "matplotlib"
else:
warnings.warn(
"Unsupported plotting backend. Lux currently only support 'altair', 'vegalite', or 'matplotlib'",
stacklevel=2,
)

def _get_action(self, pat: str, silent: bool = False):
return lux.actions[pat]

Expand Down
4 changes: 2 additions & 2 deletions lux/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ def current_vis_to_JSON(vlist, input_current_vis=""):
current_vis_spec = {}
numVC = len(vlist) # number of visualizations in the vis list
if numVC == 1:
current_vis_spec = vlist[0].to_code(prettyOutput=False)
current_vis_spec = vlist[0].to_code(language=lux.config.plotting_backend, prettyOutput=False)
elif numVC > 1:
pass
return current_vis_spec
Expand All @@ -807,7 +807,7 @@ def rec_to_JSON(recs):
if len(rec["collection"]) > 0:
rec["vspec"] = []
for vis in rec["collection"]:
chart = vis.to_code(prettyOutput=False)
chart = vis.to_code(language=lux.config.plotting_backend, prettyOutput=False)
rec["vspec"].append(chart)
rec_lst.append(rec)
# delete since not JSON serializable
Expand Down
11 changes: 11 additions & 0 deletions lux/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import pandas as pd
import matplotlib.pyplot as plt


def convert_to_list(x):
Expand Down Expand Up @@ -103,3 +104,13 @@ def like_nan(val):
import math

return math.isnan(val)


def matplotlib_setup(w, h):
plt.ioff()
fig, ax = plt.subplots(figsize=(w, h))
ax.set_axisbelow(True)
ax.grid(color="#dddddd")
ax.spines["right"].set_color("#dddddd")
ax.spines["top"].set_color("#dddddd")
return fig, ax
40 changes: 40 additions & 0 deletions lux/vis/Vis.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from lux.vis.Clause import Clause
from lux.utils.utils import check_import_lux_widget
import lux
import warnings


class Vis:
Expand Down Expand Up @@ -236,6 +237,36 @@ def to_Altair(self, standalone=False) -> str:
self._code = renderer.create_vis(self, standalone)
return self._code

def to_matplotlib(self) -> str:
"""
Generate minimal Matplotlib code to visualize the Vis
Returns
-------
str
String version of the Matplotlib code. Need to print out the string to apply formatting.
"""
from lux.vislib.matplotlib.MatplotlibRenderer import MatplotlibRenderer

renderer = MatplotlibRenderer(output_type="matplotlib")
self._code = renderer.create_vis(self)
return self._code

def to_matplotlib_code(self) -> str:
"""
Generate minimal Matplotlib code to visualize the Vis
Returns
-------
str
String version of the Matplotlib code. Need to print out the string to apply formatting.
"""
from lux.vislib.matplotlib.MatplotlibRenderer import MatplotlibRenderer

renderer = MatplotlibRenderer(output_type="matplotlib_code")
self._code = renderer.create_vis(self)
return self._code

def to_VegaLite(self, prettyOutput=True) -> Union[dict, str]:
"""
Generate minimal Vega-Lite code to visualize the Vis
Expand Down Expand Up @@ -276,6 +307,15 @@ def to_code(self, language="vegalite", **kwargs):
return self.to_VegaLite(**kwargs)
elif language == "altair":
return self.to_Altair(**kwargs)
elif language == "matplotlib":
return self.to_matplotlib()
elif language == "matplotlib_code":
return self.to_matplotlib_code()
else:
warnings.warn(
"Unsupported plotting backend. Lux currently only support 'altair', 'vegalite', or 'matplotlib'",
stacklevel=2,
)

def refresh_source(self, ldf): # -> Vis:
"""
Expand Down

0 comments on commit 9ade6dd

Please sign in to comment.