From 7d69e639c561364c8bac9f77010bca7af8fccc66 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Sat, 10 Oct 2020 00:17:39 -0700 Subject: [PATCH 01/11] remove and register action functions --- lux/__init__.py | 7 ++ lux/_config/__init__.py | 7 ++ lux/_config/config.py | 109 ++++++++++++++++++++++++ lux/action/custom.py | 19 ++++- lux/action/univariate.py | 6 +- lux/core/frame.py | 115 ++++++++++++------------- tests/test_config.py | 179 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 379 insertions(+), 63 deletions(-) create mode 100644 lux/_config/__init__.py create mode 100644 lux/_config/config.py create mode 100644 tests/test_config.py diff --git a/lux/__init__.py b/lux/__init__.py index 45d5cbec..2d149806 100644 --- a/lux/__init__.py +++ b/lux/__init__.py @@ -16,3 +16,10 @@ from lux.vis.Clause import Clause from lux.core.frame import LuxDataFrame from ._version import __version__, version_info +from lux._config import config +from lux._config.config import ( + register_action, + remove_action, + actions, + update_actions +) diff --git a/lux/_config/__init__.py b/lux/_config/__init__.py new file mode 100644 index 00000000..2d70a068 --- /dev/null +++ b/lux/_config/__init__.py @@ -0,0 +1,7 @@ +from lux._config import config +from lux._config.config import ( + register_action, + remove_action, + actions, + update_actions, +) diff --git a/lux/_config/config.py b/lux/_config/config.py new file mode 100644 index 00000000..78b5dc49 --- /dev/null +++ b/lux/_config/config.py @@ -0,0 +1,109 @@ +''' +This config file was largely borrowed from Pandas config.py set_action functionality. +For more resources, see https://github.com/pandas-dev/pandas/blob/master/pandas/_config +''' +from collections import namedtuple +from typing import Any, Callable, Dict, Iterable, List, Optional + +RegisteredOption = namedtuple("RegisteredOption", "key function validator args") + +# holds registered option metadata +_registered_actions: Dict[str, RegisteredOption] = {} +update_actions: Dict[str, bool] = {} +update_actions["flag"] = False + +def _get_action(pat: str, silent: bool = False): + return _registered_actions[pat] + +class DictWrapper: + def __init__(self, d: Dict[str, Any], prefix: str = ""): + object.__setattr__(self, "d", d) + object.__setattr__(self, "prefix", prefix) + def __init__(self, d: Dict[str, RegisteredOption], prefix: str = ""): + object.__setattr__(self, "d", d) + object.__setattr__(self, "prefix", prefix) + + def __setattr__(self, key: str, val: Any) -> None: + prefix = object.__getattribute__(self, "prefix") + if prefix: + prefix += "." + prefix += key + if key in self.d and not isinstance(self.d[key], dict): + _set_option(prefix, val) + else: + raise OptionError("You can only set the value of existing options") + + def __getattr__(self, key: str): + prefix = object.__getattribute__(self, "prefix") + if prefix: + prefix += "." + prefix += key + try: + v = object.__getattribute__(self, "d")[key] + except KeyError as err: + raise OptionError("No such option") from err + if isinstance(v, dict): + return DictWrapper(v, prefix) + else: + return _get_action(prefix) + + def __getactions__(self): + l = [] + for key in self.__dir__(): + l.append(self.__getattr__(key)) + return l + + def __len__(self): + return len(list(self.d.keys())) + + def __dir__(self) -> Iterable[str]: + return list(self.d.keys()) + +actions = DictWrapper(_registered_actions) + + +def register_action( + key: str = "", + function: Optional[Callable[[Any], Any]] = None, + validator: Optional[Callable[[Any], Any]] = None, + *args, +) -> None: + + key = key.lower() + if function: + is_callable(function) + if not function: + raise ValueError("No parameter function found") + + if validator: + is_callable(validator) + _registered_actions[key] = RegisteredOption( + key=key, function=function, validator=validator, args=args + ) + update_actions["flag"] = True + +def remove_action( + key: str = "", +) -> None: + + key = key.lower() + if key not in _registered_actions: + raise ValueError(f"Option '{key}' has not been registered") + + del _registered_actions[key] + update_actions["flag"] = True + +def is_callable(obj) -> bool: + """ + Parameters + ---------- + `obj` - the object to be checked + Returns + ------- + validator - returns True if object is callable + raises ValueError otherwise. + """ + if not callable(obj): + raise ValueError("Value must be a callable") + return True + \ No newline at end of file diff --git a/lux/action/custom.py b/lux/action/custom.py index 698c7a9c..dd795ff7 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -16,6 +16,7 @@ import lux from lux.executor.PandasExecutor import PandasExecutor from lux.executor.SQLExecutor import SQLExecutor +import lux def custom(ldf): ''' @@ -42,4 +43,20 @@ def custom(ldf): vis.score = interestingness(vis,ldf) # ldf.clear_intent() vlist.sort(remove_invalid=True) - return recommendation \ No newline at end of file + return recommendation + +def custom_actions(ldf): + if (lux.actions.__len__() > 0): + recommendations = [] + for action_name in lux.actions.__dir__(): + validator = lux.actions.__getattr__(action_name).validator + if validator is None or (validator is not None and validator(ldf)): + args = lux.actions.__getattr__(action_name).args + if args: + recommendation = lux.actions.__getattr__(action_name).function(ldf, args) + else: + recommendation = lux.actions.__getattr__(action_name).function(ldf) + recommendations.append(recommendation) + return recommendations + else: + return [] diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 2735348b..0d58c8bf 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -16,7 +16,7 @@ from lux.vis.VisList import VisList import lux from lux.utils import utils -def univariate(ldf, data_type_constraint="quantitative"): +def univariate(ldf, *args): ''' Generates bar chart distributions of different attributes in the dataframe. @@ -34,6 +34,10 @@ def univariate(ldf, data_type_constraint="quantitative"): object with a collection of visualizations that result from the Distribution action. ''' import numpy as np + if len(args) == 0: + data_type_constraint = "quantitative" + else: + data_type_constraint = args[0][0] filter_specs = utils.get_filter_specs(ldf._intent) ignore_rec_flag = False diff --git a/lux/core/frame.py b/lux/core/frame.py index 9da8a7c5..8ce536b4 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -22,6 +22,7 @@ from lux.utils.utils import check_import_lux_widget from typing import Dict, Union, List, Callable import warnings +import lux class LuxDataFrame(pd.DataFrame): ''' A subclass of pd.DataFrame that supports all dataframe operations while housing other variables and functions for generating visual recommendations. @@ -29,14 +30,13 @@ class LuxDataFrame(pd.DataFrame): # MUST register here for new properties!! _metadata = ['_intent','data_type_lookup','data_type', 'data_model_lookup','data_model','unique_values','cardinality','_rec_info', '_pandas_only', - '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history', '_saved_export'] + '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history'] def __init__(self,*args, **kw): from lux.executor.PandasExecutor import PandasExecutor self._history = History() self._intent = [] self._recommendation = {} - self._saved_export = None self._current_vis = [] self._prev = None super(LuxDataFrame, self).__init__(*args, **kw) @@ -384,13 +384,15 @@ def _append_rec(self,rec_infolist,recommendations:Dict): rec_infolist.append(recommendations) def maintain_recs(self): # `rec_df` is the dataframe to generate the recommendations on + if (lux.update_actions["flag"] == True): + self._recs_fresh = False show_prev = False # flag indicating whether rec_df is showing previous df or current self if self._prev is not None: rec_df = self._prev rec_df._message = Message() rec_df.maintain_metadata() # the prev dataframe may not have been printed before last_event = self.history._events[-1].name - rec_df._message.add(f"Lux is visualizing the previous version of the dataframe before you applied {last_event}.") + rec_df._message.append(f"Lux is visualizing the previous version of the dataframe before you applied {last_event}.") show_prev = True else: rec_df = self @@ -400,12 +402,13 @@ def maintain_recs(self): if (len(rec_df.data_type["id"])>0): for id_field in rec_df.data_type["id"]: id_fields_str += f"{id_field}, " id_fields_str = id_fields_str[:-2] - rec_df._message.add(f"{id_fields_str} is not visualized since it resembles an ID field.") + rec_df._message.append(f"{id_fields_str} is not visualized since it resembles an ID field.") rec_df._prev = None # reset _prev if (not hasattr(rec_df,"_recs_fresh") or not rec_df._recs_fresh ): # Check that recs has not yet been computed rec_infolist = [] from lux.action.custom import custom + from lux.action.custom import custom_actions from lux.action.correlation import correlation from lux.action.univariate import univariate from lux.action.enhance import enhance @@ -419,26 +422,25 @@ def maintain_recs(self): if (rec_df.index.name is not None): rec_df._append_rec(rec_infolist, column_group(rec_df)) else: - if (rec_df.current_vis is None): - no_vis = True - one_current_vis = False - multiple_current_vis = False - else: - no_vis = len(rec_df.current_vis) == 0 - one_current_vis = len(rec_df.current_vis) == 1 - multiple_current_vis = len(rec_df.current_vis) > 1 - - if (no_vis): - rec_df._append_rec(rec_infolist, correlation(rec_df)) - rec_df._append_rec(rec_infolist, univariate(rec_df,"quantitative")) - rec_df._append_rec(rec_infolist, univariate(rec_df,"nominal")) - rec_df._append_rec(rec_infolist, univariate(rec_df,"temporal")) - elif (one_current_vis): - rec_df._append_rec(rec_infolist, enhance(rec_df)) - rec_df._append_rec(rec_infolist, filter(rec_df)) - rec_df._append_rec(rec_infolist, generalize(rec_df)) - elif (multiple_current_vis): - rec_df._append_rec(rec_infolist, custom(rec_df)) + if self.recommendation == None: + no_vis = lambda ldf: (ldf.current_vis is None) or (ldf.current_vis is not None and len(ldf.current_vis) == 0) + one_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) == 1 + multiple_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) > 1 + lux.register_action("correlation", correlation, no_vis) + lux.register_action("distribution", univariate, no_vis, "quantitative") + lux.register_action("occurrence", univariate, no_vis, "nominal") + lux.register_action("temporal", univariate, no_vis, "temporal") + + lux.register_action("enhance", enhance, one_current_vis) + lux.register_action("filter", filter, one_current_vis) + lux.register_action("generalize", generalize, one_current_vis) + + lux.register_action("custom", custom, multiple_current_vis) + + custom_action_collection = custom_actions(rec_df) + for rec in custom_action_collection: + rec_df._append_rec(rec_infolist, rec) + lux.update_actions["flag"] = False # Store _rec_info into a more user-friendly dictionary form rec_df.recommendation = {} @@ -490,11 +492,9 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" , stacklevel=2) return [] - exported_vis_lst = self._widget._exportedVisIdxs + exported_vis_lst =self._widget._exportedVisIdxs exported_vis = [] if (exported_vis_lst=={}): - if self._saved_export: - return self._saved_export warnings.warn( "\nNo visualization selected to export.\n" "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" @@ -513,7 +513,6 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: elif len(exported_vis_lst) == 1 and ("currentVis" not in exported_vis_lst): export_action = list(exported_vis_lst.keys())[0] exported_vis = VisList(list(map(self.recommendation[export_action].__getitem__, exported_vis_lst[export_action]))) - self._saved_export = exported_vis return exported_vis else: warnings.warn( @@ -522,13 +521,6 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: ,stacklevel=2) return [] - def removeDeletedRecs(self, change): - for action in self._widget.deletedIndices: - deletedSoFar = 0 - for index in self._widget.deletedIndices[action]: - self.recommendation[action].remove_index(index - deletedSoFar) - deletedSoFar += 1 - def _repr_html_(self): from IPython.display import display from IPython.display import clear_output @@ -569,29 +561,30 @@ def _repr_html_(self): # df_to_display.maintain_recs() # compute the recommendations (TODO: This can be rendered in another thread in the background to populate self._widget) self.maintain_recs() - #Observers(callback_function, listen_to_this_variable) - self._widget.observe(self.removeDeletedRecs, names='deletedIndices') - - # box = widgets.Box(layout=widgets.Layout(display='inline')) - button = widgets.Button(description="Toggle Pandas/Lux",layout=widgets.Layout(width='140px',top='5px')) - output = widgets.Output() - # box.children = [button,output] - # output.children = [button] - # display(box) - display(button,output) - def on_button_clicked(b): - with output: - if (b): - self._toggle_pandas_display = not self._toggle_pandas_display - clear_output() - if (self._toggle_pandas_display): - display(self.display_pandas()) - else: - # b.layout.display = "none" - display(self._widget) - # b.layout.display = "inline-block" - button.on_click(on_button_clicked) - on_button_clicked(None) + if len(self.recommendation) > 0: + # box = widgets.Box(layout=widgets.Layout(display='inline')) + button = widgets.Button(description="Toggle Pandas/Lux",layout=widgets.Layout(width='140px',top='5px')) + output = widgets.Output() + # box.children = [button,output] + # output.children = [button] + # display(box) + display(button,output) + def on_button_clicked(b): + with output: + if (b): + self._toggle_pandas_display = not self._toggle_pandas_display + clear_output() + if (self._toggle_pandas_display): + display(self.display_pandas()) + else: + # b.layout.display = "none" + display(self._widget) + # b.layout.display = "inline-block" + button.on_click(on_button_clicked) + on_button_clicked(None) + else: + warnings.warn("\nLux defaults to Pandas when there are no valid actions defined.",stacklevel=2) + display(self.display_pandas()) except(KeyboardInterrupt,SystemExit): raise except: @@ -631,9 +624,9 @@ def render_widget(self, renderer:str ="altair", input_current_vis=""): User-specified current vis to override default Current Vis, by default """ check_import_lux_widget() - import luxwidget + import luxWidget widgetJSON = self.to_JSON(self._rec_info, input_current_vis=input_current_vis) - return luxwidget.LuxWidget( + return luxWidget.LuxWidget( currentVis=widgetJSON["current_vis"], recommendations=widgetJSON["recommendation"], intent=LuxDataFrame.intent_to_string(self._intent), @@ -695,7 +688,7 @@ def rec_to_JSON(recs): # delete DataObjectCollection since not JSON serializable del rec_lst[idx]["collection"] return rec_lst - + # Overridden Pandas Functions def head(self, n: int = 5): self._prev = self diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..ff2ff00b --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,179 @@ +# Copyright 2019-2020 The Lux Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .context import lux +import pytest +import pandas as pd +import time +from lux.vis.VisList import VisList +import lux + +# To run the script and see the printed result, run: +def test_q0_default_actions_registered(): + df = pd.read_csv("lux/data/car.csv") + df._repr_html_() + assert("Distribution" in df.recommendation) + assert(len(df.recommendation["Distribution"]) > 0) + + assert("Occurrence" in df.recommendation) + assert(len(df.recommendation["Occurrence"]) > 0) + + assert("Temporal" in df.recommendation) + assert(len(df.recommendation["Temporal"]) > 0) + + assert("Correlation" in df.recommendation) + assert(len(df.recommendation["Correlation"]) > 0) + +def test_q1_fail_validator(): + df = pd.read_csv("lux/data/car.csv") + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + def contain_horsepower(df): + for clause in df.intent: + if clause.get_attr() == "Horsepower": + return True + return False + lux.register_action("bars", random_categorical, contain_horsepower) + df._repr_html_() + assert("bars" not in df.recommendation) + +def test_q2_pass_validator(): + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + def contain_horsepower(df): + for clause in df.intent: + if clause.get_attr() == "Horsepower": + return True + return False + df = pd.read_csv("lux/data/car.csv") + lux.register_action("bars", random_categorical, contain_horsepower) + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + assert(len(df.recommendation["bars"]) > 0) + assert("bars" in df.recommendation) + +def test_q3_no_validator(): + df = pd.read_csv("lux/data/car.csv") + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + lux.register_action("bars", random_categorical) + df._repr_html_() + assert(len(df.recommendation["bars"]) > 0) + assert("bars" in df.recommendation) + +def test_q4_no_function(): + df = pd.read_csv("lux/data/car.csv") + with pytest.raises(ValueError,match="No parameter function found"): + lux.register_action("bars") + +def test_q5_invalid_function(): + df = pd.read_csv("lux/data/car.csv") + with pytest.raises(ValueError,match="Value must be a callable"): + lux.register_action("bars", "not a Callable") + +def test_q6_invalid_validator(): + df = pd.read_csv("lux/data/car.csv") + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + with pytest.raises(ValueError,match="Value must be a callable"): + lux.register_action("bars", random_categorical, "not a Callable") + +def test_q7_remove_action(): + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + def contain_horsepower(df): + for clause in df.intent: + if clause.get_attr() == "Horsepower": + return True + return False + df = pd.read_csv("lux/data/car.csv") + lux.register_action("bars", random_categorical, contain_horsepower) + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + assert("bars" in df.recommendation) + assert(len(df.recommendation["bars"]) > 0) + lux.remove_action("bars") + df._repr_html_() + assert("bars" not in df.recommendation) + +def test_q8_remove_invalid_action(): + df = pd.read_csv("lux/data/car.csv") + with pytest.raises(ValueError,match="Option 'bars' has not been registered"): + lux.remove_action("bars") + +def test_q9_remove_default_actions(): + df = pd.read_csv("lux/data/car.csv") + df._repr_html_() + + lux.remove_action("Distribution") + df._repr_html_() + assert("Distribution" not in df.recommendation) + + lux.remove_action("Occurrence") + df._repr_html_() + assert("Occurrence" not in df.recommendation) + + lux.remove_action("Temporal") + df._repr_html_() + assert("Temporal" not in df.recommendation) + + lux.remove_action("Correlation") + df._repr_html_() + assert("Correlation" not in df.recommendation) + + assert(len(df.recommendation) == 0) + + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + def contain_horsepower(df): + for clause in df.intent: + if clause.get_attr() == "Horsepower": + return True + return False + df = pd.read_csv("lux/data/car.csv") + lux.register_action("bars", random_categorical, contain_horsepower) + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + assert("bars" in df.recommendation) + assert(len(df.recommendation["bars"]) > 0) From e5a241ed7a0d5ff8b3189f98e17e57659691f463 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Sat, 10 Oct 2020 23:25:55 -0700 Subject: [PATCH 02/11] update changes inframe.py --- lux/core/frame.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 8ce536b4..7e3fc8ac 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -30,13 +30,14 @@ class LuxDataFrame(pd.DataFrame): # MUST register here for new properties!! _metadata = ['_intent','data_type_lookup','data_type', 'data_model_lookup','data_model','unique_values','cardinality','_rec_info', '_pandas_only', - '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history'] + '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history', '_saved_export'] def __init__(self,*args, **kw): from lux.executor.PandasExecutor import PandasExecutor self._history = History() self._intent = [] self._recommendation = {} + self._saved_export = None self._current_vis = [] self._prev = None super(LuxDataFrame, self).__init__(*args, **kw) @@ -224,12 +225,10 @@ def set_intent(self, intent:List[Union[str, Clause]]): """ Main function to set the intent of the dataframe. The intent input goes through the parser, so that the string inputs are parsed into a lux.Clause object. - Parameters ---------- intent : List[str,Clause] intent list, can be a mix of string shorthand or a lux.Clause object - Notes ----- :doc:`../guide/clause` @@ -257,7 +256,6 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis - Parameters ---------- vis : Vis @@ -392,7 +390,7 @@ def maintain_recs(self): rec_df._message = Message() rec_df.maintain_metadata() # the prev dataframe may not have been printed before last_event = self.history._events[-1].name - rec_df._message.append(f"Lux is visualizing the previous version of the dataframe before you applied {last_event}.") + rec_df._message.add(f"Lux is visualizing the previous version of the dataframe before you applied {last_event}.") show_prev = True else: rec_df = self @@ -402,7 +400,7 @@ def maintain_recs(self): if (len(rec_df.data_type["id"])>0): for id_field in rec_df.data_type["id"]: id_fields_str += f"{id_field}, " id_fields_str = id_fields_str[:-2] - rec_df._message.append(f"{id_fields_str} is not visualized since it resembles an ID field.") + rec_df._message.add(f"{id_fields_str} is not visualized since it resembles an ID field.") rec_df._prev = None # reset _prev if (not hasattr(rec_df,"_recs_fresh") or not rec_df._recs_fresh ): # Check that recs has not yet been computed @@ -470,7 +468,6 @@ def widget(self): def exported(self) -> Union[Dict[str,VisList], VisList]: """ Get selected visualizations as exported Vis List - Notes ----- Convert the _exportedVisIdxs dictionary into a programmable VisList @@ -492,9 +489,11 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" , stacklevel=2) return [] - exported_vis_lst =self._widget._exportedVisIdxs + exported_vis_lst = self._widget._exportedVisIdxs exported_vis = [] if (exported_vis_lst=={}): + if self._saved_export: + return self._saved_export warnings.warn( "\nNo visualization selected to export.\n" "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" @@ -513,6 +512,7 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: elif len(exported_vis_lst) == 1 and ("currentVis" not in exported_vis_lst): export_action = list(exported_vis_lst.keys())[0] exported_vis = VisList(list(map(self.recommendation[export_action].__getitem__, exported_vis_lst[export_action]))) + self._saved_export = exported_vis return exported_vis else: warnings.warn( @@ -521,6 +521,13 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: ,stacklevel=2) return [] + def removeDeletedRecs(self, change): + for action in self._widget.deletedIndices: + deletedSoFar = 0 + for index in self._widget.deletedIndices[action]: + self.recommendation[action].remove_index(index - deletedSoFar) + deletedSoFar += 1 + def _repr_html_(self): from IPython.display import display from IPython.display import clear_output @@ -561,6 +568,9 @@ def _repr_html_(self): # df_to_display.maintain_recs() # compute the recommendations (TODO: This can be rendered in another thread in the background to populate self._widget) self.maintain_recs() + #Observers(callback_function, listen_to_this_variable) + self._widget.observe(self.removeDeletedRecs, names='deletedIndices') + if len(self.recommendation) > 0: # box = widgets.Box(layout=widgets.Layout(display='inline')) button = widgets.Button(description="Toggle Pandas/Lux",layout=widgets.Layout(width='140px',top='5px')) @@ -624,9 +634,9 @@ def render_widget(self, renderer:str ="altair", input_current_vis=""): User-specified current vis to override default Current Vis, by default """ check_import_lux_widget() - import luxWidget + import luxwidget widgetJSON = self.to_JSON(self._rec_info, input_current_vis=input_current_vis) - return luxWidget.LuxWidget( + return luxwidget.LuxWidget( currentVis=widgetJSON["current_vis"], recommendations=widgetJSON["recommendation"], intent=LuxDataFrame.intent_to_string(self._intent), @@ -673,6 +683,7 @@ def current_vis_to_JSON(vlist, input_current_vis=""): elif (numVC>1): pass return current_vis_spec + @staticmethod def rec_to_JSON(recs): rec_lst = [] From 2b08f0ebdf9ac3bb6bcaf9d3c69b0b21ae7b9238 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Sat, 10 Oct 2020 23:26:36 -0700 Subject: [PATCH 03/11] update changes inframe.py --- lux/vis/Clause.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lux/vis/Clause.py b/lux/vis/Clause.py index 9aeaa2ff..2d313de3 100644 --- a/lux/vis/Clause.py +++ b/lux/vis/Clause.py @@ -73,6 +73,9 @@ def __init__(self, description:typing.Union[str,list] ="",attribute: typing.Unio self.weight = weight self.sort = sort self.exclude = exclude + + def get_attr(self): + return self.attribute def copy_clause(self): copied_clause = Clause() From c05037b1f9c0d71d253cd50ad76dbebd82bec0f3 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Mon, 12 Oct 2020 17:48:51 -0700 Subject: [PATCH 04/11] add documentation and changes --- lux/_config/config.py | 92 +++++++++++++++++----------- lux/action/custom.py | 21 +++++-- lux/core/frame.py | 7 ++- tests/test_config.py | 135 +++++++++++++++--------------------------- 4 files changed, 127 insertions(+), 128 deletions(-) diff --git a/lux/_config/config.py b/lux/_config/config.py index 78b5dc49..8a060ae5 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -5,7 +5,7 @@ from collections import namedtuple from typing import Any, Callable, Dict, Iterable, List, Optional -RegisteredOption = namedtuple("RegisteredOption", "key function validator args") +RegisteredOption = namedtuple("RegisteredOption", "name action display_condition args") # holds registered option metadata _registered_actions: Dict[str, RegisteredOption] = {} @@ -23,23 +23,22 @@ def __init__(self, d: Dict[str, RegisteredOption], prefix: str = ""): object.__setattr__(self, "d", d) object.__setattr__(self, "prefix", prefix) - def __setattr__(self, key: str, val: Any) -> None: + def __getattr__(self, name: str): + """ + Gets a specific registered action by id + Parameters + ---------- + `name` - the id for the actions + Return + ------- + DictWrapper object for the action + """ prefix = object.__getattribute__(self, "prefix") if prefix: prefix += "." - prefix += key - if key in self.d and not isinstance(self.d[key], dict): - _set_option(prefix, val) - else: - raise OptionError("You can only set the value of existing options") - - def __getattr__(self, key: str): - prefix = object.__getattribute__(self, "prefix") - if prefix: - prefix += "." - prefix += key + prefix += name try: - v = object.__getattribute__(self, "d")[key] + v = object.__getattribute__(self, "d")[name] except KeyError as err: raise OptionError("No such option") from err if isinstance(v, dict): @@ -48,9 +47,15 @@ def __getattr__(self, key: str): return _get_action(prefix) def __getactions__(self): + """ + Gathers all currently registered actions in a list of DictWrapper + Return + ------- + a list of DictWrapper objects that are registered + """ l = [] - for key in self.__dir__(): - l.append(self.__getattr__(key)) + for name in self.__dir__(): + l.append(self.__getattr__(name)) return l def __len__(self): @@ -63,34 +68,49 @@ def __dir__(self) -> Iterable[str]: def register_action( - key: str = "", - function: Optional[Callable[[Any], Any]] = None, - validator: Optional[Callable[[Any], Any]] = None, + name: str = "", + action: Callable[[Any], Any] = None, + display_condition: Optional[Callable[[Any], Any]] = None, *args, ) -> None: - - key = key.lower() - if function: - is_callable(function) - if not function: - raise ValueError("No parameter function found") - - if validator: - is_callable(validator) - _registered_actions[key] = RegisteredOption( - key=key, function=function, validator=validator, args=args + """ + Parameters + ---------- + `name` - the id for the actions + `action` - the function to be applied to the dataframe + `display_condition` - the function to check whether or not the function should be applied + `args` - any additional arguments the function may require + Description + ------- + Registers the provided action globally in lux + """ + name = name.lower() + if action: + is_callable(action) + + if display_condition: + is_callable(display_condition) + _registered_actions[name] = RegisteredOption( + name=name, action=action, display_condition=display_condition, args=args ) update_actions["flag"] = True def remove_action( - key: str = "", + name: str = "", ) -> None: + """ + Parameters + ---------- + `name` - the id for the action to remove + Description + ------- + Removes the provided action globally in lux + """ + name = name.lower() + if name not in _registered_actions: + raise ValueError(f"Option '{name}' has not been registered") - key = key.lower() - if key not in _registered_actions: - raise ValueError(f"Option '{key}' has not been registered") - - del _registered_actions[key] + del _registered_actions[name] update_actions["flag"] = True def is_callable(obj) -> bool: diff --git a/lux/action/custom.py b/lux/action/custom.py index dd795ff7..114990e7 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -46,16 +46,29 @@ def custom(ldf): return recommendation def custom_actions(ldf): + ''' + Generates user-defined vis based on globally defined actions. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. + + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that were previously registered. + ''' if (lux.actions.__len__() > 0): recommendations = [] for action_name in lux.actions.__dir__(): - validator = lux.actions.__getattr__(action_name).validator - if validator is None or (validator is not None and validator(ldf)): + display_condition = lux.actions.__getattr__(action_name).display_condition + if display_condition is None or (display_condition is not None and display_condition(ldf)): args = lux.actions.__getattr__(action_name).args if args: - recommendation = lux.actions.__getattr__(action_name).function(ldf, args) + recommendation = lux.actions.__getattr__(action_name).action(ldf, args) else: - recommendation = lux.actions.__getattr__(action_name).function(ldf) + recommendation = lux.actions.__getattr__(action_name).action(ldf) recommendations.append(recommendation) return recommendations else: diff --git a/lux/core/frame.py b/lux/core/frame.py index 7e3fc8ac..d412f076 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -382,6 +382,7 @@ def _append_rec(self,rec_infolist,recommendations:Dict): rec_infolist.append(recommendations) def maintain_recs(self): # `rec_df` is the dataframe to generate the recommendations on + # check to see if globally defined actions have been registered/removed if (lux.update_actions["flag"] == True): self._recs_fresh = False show_prev = False # flag indicating whether rec_df is showing previous df or current self @@ -421,9 +422,12 @@ def maintain_recs(self): rec_df._append_rec(rec_infolist, column_group(rec_df)) else: if self.recommendation == None: + # display conditions for default actions no_vis = lambda ldf: (ldf.current_vis is None) or (ldf.current_vis is not None and len(ldf.current_vis) == 0) one_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) == 1 multiple_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) > 1 + + # globally register default actions lux.register_action("correlation", correlation, no_vis) lux.register_action("distribution", univariate, no_vis, "quantitative") lux.register_action("occurrence", univariate, no_vis, "nominal") @@ -435,6 +439,7 @@ def maintain_recs(self): lux.register_action("custom", custom, multiple_current_vis) + # generate vis from globally registered actions and append to dataframe custom_action_collection = custom_actions(rec_df) for rec in custom_action_collection: rec_df._append_rec(rec_infolist, rec) @@ -699,7 +704,7 @@ def rec_to_JSON(recs): # delete DataObjectCollection since not JSON serializable del rec_lst[idx]["collection"] return rec_lst - + # Overridden Pandas Functions def head(self, n: int = 5): self._prev = self diff --git a/tests/test_config.py b/tests/test_config.py index ff2ff00b..8aee0748 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -19,8 +19,27 @@ from lux.vis.VisList import VisList import lux -# To run the script and see the printed result, run: -def test_q0_default_actions_registered(): +def register_new_action(validator: bool=True): + df = pd.read_csv("lux/data/car.csv") + def random_categorical(ldf): + intent = [lux.Clause("?",data_type="nominal")] + vlist = VisList(intent,ldf) + for vis in vlist: + vis.score = 10 + vlist = vlist.topK(15) + return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} + def contain_horsepower(df): + for clause in df.intent: + if clause.get_attr() == "Horsepower": + return True + return False + if validator: + lux.register_action("bars", random_categorical, contain_horsepower) + else: + lux.register_action("bars", random_categorical) + return df + +def test_default_actions_registered(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() assert("Distribution" in df.recommendation) @@ -35,69 +54,32 @@ def test_q0_default_actions_registered(): assert("Correlation" in df.recommendation) assert(len(df.recommendation["Correlation"]) > 0) -def test_q1_fail_validator(): - df = pd.read_csv("lux/data/car.csv") - def random_categorical(ldf): - intent = [lux.Clause("?",data_type="nominal")] - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = 10 - vlist = vlist.topK(15) - return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} - def contain_horsepower(df): - for clause in df.intent: - if clause.get_attr() == "Horsepower": - return True - return False - lux.register_action("bars", random_categorical, contain_horsepower) +def test_fail_validator(): + df = register_new_action() df._repr_html_() - assert("bars" not in df.recommendation) + assert("bars" not in df.recommendation, + "Bars should not be rendered when there is no intent 'horsepower' specified.") -def test_q2_pass_validator(): - def random_categorical(ldf): - intent = [lux.Clause("?",data_type="nominal")] - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = 10 - vlist = vlist.topK(15) - return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} - def contain_horsepower(df): - for clause in df.intent: - if clause.get_attr() == "Horsepower": - return True - return False - df = pd.read_csv("lux/data/car.csv") - lux.register_action("bars", random_categorical, contain_horsepower) +def test_pass_validator(): + df = register_new_action() df.set_intent(["Acceleration", "Horsepower"]) df._repr_html_() assert(len(df.recommendation["bars"]) > 0) - assert("bars" in df.recommendation) + assert("bars" in df.recommendation, + "Bars should be rendered when intent 'horsepower' is specified.") -def test_q3_no_validator(): - df = pd.read_csv("lux/data/car.csv") - def random_categorical(ldf): - intent = [lux.Clause("?",data_type="nominal")] - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = 10 - vlist = vlist.topK(15) - return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} - lux.register_action("bars", random_categorical) +def test_no_validator(): + df = register_new_action(False) df._repr_html_() assert(len(df.recommendation["bars"]) > 0) assert("bars" in df.recommendation) -def test_q4_no_function(): - df = pd.read_csv("lux/data/car.csv") - with pytest.raises(ValueError,match="No parameter function found"): - lux.register_action("bars") - -def test_q5_invalid_function(): +def test_invalid_function(): df = pd.read_csv("lux/data/car.csv") with pytest.raises(ValueError,match="Value must be a callable"): lux.register_action("bars", "not a Callable") -def test_q6_invalid_validator(): +def test_invalid_validator(): df = pd.read_csv("lux/data/car.csv") def random_categorical(ldf): intent = [lux.Clause("?",data_type="nominal")] @@ -109,35 +91,25 @@ def random_categorical(ldf): with pytest.raises(ValueError,match="Value must be a callable"): lux.register_action("bars", random_categorical, "not a Callable") -def test_q7_remove_action(): - def random_categorical(ldf): - intent = [lux.Clause("?",data_type="nominal")] - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = 10 - vlist = vlist.topK(15) - return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} - def contain_horsepower(df): - for clause in df.intent: - if clause.get_attr() == "Horsepower": - return True - return False - df = pd.read_csv("lux/data/car.csv") - lux.register_action("bars", random_categorical, contain_horsepower) +def test_remove_action(): + df = register_new_action() df.set_intent(["Acceleration", "Horsepower"]) df._repr_html_() - assert("bars" in df.recommendation) - assert(len(df.recommendation["bars"]) > 0) + assert("bars" in df.recommendation, + "Bars should be rendered after it has been registered with correct intent.") + assert(len(df.recommendation["bars"]) > 0, + "Bars should be rendered after it has been registered with correct intent.") lux.remove_action("bars") df._repr_html_() - assert("bars" not in df.recommendation) + assert("bars" not in df.recommendation, + "Bars should not be rendered after it has been removed.") -def test_q8_remove_invalid_action(): +def test_remove_invalid_action(): df = pd.read_csv("lux/data/car.csv") with pytest.raises(ValueError,match="Option 'bars' has not been registered"): lux.remove_action("bars") -def test_q9_remove_default_actions(): +def test_remove_default_actions(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() @@ -157,23 +129,12 @@ def test_q9_remove_default_actions(): df._repr_html_() assert("Correlation" not in df.recommendation) - assert(len(df.recommendation) == 0) + assert(len(df.recommendation) == 0, + "Default actions should not be rendered after it has been removed.") - def random_categorical(ldf): - intent = [lux.Clause("?",data_type="nominal")] - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = 10 - vlist = vlist.topK(15) - return {"action":"bars", "description": "Random list of Bar charts", "collection": vlist} - def contain_horsepower(df): - for clause in df.intent: - if clause.get_attr() == "Horsepower": - return True - return False - df = pd.read_csv("lux/data/car.csv") - lux.register_action("bars", random_categorical, contain_horsepower) + df = register_new_action() df.set_intent(["Acceleration", "Horsepower"]) df._repr_html_() - assert("bars" in df.recommendation) + assert("bars" in df.recommendation, + "Bars should be rendered after it has been registered with correct intent.") assert(len(df.recommendation["bars"]) > 0) From 836d8586ca64027c9091ac8e33ab3e14fd48088e Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Fri, 16 Oct 2020 09:59:56 -0700 Subject: [PATCH 05/11] indentation and comments --- lux/_config/config.py | 84 ++++++++++++++++++++++++------------------- lux/core/frame.py | 3 ++ 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/lux/_config/config.py b/lux/_config/config.py index 8a060ae5..193ae3d6 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -9,6 +9,7 @@ # holds registered option metadata _registered_actions: Dict[str, RegisteredOption] = {} +# flags whether or not an action has been registered or removed and should be re-rendered by frame.py update_actions: Dict[str, bool] = {} update_actions["flag"] = False @@ -25,14 +26,16 @@ def __init__(self, d: Dict[str, RegisteredOption], prefix: str = ""): def __getattr__(self, name: str): """ - Gets a specific registered action by id - Parameters - ---------- - `name` - the id for the actions - Return - ------- - DictWrapper object for the action - """ + Gets a specific registered action by id + + Parameters + ---------- + name : str + the name of the action + Return + ------- + DictWrapper object for the action + """ prefix = object.__getattribute__(self, "prefix") if prefix: prefix += "." @@ -48,10 +51,11 @@ def __getattr__(self, name: str): def __getactions__(self): """ - Gathers all currently registered actions in a list of DictWrapper - Return - ------- - a list of DictWrapper objects that are registered + Gathers all currently registered actions in a list of DictWrapper + + Return + ------- + List of DictWrapper objects that are registered """ l = [] for name in self.__dir__(): @@ -74,16 +78,19 @@ def register_action( *args, ) -> None: """ - Parameters - ---------- - `name` - the id for the actions - `action` - the function to be applied to the dataframe - `display_condition` - the function to check whether or not the function should be applied - `args` - any additional arguments the function may require - Description - ------- - Registers the provided action globally in lux - """ + Registers the provided action globally in lux + + Parameters + ---------- + name : str + the name of the action + action : Callable[[Any], Any] + the function used to generate the recommendations + display_condition : Callable[[Any], Any] + the function to check whether or not the function should be applied + args: Any + any additional arguments the function may require + """ name = name.lower() if action: is_callable(action) @@ -99,13 +106,13 @@ def remove_action( name: str = "", ) -> None: """ - Parameters - ---------- - `name` - the id for the action to remove - Description - ------- - Removes the provided action globally in lux - """ + Removes the provided action globally in lux + + Parameters + ---------- + name : str + the name of the action to remove + """ name = name.lower() if name not in _registered_actions: raise ValueError(f"Option '{name}' has not been registered") @@ -115,14 +122,17 @@ def remove_action( def is_callable(obj) -> bool: """ - Parameters - ---------- - `obj` - the object to be checked - Returns - ------- - validator - returns True if object is callable - raises ValueError otherwise. - """ + Parameters + ---------- + obj: Any + the object to be checked + + Returns + ------- + validator : bool + returns True if object is callable + raises ValueError otherwise. + """ if not callable(obj): raise ValueError("Value must be a callable") return True diff --git a/lux/core/frame.py b/lux/core/frame.py index d412f076..ce676c92 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -225,10 +225,12 @@ def set_intent(self, intent:List[Union[str, Clause]]): """ Main function to set the intent of the dataframe. The intent input goes through the parser, so that the string inputs are parsed into a lux.Clause object. + Parameters ---------- intent : List[str,Clause] intent list, can be a mix of string shorthand or a lux.Clause object + Notes ----- :doc:`../guide/clause` @@ -256,6 +258,7 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis + Parameters ---------- vis : Vis From 279163868b77d2c54394ddcb0c3d38047b5a144d Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Fri, 16 Oct 2020 10:00:52 -0700 Subject: [PATCH 06/11] new line --- lux/core/frame.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index ce676c92..839c80f3 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -258,7 +258,7 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis - + Parameters ---------- vis : Vis @@ -476,6 +476,7 @@ def widget(self): def exported(self) -> Union[Dict[str,VisList], VisList]: """ Get selected visualizations as exported Vis List + Notes ----- Convert the _exportedVisIdxs dictionary into a programmable VisList From 1e6e3959fddab52e06e8fe8bd80c70347cff7585 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Thu, 22 Oct 2020 22:08:54 -0700 Subject: [PATCH 07/11] globally defined default display works with warning --- examples/demo/Custom Action.ipynb | 282 ++++++++++++++++++++++ examples/demo/Untitled.ipynb | 375 ++++++++++++++++++++++++++++++ examples/demo/cars_demo.ipynb | 58 ++++- lux/__init__.py | 2 +- lux/_config/__init__.py | 2 +- lux/_config/config.py | 52 ++--- lux/core/frame.py | 25 +- 7 files changed, 735 insertions(+), 61 deletions(-) create mode 100644 examples/demo/Custom Action.ipynb create mode 100644 examples/demo/Untitled.ipynb diff --git a/examples/demo/Custom Action.ipynb b/examples/demo/Custom Action.ipynb new file mode 100644 index 00000000..be2f1fb4 --- /dev/null +++ b/examples/demo/Custom Action.ipynb @@ -0,0 +1,282 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/Users/dorislee/Desktop/Research/lux/lux\n" + ] + } + ], + "source": [ + "cd ../../" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import lux" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"../lux-datasets/data/hpi.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "df[\"G10\"] = df[\"Country\"].isin([\"Belgium\",\"Canada\",\"France\",\"Germany\",\"Italy\",\"Japan\",\"Netherlands\",\"United Kingdom\",\"Switzerland\",\"Sweden\",\"United States\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from lux.vis.VisList import VisList" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "intent = [lux.Clause(\"?\",data_type=\"quantitative\"),lux.Clause(\"G10\")]\n", + "vlist = VisList(intent,df)\n", + "\n", + "for vis in vlist:\n", + " # Percentage Change Between G10 v.s. non-G10 countries \n", + " a = vis.data.iloc[0,1]\n", + " b = vis.data.iloc[1,1]\n", + " vis.score = (b-a)/a\n", + "vlist = vlist.topK(15)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "96ffe7def5bc4b2a8864dcb0cb5560b1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "LuxWidget(recommendations=[{'action': 'Vis List', 'description': 'Shows a vis list defined by the intent', 'vs…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "[,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vlist" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def G10_mean_difference(ldf):\n", + " intent = [lux.Clause(\"?\",data_type=\"quantitative\"),lux.Clause(\"G10\")]\n", + " vlist = VisList(intent,df)\n", + "\n", + " for vis in vlist:\n", + " a = vis.data.iloc[0,1]\n", + " b = vis.data.iloc[1,1]\n", + " vis.score = (b-a)/a\n", + " vlist = vlist.topK(15)\n", + " return {\"action\":\"G10\", \"description\": \"Percentage Change of Means Between G10 v.s. non-G10 countries\", \"collection\": vlist}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "lux.register_action(\"G10\", G10_mean_difference)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can look at the attributes that are most different across G10 and non-G10 countries. We find that G10 countries have higher GDPPerCapita, but also contribute to more Footprint." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ad5271b7a17f402994d4a3bf5c52bc48", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b8716851eb6741e49678341870a69e09", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What if we look at countries above a certain GDP threshold? " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7fe27311ca2e47b19489d6d441758cae", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "652d42bf51e74a2b8ede7c3b0f55809f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[df[\"GDPPerCapita\"]>40000]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Bug: it doesn't look like the G10 action is being regenerated but the other actions are being recomputed." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/demo/Untitled.ipynb b/examples/demo/Untitled.ipynb new file mode 100644 index 00000000..05f04311 --- /dev/null +++ b/examples/demo/Untitled.ipynb @@ -0,0 +1,375 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import math\n", + "import numpy as np\n", + "from lux.vis.VisList import VisList\n", + "\n", + "def similar_pattern(ldf, *args):\n", + " '''\n", + " Generates visualizations with similar patterns to a query visualization.\n", + "\n", + " Parameters\n", + " ----------\n", + " ldf : lux.core.frame\n", + " LuxDataFrame with underspecified intent.\n", + "\n", + " intent: list[lux.Clause]\n", + " intent for specifying the visual query for the similarity search.\n", + "\n", + " topK: int\n", + " number of visual recommendations to return.\n", + "\n", + " Returns\n", + " -------\n", + " recommendations : Dict[str,obj]\n", + " object with a collection of visualizations that result from the Similarity action\n", + " '''\n", + " print(args[0][0])\n", + " intent = [lux.Clause(\"Year\",channel=\"x\"), \n", + " lux.Clause(\"Displacement\",channel=\"y\"), \n", + " lux.Clause(\"Origin=?\")]\n", + " row_specs = [lux.Clause(\"Origin\", value=\"USA\")]\n", + " topK = 15\n", + " \n", + " row_specs = list(filter(lambda x: x.value != \"\", row_specs))\n", + " print(len(row_specs))\n", + " if(len(row_specs) == 1):\n", + " search_space_vc = VisList(ldf.current_vis,ldf)\n", + " \n", + " query_vc = VisList(intent,ldf) \n", + " query_vis = query_vc[0]\n", + " print(query_vis.data)\n", + " preprocess(query_vis)\n", + " #for loop to create assign euclidean distance\n", + " recommendation = {\"action\":\"Similarity\",\n", + " \"description\":\"Show other charts that are visually similar to the Current vis.\"}\n", + " for vis in search_space_vc:\n", + " preprocess(vis)\n", + " vis.score = euclidean_dist(query_vis, vis)\n", + " search_space_vc.normalize_score(invert_order=True)\n", + " if(topK!=-1):\n", + " search_space_vc = search_space_vc.topK(topK)\n", + " recommendation[\"collection\"] = search_space_vc\n", + " return recommendation\n", + " else:\n", + " print(\"Query needs to have 1 row value\")\n", + "\n", + "def aggregate(vis):\n", + " '''\n", + " Aggregates data values on the y axis so that the vis is a time series\n", + "\n", + " Parameters\n", + " ----------\n", + " vis : lux.vis.Vis\n", + " vis that represents the candidate visualization\n", + " Returns\n", + " -------\n", + " None\n", + " '''\n", + " if vis.get_attr_by_channel(\"x\") and vis.get_attr_by_channel(\"y\"):\n", + "\n", + " xAxis = vis.get_attr_by_channel(\"x\")[0].attribute\n", + " yAxis = vis.get_attr_by_channel(\"y\")[0].attribute\n", + "\n", + " vis.data = vis.data[[xAxis,yAxis]].groupby(xAxis,as_index=False).agg({yAxis:'mean'}).copy()\n", + "\n", + "def interpolate(vis,length):\n", + " '''\n", + " Interpolates the vis data so that the number of data points is fixed to a constant\n", + "\n", + " Parameters\n", + " ----------\n", + " vis : lux.vis.Vis\n", + " vis that represents the candidate visualization\n", + " length : int\n", + " number of points a vis should have\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " '''\n", + " if vis.get_attr_by_channel(\"x\") and vis.get_attr_by_channel(\"y\"):\n", + "\n", + " xAxis = vis.get_attr_by_channel(\"x\")[0].attribute\n", + " yAxis = vis.get_attr_by_channel(\"y\")[0].attribute\n", + "\n", + " if xAxis and yAxis:\n", + " yVals = vis.data[yAxis]\n", + " xVals = vis.data[xAxis]\n", + " n = length\n", + "\n", + " interpolated_x_vals = [0.0]*(length)\n", + " interpolated_y_vals = [0.0]*(length)\n", + "\n", + " granularity = (xVals[len(xVals)-1] - xVals[0]) / n\n", + "\n", + " count = 0\n", + "\n", + " for i in range(0,n):\n", + " interpolated_x = xVals[0] + i * granularity\n", + " interpolated_x_vals[i] = interpolated_x\n", + "\n", + " while xVals[count] < interpolated_x:\n", + " if(count < len(xVals)):\n", + " count += 1\n", + " if xVals[count] == interpolated_x:\n", + " interpolated_y_vals[i] = yVals[count]\n", + " else:\n", + " x_diff = xVals[count] - xVals[count-1]\n", + " yDiff = yVals[count] - yVals[count-1]\n", + " interpolated_y_vals[i] = yVals[count-1] + (interpolated_x - xVals[count-1]) / x_diff * yDiff\n", + " vis.data = pd.DataFrame(list(zip(interpolated_x_vals, interpolated_y_vals)),columns = [xAxis, yAxis])\n", + "\n", + "# interpolate dataset\n", + "\n", + "def normalize(vis):\n", + " '''\n", + " Normalizes the vis data so that the range of values is 0 to 1 for the vis\n", + "\n", + " Parameters\n", + " ----------\n", + " vis : lux.vis.Vis\n", + " vis that represents the candidate visualization\n", + " Returns\n", + " -------\n", + " None\n", + " '''\n", + " if vis.get_attr_by_channel(\"y\"):\n", + " y_axis = vis.get_attr_by_channel(\"y\")[0].attribute\n", + " max = vis.data[y_axis].max()\n", + " min = vis.data[y_axis].min()\n", + " if(max == min or (max-min<1)):\n", + " return\n", + " vis.data[y_axis] = (vis.data[y_axis] - min) / (max - min)\n", + "\n", + "def euclidean_dist(query_vis, vis):\n", + " '''\n", + " Calculates euclidean distance score for similarity between two visualizations\n", + "\n", + " Parameters\n", + " ----------\n", + " query_vis : lux.vis.Vis\n", + " vis that represents the query pattern\n", + " vis : lux.vis.Vis\n", + " vis that represents the candidate visualization\n", + "\n", + " Returns\n", + " -------\n", + " score : float\n", + " euclidean distance score\n", + " '''\n", + "\n", + " if query_vis.get_attr_by_channel(\"y\") and vis.get_attr_by_channel(\"y\"):\n", + "\n", + " vis_y_axis = vis.get_attr_by_channel(\"y\")[0].attribute\n", + " query_y_axis = query_vis.get_attr_by_channel(\"y\")[0].attribute\n", + "\n", + " vis_vector = vis.data[vis_y_axis].values\n", + " query_vector = query_vis.data[query_y_axis].values\n", + " score = np.linalg.norm(vis_vector - query_vector)\n", + "\n", + " return score\n", + " else:\n", + " print(\"no y axis detected\")\n", + " return 0\n", + "def preprocess(vis):\n", + " '''\n", + " Processes vis data to allow similarity comparisons between visualizations\n", + "\n", + " Parameters\n", + " ----------\n", + " vis : lux.vis.Vis\n", + " vis that represents the candidate visualization\n", + " Returns\n", + " -------\n", + " None\n", + " '''\n", + " aggregate(vis)\n", + " interpolate(vis, 100)\n", + " normalize(vis)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import lux\n", + "from lux.vis.Clause import Clause\n" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": {}, + "outputs": [], + "source": [ + "one_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) == 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"../../lux/data/car.csv\")\n", + "# df[\"Year\"] = pd.to_datetime(df[\"Year\"], format='%Y') # change pandas dtype for the column \"Year\" to datetype\n", + "# df[\"Month\"] = pd.to_datetime(df[\"Month\"], format='%M') # change pandas dtype for the column \"Year\" to datetype\n", + "\n", + "df.set_intent([lux.Clause(\"Year\",channel=\"x\"), \n", + " lux.Clause(\"Displacement\",channel=\"y\"), \n", + " lux.Clause(\"Origin=USA\")])\n", + "query = [lux.Clause(\"Year\",channel=\"x\"), lux.Clause(\"Displacement\",channel=\"y\"), lux.Clause(\"Origin=Europe\")]\n", + "# df.similiarPattern(query)\n", + "# df.set_intent([\"PctPriceReductions\"])\n", + "# intent = \n", + "# intent[0].value = [\"USA\", \"Europe\"]\n", + "# intent[1].value = [8, 4]\n", + "# intent" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": {}, + "outputs": [], + "source": [ + "lux.register_action(\"similarity\", similar_pattern, one_current_vis, query)" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[, , ]\n", + "1\n", + "\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "can't set attribute", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 576\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 577\u001b[0m \u001b[0;31m# df_to_display.maintain_recs() # compute the recommendations (TODO: This can be rendered in another thread in the background to populate self._widget)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 578\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmaintain_recs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 579\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 580\u001b[0m \u001b[0;31m#Observers(callback_function, listen_to_this_variable)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36mmaintain_recs\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 444\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 445\u001b[0m \u001b[0;31m# generate vis from globally registered actions and append to dataframe\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 446\u001b[0;31m \u001b[0mcustom_action_collection\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcustom_actions\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec_df\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 447\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrec\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcustom_action_collection\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 448\u001b[0m \u001b[0mrec_df\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_append_rec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec_infolist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/action/custom.py\u001b[0m in \u001b[0;36mcustom_actions\u001b[0;34m(ldf)\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 69\u001b[0;31m \u001b[0mrecommendation\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mldf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 70\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0mrecommendation\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mldf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36msimilar_pattern\u001b[0;34m(ldf, *args)\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0mquery_vis\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mquery_vc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery_vis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 41\u001b[0;31m \u001b[0mpreprocess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery_vis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 42\u001b[0m \u001b[0;31m#for loop to create assign euclidean distance\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m recommendation = {\"action\":\"Similarity\",\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mpreprocess\u001b[0;34m(vis)\u001b[0m\n\u001b[1;32m 184\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 185\u001b[0m '''\n\u001b[0;32m--> 186\u001b[0;31m \u001b[0maggregate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 187\u001b[0m \u001b[0minterpolate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 188\u001b[0m \u001b[0mnormalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36maggregate\u001b[0;34m(vis)\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0myAxis\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_attr_by_channel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"y\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mattribute\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 73\u001b[0;31m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mxAxis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0myAxis\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgroupby\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxAxis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mas_index\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0magg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0myAxis\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m'mean'\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 74\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minterpolate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mlength\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: can't set attribute" + ] + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'to_dict'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/VisList.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0mcheck_import_lux_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 242\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mluxwidget\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 243\u001b[0;31m \u001b[0mrecJSON\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLuxDataFrame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrec_to_JSON\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mrecommendation\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 244\u001b[0m \t\tself._widget = luxwidget.LuxWidget(\n\u001b[1;32m 245\u001b[0m \u001b[0mcurrentVis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36mrec_to_JSON\u001b[0;34m(recs)\u001b[0m\n\u001b[1;32m 703\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"vspec\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mvis\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"collection\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 705\u001b[0;31m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrender_VSpec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 706\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"vspec\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchart\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[0mrec_lst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/Vis.py\u001b[0m in \u001b[0;36mrender_VSpec\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrender_VSpec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"altair\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"altair\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 231\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_VegaLite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprettyOutput\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 232\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrefresh_source\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mldf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;31m# -> Vis:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/Vis.py\u001b[0m in \u001b[0;36mto_VegaLite\u001b[0;34m(self, prettyOutput)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvislib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maltair\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mAltairRenderer\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mAltairRenderer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mrenderer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAltairRenderer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"VegaLite\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_code\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_vis\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mprettyOutput\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m\"** Remove this comment -- Copy Text Below to Vega Editor(vega.github.io/editor) to visualize and edit **\\n\"\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_code\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/AltairRenderer.py\u001b[0m in \u001b[0;36mcreate_vis\u001b[0;34m(self, vis, standalone)\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mScatterChart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmark\u001b[0m \u001b[0;34m==\u001b[0m\u001b[0;34m\"line\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 67\u001b[0;31m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLineChart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 68\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmark\u001b[0m \u001b[0;34m==\u001b[0m\u001b[0;34m\"heatmap\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mHeatmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/LineChart.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dobj)\u001b[0m\n\u001b[1;32m 27\u001b[0m \t\"\"\"\n\u001b[1;32m 28\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 29\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 30\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"Line Chart <{str(self.vis)}>\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/AltairChart.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, vis)\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0;31m# ----- START self.code modification -----\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 34\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minitialize_chart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 35\u001b[0m \u001b[0;31m# self.add_tooltip()\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode_color\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/LineChart.py\u001b[0m in \u001b[0;36minitialize_chart\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m\"import pandas._libs.tslibs.timestamps\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m\"from pandas._libs.tslibs.timestamps import Timestamp\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 41\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34mf\"visData = pd.DataFrame({str(self.data.to_dict())})\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 42\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0my_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_model\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"measure\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'to_dict'" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.current_vis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/demo/cars_demo.ipynb b/examples/demo/cars_demo.ipynb index 3fe96d53..ddb7289e 100644 --- a/examples/demo/cars_demo.ipynb +++ b/examples/demo/cars_demo.ipynb @@ -44,28 +44,66 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0d6512e173a74ef8bd929bd8832daaf2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fe11528c20aa4caeb443662bf629ef2c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "df" + "lux.config.default_display = \"lux\"\n", + "df\n", + "# lux.config.default_display" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 4, "metadata": {}, "outputs": [ { - "ename": "AttributeError", - "evalue": "'Clause' object has no attribute 'get_attr'", + "ename": "NameError", + "evalue": "name 'df' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mcontain_horsepower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;31m# lux.register_action(\"bars\", random_categorical, contain_horsepower)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;31m# df.set_intent([\"Acceleration\", \"Horsepower\"])\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mcontain_horsepower\u001b[0;34m(df)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mcontain_horsepower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mclause\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mintent\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mclause\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_attr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"Horsepower\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 12\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'Clause' object has no attribute 'get_attr'" + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mcontain_horsepower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;31m# lux.register_action(\"bars\", random_categorical, contain_horsepower)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;31m# df.set_intent([\"Acceleration\", \"Horsepower\"])\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'df' is not defined" ] } ], diff --git a/lux/__init__.py b/lux/__init__.py index 64e92664..8688b3d2 100644 --- a/lux/__init__.py +++ b/lux/__init__.py @@ -22,5 +22,5 @@ remove_action, actions, update_actions, - default_display, + config, ) diff --git a/lux/_config/__init__.py b/lux/_config/__init__.py index dfa729fc..f7130949 100644 --- a/lux/_config/__init__.py +++ b/lux/_config/__init__.py @@ -4,5 +4,5 @@ remove_action, actions, update_actions, - default_display, + config, ) diff --git a/lux/_config/config.py b/lux/_config/config.py index 28ca21ed..b970600f 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -138,29 +138,29 @@ def is_callable(obj) -> bool: raise ValueError("Value must be a callable") return True -default_pandas_display = True - -@property -def default_display(self): - if (default_pandas_display): - return "pandas" - else: - return "lux" - -@default_display.setter -def default_display(self, type:str) -> None: - """ - Set the widget display to show Pandas by default or Lux by default - Parameters - ---------- - type : str - Default display type, can take either the string `lux` or `pandas` (regardless of capitalization) - """ - warnings.warn("Unsupported display type. Default display option should either be `lux` or `pandas`.",stacklevel=2) - - if (type.lower()=="lux"): - default_pandas_display = False - elif (type.lower()=="pandas"): - default_pandas_display = True - else: - warnings.warn("Unsupported display type. Default display option should either be `lux` or `pandas`.",stacklevel=2) +class Config: + + def __init__(self): + self._default_display = "pandas" + + @property + def default_display(self): + return self._default_display + + @default_display.setter + def default_display(self, type:str) -> None: + # """ + # Set the widget display to show Pandas by default or Lux by default + # Parameters + # ---------- + # type : str + # Default display type, can take either the string `lux` or `pandas` (regardless of capitalization) + # """ + if (type.lower()=="lux"): + self._default_display = "lux" + elif (type.lower()=="pandas"): + self._default_display = "pandas" + else: + warnings.warn("Unsupported display type. Default display option should either be `lux` or `pandas`.",stacklevel=2) + +config = Config() \ No newline at end of file diff --git a/lux/core/frame.py b/lux/core/frame.py index 5ee9a74f..a27f0c52 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -121,27 +121,6 @@ def _set_item(self, key, value): super(LuxDataFrame, self)._set_item(key, value) self.expire_metadata() self.expire_recs() - # @property - # def default_display(self): - # if (lux.default_display == "pandas"): - # return "pandas" - # else: - # return "lux" - # @default_display.setter - # def default_display(self, type:str) -> None: - # """ - # Set the widget display to show Pandas by default or Lux by default - # Parameters - # ---------- - # type : str - # Default display type, can take either the string `lux` or `pandas` (regardless of capitalization) - # """ - # if (type.lower()=="lux"): - # lux.default_display = "lux" - # elif (type.lower()=="pandas"): - # lux.default_display = "pandas" - # else: - # warnings.warn("Unsupported display type. Default display option should either be `lux` or `pandas`.",stacklevel=2) def _infer_structure(self): # If the dataframe is very small and the index column is not a range index, then it is likely that this is an aggregated data is_multi_index_flag = self.index.nlevels !=1 @@ -564,7 +543,7 @@ def _repr_html_(self): return if (len(self.columns)<=1): warnings.warn("\nLux defaults to Pandas when there is only a single column.",stacklevel=2) - display(self.display_pandas()) + display(self.display_pandas()) return self.maintain_metadata() @@ -572,7 +551,7 @@ def _repr_html_(self): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) - if (lux.default_display == "lux"): + if (lux.config.default_display == "lux"): self._toggle_pandas_display = False else: self._toggle_pandas_display = True From 79cf5987a3d2335811fa2687c61cca7fee8a75f5 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Thu, 22 Oct 2020 22:15:42 -0700 Subject: [PATCH 08/11] no examples --- examples/demo/Custom Action.ipynb | 282 -------------------- examples/demo/Untitled.ipynb | 375 -------------------------- examples/demo/cars_demo.ipynb | 419 ------------------------------ lux/core/frame.py | 14 +- 4 files changed, 7 insertions(+), 1083 deletions(-) delete mode 100644 examples/demo/Custom Action.ipynb delete mode 100644 examples/demo/Untitled.ipynb delete mode 100644 examples/demo/cars_demo.ipynb diff --git a/examples/demo/Custom Action.ipynb b/examples/demo/Custom Action.ipynb deleted file mode 100644 index be2f1fb4..00000000 --- a/examples/demo/Custom Action.ipynb +++ /dev/null @@ -1,282 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/Users/dorislee/Desktop/Research/lux/lux\n" - ] - } - ], - "source": [ - "cd ../../" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import lux" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"../lux-datasets/data/hpi.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "df[\"G10\"] = df[\"Country\"].isin([\"Belgium\",\"Canada\",\"France\",\"Germany\",\"Italy\",\"Japan\",\"Netherlands\",\"United Kingdom\",\"Switzerland\",\"Sweden\",\"United States\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from lux.vis.VisList import VisList" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "intent = [lux.Clause(\"?\",data_type=\"quantitative\"),lux.Clause(\"G10\")]\n", - "vlist = VisList(intent,df)\n", - "\n", - "for vis in vlist:\n", - " # Percentage Change Between G10 v.s. non-G10 countries \n", - " a = vis.data.iloc[0,1]\n", - " b = vis.data.iloc[1,1]\n", - " vis.score = (b-a)/a\n", - "vlist = vlist.topK(15)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "96ffe7def5bc4b2a8864dcb0cb5560b1", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "LuxWidget(recommendations=[{'action': 'Vis List', 'description': 'Shows a vis list defined by the intent', 'vs…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vlist" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def G10_mean_difference(ldf):\n", - " intent = [lux.Clause(\"?\",data_type=\"quantitative\"),lux.Clause(\"G10\")]\n", - " vlist = VisList(intent,df)\n", - "\n", - " for vis in vlist:\n", - " a = vis.data.iloc[0,1]\n", - " b = vis.data.iloc[1,1]\n", - " vis.score = (b-a)/a\n", - " vlist = vlist.topK(15)\n", - " return {\"action\":\"G10\", \"description\": \"Percentage Change of Means Between G10 v.s. non-G10 countries\", \"collection\": vlist}" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "lux.register_action(\"G10\", G10_mean_difference)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can look at the attributes that are most different across G10 and non-G10 countries. We find that G10 countries have higher GDPPerCapita, but also contribute to more Footprint." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ad5271b7a17f402994d4a3bf5c52bc48", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b8716851eb6741e49678341870a69e09", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What if we look at countries above a certain GDP threshold? " - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7fe27311ca2e47b19489d6d441758cae", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "652d42bf51e74a2b8ede7c3b0f55809f", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df[df[\"GDPPerCapita\"]>40000]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Bug: it doesn't look like the G10 action is being regenerated but the other actions are being recomputed." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/demo/Untitled.ipynb b/examples/demo/Untitled.ipynb deleted file mode 100644 index 05f04311..00000000 --- a/examples/demo/Untitled.ipynb +++ /dev/null @@ -1,375 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 140, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import math\n", - "import numpy as np\n", - "from lux.vis.VisList import VisList\n", - "\n", - "def similar_pattern(ldf, *args):\n", - " '''\n", - " Generates visualizations with similar patterns to a query visualization.\n", - "\n", - " Parameters\n", - " ----------\n", - " ldf : lux.core.frame\n", - " LuxDataFrame with underspecified intent.\n", - "\n", - " intent: list[lux.Clause]\n", - " intent for specifying the visual query for the similarity search.\n", - "\n", - " topK: int\n", - " number of visual recommendations to return.\n", - "\n", - " Returns\n", - " -------\n", - " recommendations : Dict[str,obj]\n", - " object with a collection of visualizations that result from the Similarity action\n", - " '''\n", - " print(args[0][0])\n", - " intent = [lux.Clause(\"Year\",channel=\"x\"), \n", - " lux.Clause(\"Displacement\",channel=\"y\"), \n", - " lux.Clause(\"Origin=?\")]\n", - " row_specs = [lux.Clause(\"Origin\", value=\"USA\")]\n", - " topK = 15\n", - " \n", - " row_specs = list(filter(lambda x: x.value != \"\", row_specs))\n", - " print(len(row_specs))\n", - " if(len(row_specs) == 1):\n", - " search_space_vc = VisList(ldf.current_vis,ldf)\n", - " \n", - " query_vc = VisList(intent,ldf) \n", - " query_vis = query_vc[0]\n", - " print(query_vis.data)\n", - " preprocess(query_vis)\n", - " #for loop to create assign euclidean distance\n", - " recommendation = {\"action\":\"Similarity\",\n", - " \"description\":\"Show other charts that are visually similar to the Current vis.\"}\n", - " for vis in search_space_vc:\n", - " preprocess(vis)\n", - " vis.score = euclidean_dist(query_vis, vis)\n", - " search_space_vc.normalize_score(invert_order=True)\n", - " if(topK!=-1):\n", - " search_space_vc = search_space_vc.topK(topK)\n", - " recommendation[\"collection\"] = search_space_vc\n", - " return recommendation\n", - " else:\n", - " print(\"Query needs to have 1 row value\")\n", - "\n", - "def aggregate(vis):\n", - " '''\n", - " Aggregates data values on the y axis so that the vis is a time series\n", - "\n", - " Parameters\n", - " ----------\n", - " vis : lux.vis.Vis\n", - " vis that represents the candidate visualization\n", - " Returns\n", - " -------\n", - " None\n", - " '''\n", - " if vis.get_attr_by_channel(\"x\") and vis.get_attr_by_channel(\"y\"):\n", - "\n", - " xAxis = vis.get_attr_by_channel(\"x\")[0].attribute\n", - " yAxis = vis.get_attr_by_channel(\"y\")[0].attribute\n", - "\n", - " vis.data = vis.data[[xAxis,yAxis]].groupby(xAxis,as_index=False).agg({yAxis:'mean'}).copy()\n", - "\n", - "def interpolate(vis,length):\n", - " '''\n", - " Interpolates the vis data so that the number of data points is fixed to a constant\n", - "\n", - " Parameters\n", - " ----------\n", - " vis : lux.vis.Vis\n", - " vis that represents the candidate visualization\n", - " length : int\n", - " number of points a vis should have\n", - "\n", - " Returns\n", - " -------\n", - " None\n", - " '''\n", - " if vis.get_attr_by_channel(\"x\") and vis.get_attr_by_channel(\"y\"):\n", - "\n", - " xAxis = vis.get_attr_by_channel(\"x\")[0].attribute\n", - " yAxis = vis.get_attr_by_channel(\"y\")[0].attribute\n", - "\n", - " if xAxis and yAxis:\n", - " yVals = vis.data[yAxis]\n", - " xVals = vis.data[xAxis]\n", - " n = length\n", - "\n", - " interpolated_x_vals = [0.0]*(length)\n", - " interpolated_y_vals = [0.0]*(length)\n", - "\n", - " granularity = (xVals[len(xVals)-1] - xVals[0]) / n\n", - "\n", - " count = 0\n", - "\n", - " for i in range(0,n):\n", - " interpolated_x = xVals[0] + i * granularity\n", - " interpolated_x_vals[i] = interpolated_x\n", - "\n", - " while xVals[count] < interpolated_x:\n", - " if(count < len(xVals)):\n", - " count += 1\n", - " if xVals[count] == interpolated_x:\n", - " interpolated_y_vals[i] = yVals[count]\n", - " else:\n", - " x_diff = xVals[count] - xVals[count-1]\n", - " yDiff = yVals[count] - yVals[count-1]\n", - " interpolated_y_vals[i] = yVals[count-1] + (interpolated_x - xVals[count-1]) / x_diff * yDiff\n", - " vis.data = pd.DataFrame(list(zip(interpolated_x_vals, interpolated_y_vals)),columns = [xAxis, yAxis])\n", - "\n", - "# interpolate dataset\n", - "\n", - "def normalize(vis):\n", - " '''\n", - " Normalizes the vis data so that the range of values is 0 to 1 for the vis\n", - "\n", - " Parameters\n", - " ----------\n", - " vis : lux.vis.Vis\n", - " vis that represents the candidate visualization\n", - " Returns\n", - " -------\n", - " None\n", - " '''\n", - " if vis.get_attr_by_channel(\"y\"):\n", - " y_axis = vis.get_attr_by_channel(\"y\")[0].attribute\n", - " max = vis.data[y_axis].max()\n", - " min = vis.data[y_axis].min()\n", - " if(max == min or (max-min<1)):\n", - " return\n", - " vis.data[y_axis] = (vis.data[y_axis] - min) / (max - min)\n", - "\n", - "def euclidean_dist(query_vis, vis):\n", - " '''\n", - " Calculates euclidean distance score for similarity between two visualizations\n", - "\n", - " Parameters\n", - " ----------\n", - " query_vis : lux.vis.Vis\n", - " vis that represents the query pattern\n", - " vis : lux.vis.Vis\n", - " vis that represents the candidate visualization\n", - "\n", - " Returns\n", - " -------\n", - " score : float\n", - " euclidean distance score\n", - " '''\n", - "\n", - " if query_vis.get_attr_by_channel(\"y\") and vis.get_attr_by_channel(\"y\"):\n", - "\n", - " vis_y_axis = vis.get_attr_by_channel(\"y\")[0].attribute\n", - " query_y_axis = query_vis.get_attr_by_channel(\"y\")[0].attribute\n", - "\n", - " vis_vector = vis.data[vis_y_axis].values\n", - " query_vector = query_vis.data[query_y_axis].values\n", - " score = np.linalg.norm(vis_vector - query_vector)\n", - "\n", - " return score\n", - " else:\n", - " print(\"no y axis detected\")\n", - " return 0\n", - "def preprocess(vis):\n", - " '''\n", - " Processes vis data to allow similarity comparisons between visualizations\n", - "\n", - " Parameters\n", - " ----------\n", - " vis : lux.vis.Vis\n", - " vis that represents the candidate visualization\n", - " Returns\n", - " -------\n", - " None\n", - " '''\n", - " aggregate(vis)\n", - " interpolate(vis, 100)\n", - " normalize(vis)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 141, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import lux\n", - "from lux.vis.Clause import Clause\n" - ] - }, - { - "cell_type": "code", - "execution_count": 142, - "metadata": {}, - "outputs": [], - "source": [ - "one_current_vis = lambda ldf: ldf.current_vis is not None and len(ldf.current_vis) == 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 143, - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"../../lux/data/car.csv\")\n", - "# df[\"Year\"] = pd.to_datetime(df[\"Year\"], format='%Y') # change pandas dtype for the column \"Year\" to datetype\n", - "# df[\"Month\"] = pd.to_datetime(df[\"Month\"], format='%M') # change pandas dtype for the column \"Year\" to datetype\n", - "\n", - "df.set_intent([lux.Clause(\"Year\",channel=\"x\"), \n", - " lux.Clause(\"Displacement\",channel=\"y\"), \n", - " lux.Clause(\"Origin=USA\")])\n", - "query = [lux.Clause(\"Year\",channel=\"x\"), lux.Clause(\"Displacement\",channel=\"y\"), lux.Clause(\"Origin=Europe\")]\n", - "# df.similiarPattern(query)\n", - "# df.set_intent([\"PctPriceReductions\"])\n", - "# intent = \n", - "# intent[0].value = [\"USA\", \"Europe\"]\n", - "# intent[1].value = [8, 4]\n", - "# intent" - ] - }, - { - "cell_type": "code", - "execution_count": 144, - "metadata": {}, - "outputs": [], - "source": [ - "lux.register_action(\"similarity\", similar_pattern, one_current_vis, query)" - ] - }, - { - "cell_type": "code", - "execution_count": 145, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[, , ]\n", - "1\n", - "\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "can't set attribute", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 576\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 577\u001b[0m \u001b[0;31m# df_to_display.maintain_recs() # compute the recommendations (TODO: This can be rendered in another thread in the background to populate self._widget)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 578\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmaintain_recs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 579\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 580\u001b[0m \u001b[0;31m#Observers(callback_function, listen_to_this_variable)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36mmaintain_recs\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 444\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 445\u001b[0m \u001b[0;31m# generate vis from globally registered actions and append to dataframe\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 446\u001b[0;31m \u001b[0mcustom_action_collection\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcustom_actions\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec_df\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 447\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrec\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcustom_action_collection\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 448\u001b[0m \u001b[0mrec_df\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_append_rec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec_infolist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/action/custom.py\u001b[0m in \u001b[0;36mcustom_actions\u001b[0;34m(ldf)\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 69\u001b[0;31m \u001b[0mrecommendation\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mldf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 70\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0mrecommendation\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maction_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mldf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36msimilar_pattern\u001b[0;34m(ldf, *args)\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0mquery_vis\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mquery_vc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery_vis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 41\u001b[0;31m \u001b[0mpreprocess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery_vis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 42\u001b[0m \u001b[0;31m#for loop to create assign euclidean distance\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m recommendation = {\"action\":\"Similarity\",\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mpreprocess\u001b[0;34m(vis)\u001b[0m\n\u001b[1;32m 184\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 185\u001b[0m '''\n\u001b[0;32m--> 186\u001b[0;31m \u001b[0maggregate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 187\u001b[0m \u001b[0minterpolate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 188\u001b[0m \u001b[0mnormalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36maggregate\u001b[0;34m(vis)\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0myAxis\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_attr_by_channel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"y\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mattribute\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 73\u001b[0;31m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mxAxis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0myAxis\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgroupby\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxAxis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mas_index\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0magg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0myAxis\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m'mean'\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 74\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minterpolate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mlength\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: can't set attribute" - ] - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 145, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df" - ] - }, - { - "cell_type": "code", - "execution_count": 146, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'NoneType' object has no attribute 'to_dict'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/VisList.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0mcheck_import_lux_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 242\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mluxwidget\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 243\u001b[0;31m \u001b[0mrecJSON\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLuxDataFrame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrec_to_JSON\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mrecommendation\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 244\u001b[0m \t\tself._widget = luxwidget.LuxWidget(\n\u001b[1;32m 245\u001b[0m \u001b[0mcurrentVis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/core/frame.py\u001b[0m in \u001b[0;36mrec_to_JSON\u001b[0;34m(recs)\u001b[0m\n\u001b[1;32m 703\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"vspec\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mvis\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"collection\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 705\u001b[0;31m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrender_VSpec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 706\u001b[0m \u001b[0mrec\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"vspec\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchart\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[0mrec_lst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrec\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/Vis.py\u001b[0m in \u001b[0;36mrender_VSpec\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrender_VSpec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"altair\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"altair\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 231\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_VegaLite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprettyOutput\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 232\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrefresh_source\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mldf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;31m# -> Vis:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vis/Vis.py\u001b[0m in \u001b[0;36mto_VegaLite\u001b[0;34m(self, prettyOutput)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mlux\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvislib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maltair\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mAltairRenderer\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mAltairRenderer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mrenderer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAltairRenderer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"VegaLite\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_code\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_vis\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mprettyOutput\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m\"** Remove this comment -- Copy Text Below to Vega Editor(vega.github.io/editor) to visualize and edit **\\n\"\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_code\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/AltairRenderer.py\u001b[0m in \u001b[0;36mcreate_vis\u001b[0;34m(self, vis, standalone)\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mScatterChart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmark\u001b[0m \u001b[0;34m==\u001b[0m\u001b[0;34m\"line\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 67\u001b[0;31m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLineChart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 68\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmark\u001b[0m \u001b[0;34m==\u001b[0m\u001b[0;34m\"heatmap\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mHeatmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvis\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/LineChart.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dobj)\u001b[0m\n\u001b[1;32m 27\u001b[0m \t\"\"\"\n\u001b[1;32m 28\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 29\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 30\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"Line Chart <{str(self.vis)}>\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/AltairChart.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, vis)\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0;31m# ----- START self.code modification -----\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 34\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minitialize_chart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 35\u001b[0m \u001b[0;31m# self.add_tooltip()\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode_color\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/opt/anaconda3/envs/lux/lib/python3.8/site-packages/lux_api-0.2.0-py3.8.egg/lux/vislib/altair/LineChart.py\u001b[0m in \u001b[0;36minitialize_chart\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m\"import pandas._libs.tslibs.timestamps\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m\"from pandas._libs.tslibs.timestamps import Timestamp\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 41\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34mf\"visData = pd.DataFrame({str(self.data.to_dict())})\\n\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 42\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0my_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_model\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"measure\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'to_dict'" - ] - }, - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 146, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.current_vis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/demo/cars_demo.ipynb b/examples/demo/cars_demo.ipynb deleted file mode 100644 index ddb7289e..00000000 --- a/examples/demo/cars_demo.ipynb +++ /dev/null @@ -1,419 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Basic Walkthrough" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import lux" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We first load in the [Cars dataset](http://lib.stat.cmu.edu/datasets/) with 392 different cars from 1970-1982, which contains information about its Horsepower, MilesPerGal, Acceleration, etc." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"../../lux/data/car.csv\")\n", - "df[\"Year\"] = pd.to_datetime(df[\"Year\"], format='%Y') # change pandas dtype for the column \"Year\" to datetype" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We print out the dataframe, we see the default Pandas display and we can toggle to a set of recommendations generated by Lux. \n", - "Lux returns four sets of visualizations to show an overview of the dataset.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0d6512e173a74ef8bd929bd8832daaf2", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Button(description='Toggle Pandas/Lux', layout=Layout(top='5px', width='140px'), style=ButtonStyle())" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fe11528c20aa4caeb443662bf629ef2c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lux.config.default_display = \"lux\"\n", - "df\n", - "# lux.config.default_display" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'df' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mcontain_horsepower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;31m# lux.register_action(\"bars\", random_categorical, contain_horsepower)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;31m# df.set_intent([\"Acceleration\", \"Horsepower\"])\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mNameError\u001b[0m: name 'df' is not defined" - ] - } - ], - "source": [ - "from lux.vis.VisList import VisList\n", - "def random_categorical(ldf):\n", - " intent = [lux.Clause(\"?\",data_type=\"nominal\")]\n", - " vlist = VisList(intent,ldf)\n", - " for vis in vlist:\n", - " vis.score = 10\n", - " vlist = vlist.topK(15)\n", - " return {\"action\":\"bars\", \"description\": \"Random list of Bar charts\", \"collection\": vlist}\n", - "def contain_horsepower(df):\n", - " for clause in df.intent:\n", - " if clause.get_attr() == \"Horsepower\":\n", - " return True\n", - " return False\n", - "contain_horsepower(df)\n", - "# lux.register_action(\"bars\", random_categorical, contain_horsepower)\n", - "# df.set_intent([\"Acceleration\", \"Horsepower\"])\n", - "# df._repr_html_()\n", - "# lux.actions.__getattr__(\"bars\")\n", - "# df._repr_html_()\n", - "# len(df.recommendation[\"bars\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we spot this scatterplot visualization `Acceleration` v.s. `Horsepower`.\n", - "Intuitively, we expect cars with higher horsepower to have higher acceleration, but we are actually seeing the opposite of that trend. \n", - "\n", - "Let's learn more about whether there are additional factors that is affecting this relationship.\n", - "Using the `intent` property, we indicate to Lux that we are interested in the attributes `Acceleration` and `Horsepower`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.intent = [\"Acceleration\",\"Horsepower\"]\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "On the left, we see that the Current Visualization corresponds to our specified intent.\n", - "On the right, we see different tabs of recommendations: \n", - "- `Enhance` shows what happens when we add an additional variable to the current selection\n", - "- Then, we have the `Filter` tab, which adds an additional filter, while fixing the selected variables on the X Y axes\n", - "- Finally, we have `Generalize` which removes one of the attributes, to display the more general trend.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can really quickly compare the relationship between `Horsepower` and `Acceleration` when an additional factor is in play. For example, we see here that `Displacement` and `Weight` is higher on the top left and lower towards the bottom right, whereas `MilesPerGal` has the opposite effect.\n", - "\n", - "We see that there is a strong separation between cars that have 4 cylinders (Orange) and cars with 8 cylinders (green), so we are interested in learning more about the attribute `Cylinders`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df.intent = [\"Cylinders\"]\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Count distribution shows that there is not a lot of cars with 3 and 5 cylinders, so let's clean the data up to remove those." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"Cylinders\"]==3].to_pandas()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"Cylinders\"]==5].to_pandas()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note how easy it is to clean up data and update recommendations in Lux, due to the tight integration with Pandas." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "df = df[(df[\"Cylinders\"]!=3) & (df[\"Cylinders\"]!=5)]\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's say we find the time series showing the number of cars of different `Cylinder` over time to be very interesting. In particular, there seems to be a spike in production of 4 Cylinder cars in 1982. To dig into this more, we can export it by selecting the visualization and clicking on the export button." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vis = df.exported[0]\n", - "vis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can then print out the visualization code in [Altair](https://altair-viz.github.io/) that generated this chart so that we can further tweak the chart as desired." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(vis.to_Altair())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import altair as alt\n", - "import pandas._libs.tslibs.timestamps\n", - "from pandas._libs.tslibs.timestamps import Timestamp\n", - "visData = pd.DataFrame({'Year': {0: Timestamp('1970-01-01 00:00:00'), 1: Timestamp('1970-01-01 00:00:00'), 2: Timestamp('1970-01-01 00:00:00'), 3: Timestamp('1971-01-01 00:00:00'), 4: Timestamp('1971-01-01 00:00:00'), 5: Timestamp('1971-01-01 00:00:00'), 6: Timestamp('1972-01-01 00:00:00'), 7: Timestamp('1972-01-01 00:00:00'), 8: Timestamp('1972-01-01 00:00:00'), 9: Timestamp('1973-01-01 00:00:00'), 10: Timestamp('1973-01-01 00:00:00'), 11: Timestamp('1973-01-01 00:00:00'), 12: Timestamp('1974-01-01 00:00:00'), 13: Timestamp('1974-01-01 00:00:00'), 14: Timestamp('1974-01-01 00:00:00'), 15: Timestamp('1975-01-01 00:00:00'), 16: Timestamp('1975-01-01 00:00:00'), 17: Timestamp('1975-01-01 00:00:00'), 18: Timestamp('1976-01-01 00:00:00'), 19: Timestamp('1976-01-01 00:00:00'), 20: Timestamp('1976-01-01 00:00:00'), 21: Timestamp('1977-01-01 00:00:00'), 22: Timestamp('1977-01-01 00:00:00'), 23: Timestamp('1977-01-01 00:00:00'), 24: Timestamp('1978-01-01 00:00:00'), 25: Timestamp('1978-01-01 00:00:00'), 26: Timestamp('1978-01-01 00:00:00'), 27: Timestamp('1979-01-01 00:00:00'), 28: Timestamp('1979-01-01 00:00:00'), 29: Timestamp('1979-01-01 00:00:00'), 30: Timestamp('1980-01-01 00:00:00'), 31: Timestamp('1980-01-01 00:00:00'), 32: Timestamp('1980-01-01 00:00:00'), 33: Timestamp('1982-01-01 00:00:00'), 34: Timestamp('1982-01-01 00:00:00'), 35: Timestamp('1982-01-01 00:00:00')}, 'Cylinders': {0: 8, 1: 6, 2: 4, 3: 8, 4: 6, 5: 4, 6: 8, 7: 6, 8: 4, 9: 8, 10: 6, 11: 4, 12: 8, 13: 6, 14: 4, 15: 6, 16: 4, 17: 8, 18: 4, 19: 6, 20: 8, 21: 4, 22: 6, 23: 8, 24: 8, 25: 4, 26: 6, 27: 4, 28: 6, 29: 8, 30: 6, 31: 8, 32: 4, 33: 4, 34: 8, 35: 6}, 'Record': {0: 18.0, 1: 4.0, 2: 7.0, 3: 7.0, 4: 8.0, 5: 12.0, 6: 13.0, 7: 0.0, 8: 14.0, 9: 20.0, 10: 8.0, 11: 11.0, 12: 5.0, 13: 6.0, 14: 15.0, 15: 12.0, 16: 12.0, 17: 6.0, 18: 15.0, 19: 10.0, 20: 9.0, 21: 14.0, 22: 5.0, 23: 8.0, 24: 6.0, 25: 17.0, 26: 12.0, 27: 12.0, 28: 6.0, 29: 10.0, 30: 2.0, 31: 0.0, 32: 23.0, 33: 47.0, 34: 1.0, 35: 10.0}})\n", - "\n", - "chart = alt.Chart(visData).mark_line().encode(\n", - " y = alt.Y('Record', type= 'quantitative', title='Number of Records'),\n", - " x = alt.X('Year', type = 'temporal'),\n", - ")\n", - "chart = chart.interactive() # Enable Zooming and Panning\n", - "chart = chart.encode(color=alt.Color('Cylinders',type='nominal'))\n", - "\n", - "chart = chart.configure_title(fontWeight=500,fontSize=13,font='Helvetica Neue')\n", - "chart = chart.configure_axis(titleFontWeight=500,titleFontSize=11,titleFont='Helvetica Neue',\n", - "\t\t\tlabelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue',labelColor='#505050')\n", - "chart = chart.configure_legend(titleFontWeight=500,titleFontSize=10,titleFont='Helvetica Neue',\n", - "\t\t\tlabelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue')\n", - "chart = chart.properties(width=160,height=150)\n", - "\n", - "chart" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is obviously a lot of code, let's look at how easy it is if we were to specify this visualization intent in Lux if we knew what we were looking for." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from lux.vis.Vis import Vis\n", - "Vis([\"Year\",\"Cylinders\"],df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Creating Visualizations " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Lux, user can specify particular visualizations that they want to specify and visualize their data on-demand." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from lux.vis.Vis import Vis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Vis([\"Horsepower\"],df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Vis([\"Origin\",\"Horsepower\"],df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Vis([\"Origin\",lux.Clause(\"Horsepower\",aggregation=\"sum\")],df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also work with collections of Visualization via a `VisList` object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from lux.vis.VisList import VisList" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For example, we can create a set of visualizations of Weight with respect to all other attributes, using the wildcard “?” symbol." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "VisList([\"Horsepower\",\"?\"],df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For more resources about how to use Lux:\n", - "- ReadTheDoc documentation: https://lux-api.readthedocs.io/en/latest/\n", - "- Notebook tutorial series: https://github.com/lux-org/lux/tree/master/examples/tutorial\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/lux/core/frame.py b/lux/core/frame.py index a27f0c52..0e8c040d 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -588,13 +588,13 @@ def on_button_clicked(b): display(self.display_pandas()) except(KeyboardInterrupt,SystemExit): raise - # except: - # warnings.warn( - # "\nUnexpected error in rendering Lux widget and recommendations. " - # "Falling back to Pandas display.\n\n" - # "Please report this issue on Github: https://github.com/lux-org/lux/issues " - # ,stacklevel=2) - # display(self.display_pandas()) + except: + warnings.warn( + "\nUnexpected error in rendering Lux widget and recommendations. " + "Falling back to Pandas display.\n\n" + "Please report this issue on Github: https://github.com/lux-org/lux/issues " + ,stacklevel=2) + display(self.display_pandas()) def display_pandas(self): return self.to_pandas() def render_widget(self, renderer:str ="altair", input_current_vis=""): From a3e96177b054beb7a4b9f39d71dc5ca6c8fb4132 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Thu, 22 Oct 2020 22:17:02 -0700 Subject: [PATCH 09/11] add back space --- lux/core/frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 0e8c040d..42ba8ad2 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -543,7 +543,7 @@ def _repr_html_(self): return if (len(self.columns)<=1): warnings.warn("\nLux defaults to Pandas when there is only a single column.",stacklevel=2) - display(self.display_pandas()) + display(self.display_pandas()) return self.maintain_metadata() From 06c4f7f6b4896ed6994de8d4e45940dc882d9616 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Thu, 22 Oct 2020 22:19:38 -0700 Subject: [PATCH 10/11] new line --- lux/_config/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lux/_config/config.py b/lux/_config/config.py index 68197f23..ea30fdef 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -169,4 +169,4 @@ def default_display(self, type:str) -> None: else: warnings.warn("Unsupported display type. Default display option should either be `lux` or `pandas`.",stacklevel=2) -config = Config() \ No newline at end of file +config = Config() From 7e9d3c394e60133e9a6d937ff3967fc804149161 Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Fri, 23 Oct 2020 08:05:17 -0700 Subject: [PATCH 11/11] uncomment docstring --- lux/_config/config.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lux/_config/config.py b/lux/_config/config.py index ea30fdef..12a7271b 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -155,13 +155,13 @@ def default_display(self): @default_display.setter def default_display(self, type:str) -> None: - # """ - # Set the widget display to show Pandas by default or Lux by default - # Parameters - # ---------- - # type : str - # Default display type, can take either the string `lux` or `pandas` (regardless of capitalization) - # """ + """ + Set the widget display to show Pandas by default or Lux by default + Parameters + ---------- + type : str + Default display type, can take either the string `lux` or `pandas` (regardless of capitalization) + """ if (type.lower()=="lux"): self._default_display = "lux" elif (type.lower()=="pandas"):