Skip to content

Commit

Permalink
supporting user defined view collection through either inputting list…
Browse files Browse the repository at this point in the history
… of views or Spec-based creation of views
  • Loading branch information
dorisjlee committed Jun 10, 2020
1 parent 3b5aee3 commit ef67512
Show file tree
Hide file tree
Showing 9 changed files with 585 additions and 20 deletions.
522 changes: 522 additions & 0 deletions examples/User Defined View .ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lux/action/Correlation.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def correlation(ldf:LuxDataFrame,ignoreTranspose:bool=False):
"description":"Show relationships between two quantitative variables."}
vc = ldf.viewCollection
# if (ignoreIdentity): vc = filter(lambda x: x.specLst[0].attribute!=x.specLst[1].attribute,ldf.viewCollection)
vc = Compiler.compile(ldf, vc, enumerateCollection=False)
vc = Compiler.compile(ldf,ldf.context, vc, enumerateCollection=False)

ldf.executor.execute(vc,ldf)
# Then use the data populated in the view collection to compute score
Expand Down
2 changes: 1 addition & 1 deletion lux/action/Enhance.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def enhance(ldf):
view = lux.view.View.View(cxtNew)
output.append(view)
vc = lux.view.ViewCollection.ViewCollection(output)
vc = Compiler.compile(ldf,vc,enumerateCollection=False)
vc = Compiler.compile(ldf,ldf.context,vc,enumerateCollection=False)

ldf.executor.execute(vc,ldf)

Expand Down
2 changes: 1 addition & 1 deletion lux/action/Filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def filter(ldf):
tempView = View(newSpec)
output.append(tempView)
vc = lux.view.ViewCollection.ViewCollection(output)
vc = Compiler.compile(ldf,vc,enumerateCollection=False)
vc = Compiler.compile(ldf,ldf.context,vc,enumerateCollection=False)

ldf.executor.execute(vc,ldf)
for view in vc:
Expand Down
2 changes: 1 addition & 1 deletion lux/action/Generalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def generalize(ldf):
output.append(tempView)

vc = lux.view.ViewCollection.ViewCollection(output)
vc = Compiler.compile(ldf,vc,enumerateCollection=False)
vc = Compiler.compile(ldf,ldf.context,vc,enumerateCollection=False)
ldf.executor.execute(vc,ldf)
recommendation["collection"] = vc
for view in vc:
Expand Down
19 changes: 9 additions & 10 deletions lux/compiler/Compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __repr__(self):
return f"<Compiler>"

@staticmethod
def compile(ldf: LuxDataFrame, viewCollection: ViewCollection, enumerateCollection=True) -> ViewCollection:
def compile(ldf: LuxDataFrame,specLst:List[Spec], viewCollection: ViewCollection, enumerateCollection=True) -> ViewCollection:
"""
Compiles input specifications in the context of the ldf into a collection of lux.View objects for visualization.
1) Enumerate a collection of views interested by the user to generate a view collection
Expand All @@ -41,15 +41,15 @@ def compile(ldf: LuxDataFrame, viewCollection: ViewCollection, enumerateCollecti
view collection with compiled lux.View objects.
"""
if (enumerateCollection):
viewCollection = Compiler.enumerateCollection(ldf)
viewCollection = Compiler.enumerateCollection(specLst,ldf)
viewCollection = Compiler.expandUnderspecified(ldf, viewCollection) # autofill data type/model information
viewCollection = Compiler.removeAllInvalid(viewCollection) # remove invalid views from collection
for view in viewCollection:
Compiler.determineEncoding(ldf, view) # autofill viz related information
return viewCollection

@staticmethod
def enumerateCollection(ldf: LuxDataFrame) -> ViewCollection:
def enumerateCollection(specLst:List[Spec],ldf: LuxDataFrame) -> ViewCollection:
"""
Given specifications that have been expanded thorught populateOptions,
recursively iterate over the resulting list combinations to generate a View collection.
Expand All @@ -65,7 +65,7 @@ def enumerateCollection(ldf: LuxDataFrame) -> ViewCollection:
view collection with compiled lux.View objects.
"""
import copy
specs = Compiler.populateWildcardOptions(ldf)
specs = Compiler.populateWildcardOptions(specLst,ldf)
attributes = specs['attributes']
filters = specs['filters']
if len(attributes) == 0 and len(filters) > 0:
Expand All @@ -91,7 +91,6 @@ def combine(colAttrs, accum):
collection.append(view)
else:
combine(colAttrs[1:], columnList)

combine(attributes, [])
return ViewCollection(collection)

Expand Down Expand Up @@ -348,8 +347,8 @@ def enforceSpecifiedChannel(view: View, autoChannel: Dict[str,str]):
return view

@staticmethod
def populateWildcardOptions(ldf: LuxDataFrame) -> dict:
# def populateWildcardOptions(specLst:List[Spec],ldf: LuxDataFrame) -> dict:
# def populateWildcardOptions(ldf: LuxDataFrame) -> dict:
def populateWildcardOptions(specLst:List[Spec],ldf: LuxDataFrame) -> dict:
"""
Given wildcards and constraints in the LuxDataFrame's context,
return the list of available values that satisfies the dataType or dataModel constraints.
Expand All @@ -368,7 +367,7 @@ def populateWildcardOptions(ldf: LuxDataFrame) -> dict:
from lux.utils.utils import convert2List

specs = {"attributes": [], "filters": []}
for spec in ldf.context:
for spec in specLst:
specOptions = []
if spec.value == "": # attribute
if spec.attribute == "?":
Expand All @@ -392,8 +391,8 @@ def populateWildcardOptions(ldf: LuxDataFrame) -> dict:
options = []
if spec.value == "?":
options = ldf.uniqueValues[attr]
specInd = ldf.context.index(spec)
ldf.context[specInd] = Spec(attribute=spec.attribute, filterOp="=", value=list(options))
specInd = specLst.index(spec)
specLst[specInd] = Spec(attribute=spec.attribute, filterOp="=", value=list(options))
else:
options.extend(convert2List(spec.value))
for optStr in options:
Expand Down
2 changes: 1 addition & 1 deletion lux/luxDataFrame/LuxDataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def _refreshContext(self):
self.computeDatasetMetadata()
self.context = Parser.parse(self.getContext())
Validator.validateSpec(self.context,self)
viewCollection = Compiler.compile(self,self.viewCollection)
viewCollection = Compiler.compile(self,self.context,self.viewCollection)
self.setViewCollection(viewCollection)

def setContext(self,context:typing.List[typing.Union[str,Spec]]):
Expand Down
2 changes: 1 addition & 1 deletion lux/view/View.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,6 @@ def load(self, ldf) -> View:
#TODO: handle case when user input vanilla Pandas dataframe
self.specLst = Parser.parse(self.specLst)
Validator.validateSpec(self.specLst,ldf)
vc = Compiler.compile(ldf,[self],enumerateCollection=False)
vc = Compiler.compile(ldf,ldf.context,[self],enumerateCollection=False)
PandasExecutor.execute(vc,ldf)
return vc[0]
52 changes: 48 additions & 4 deletions lux/view/ViewCollection.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from __future__ import annotations
from lux.vizLib.altair.AltairRenderer import AltairRenderer
from lux.utils.utils import checkImportLuxWidget
from typing import List, Union
from lux.view.View import View
from lux.context.Spec import Spec
class ViewCollection():
'''
ViewCollection is a list of View objects.
'''
def __init__(self,collection):
self.collection=collection

def __init__(self,inputLst:Union[List[View],List[Spec]]):
# Overloaded Constructor
if (type(inputLst[0])==View):
self.collection = inputLst
self.inputType = "View"
elif (type(inputLst[0])==Spec):
self.specLst = inputLst
self.collection = []
self.inputType = "Spec"
def __getitem__(self, key):
return self.collection[key]
def __setitem__(self, key, value):
Expand Down Expand Up @@ -135,4 +145,38 @@ def _repr_html_(self):
recommendations=recJSON,
context={}
)
display(widget)
display(widget)

def load(self, ldf) -> ViewCollection:
"""
Loading the data into the views in the ViewCollection by instantiating the specification and populating the view based on the data, effectively "materializing" the view.
Parameters
----------
ldf : LuxDataframe
Input Dataframe to be attached to the ViewCollection
Returns
-------
ViewCollection
Complete ViewCollection with fully-specified fields
See Also
--------
lux.view.View.load
"""
from lux.compiler.Parser import Parser
from lux.compiler.Validator import Validator
from lux.compiler.Compiler import Compiler
from lux.executor.PandasExecutor import PandasExecutor #TODO: temporary (generalize to executor)
if (self.inputType=="View"):
for view in self.collection:
view.specLst = Parser.parse(view.specLst)
Validator.validateSpec(view.specLst,ldf)
vc = Compiler.compile(ldf,ldf.context,self.collection,enumerateCollection=False)
elif (self.inputType=="Spec"):
self.specLst = Parser.parse(self.specLst)
Validator.validateSpec(self.specLst,ldf)
vc = Compiler.compile(ldf,self.specLst,self)
PandasExecutor.execute(vc,ldf)
return vc

0 comments on commit ef67512

Please sign in to comment.