Skip to content

Commit

Permalink
routing View to go through parser, validator, compiler, executor
Browse files Browse the repository at this point in the history
  • Loading branch information
dorisjlee committed Jun 8, 2020
1 parent 2d26715 commit 3b5aee3
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 79 deletions.
12 changes: 2 additions & 10 deletions lux/compiler/Compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,10 @@ def compile(ldf: LuxDataFrame, viewCollection: ViewCollection, enumerateCollecti
viewCollection: list[lux.View]
view collection with compiled lux.View objects.
"""
# 1. If the DataObj represent a collection, then compile it into a collection. Otherwise, return False
# Input: DataObj --> Output: DataObjCollection/False
if (enumerateCollection):
viewCollection = Compiler.enumerateCollection(ldf)
# else:
# dataObjCollection = False
# 2. For every DataObject in the DataObject Collection, expand underspecified
# Output : DataObj/DataObjectCollection
# compiledCollection = []
viewCollection = Compiler.expandUnderspecified(ldf, viewCollection) # autofill data type/model information

viewCollection = Compiler.removeAllInvalid(viewCollection)

viewCollection = Compiler.removeAllInvalid(viewCollection) # remove invalid views from collection
for view in viewCollection:
Compiler.determineEncoding(ldf, view) # autofill viz related information
return viewCollection
Expand Down Expand Up @@ -358,6 +349,7 @@ def enforceSpecifiedChannel(view: View, autoChannel: Dict[str,str]):

@staticmethod
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 Down
64 changes: 31 additions & 33 deletions lux/compiler/Parser.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
from lux.context.Spec import Spec
from lux.luxDataFrame.LuxDataframe import LuxDataFrame
from typing import List
class Parser:
"""
The parser takes in the user's input context specification,
The parser takes in the user's input specifications (with string `description` fields),
then generates the Lux internal specification through lux.Spec.
"""
@staticmethod
def parse(ldf: LuxDataFrame) -> None:
def parse(specs: List[Spec]) -> List[Spec]:
"""
Given the string description from the input Spec,
Given the string description from a list of input Specs (often context),
assign the appropriate spec.attribute, spec.filterOp, and spec.value.
Parameters
----------
ldf : lux.luxDataFrame.LuxDataFrame
LuxDataFrame with fully specified context consisting of lux.Spec objects.
specs : List[Spec]
Underspecified list of lux.Spec objects.
Returns
-------
None
Examples
--------
>>> ldf.context = ["Horsepower", "Origin=USA"]
>>> ldf.context
"""
List[Spec]
Parsed list of lux.Spec objects.
"""
import re
parsedContext = ldf.getContext()
# specs = ldf.getContext()
newContext = []
#checks for and converts users' string inputs into lux specifications
for s in parsedContext:
for s in specs:
validValues = []

if type(s) is list:
validValues = []
for v in s:
if type(v) is str and v in list(ldf.columns):
if type(v) is str: # and v in list(ldf.columns): #TODO: Move validation check to Validator
validValues.append(v)
tempSpec = Spec(attribute = validValues)
newContext.append(tempSpec)
Expand All @@ -47,42 +43,44 @@ def parse(ldf: LuxDataFrame) -> None:
if "|" in s:
values = s[eqInd+1:].split("|")
for v in values:
if v in ldf.uniqueValues[var]:
validValues.append(v)
# if v in ldf.uniqueValues[var]: #TODO: Move validation check to Validator
validValues.append(v)
else:
validValues = s[eqInd+1:]
if var in list(ldf.columns):
tempSpec = Spec(attribute = var, filterOp = "=", value = validValues)
newContext.append(tempSpec)
# if var in list(ldf.columns): #TODO: Move validation check to Validator
tempSpec = Spec(attribute = var, filterOp = "=", value = validValues)
newContext.append(tempSpec)
#case where user specifies a variable
else:
if "|" in s:
values = s.split("|")
for v in values:
if v in list(ldf.columns):
validValues.append(v)
# if v in list(ldf.columns): #TODO: Move validation check to Validator
validValues.append(v)
else:
validValues = s
tempSpec = Spec(attribute = validValues)
newContext.append(tempSpec)
elif type(s) is Spec:
newContext.append(s)
parsedContext = newContext
ldf.context = newContext
specs = newContext
# ldf.context = newContext

for spec in parsedContext:
for spec in specs:
if (spec.description):
if ((spec.description in list(ldf.columns)) or spec.description == "?"):# if spec.description in the list of attributes
spec.attribute = spec.description
elif any(ext in [">","<","="] for ext in spec.description): # spec.description contain ">","<". or "="
#TODO: Move validation check to Validator
#if ((spec.description in list(ldf.columns)) or spec.description == "?"):# if spec.description in the list of attributes
if any(ext in [">","<","="] for ext in spec.description): # spec.description contain ">","<". or "="
# then parse it and assign to spec.attribute, spec.filterOp, spec.values
spec.filterOp = re.findall(r'/.*/>|=|<|>=|<=|!=', spec.description)[0]
splitDescription = spec.description.split(spec.filterOp)
spec.attribute = splitDescription[0]
spec.value = splitDescription[1]
elif (type(spec.description) == str):
spec.attribute = spec.description
elif (type(spec.description)==list):
spec.attribute = spec.description
else: # then it is probably a value
spec.values = spec.description

ldf.context = parsedContext
# else: # then it is probably a value
# spec.values = spec.description
return specs
# ldf.context = specs
17 changes: 8 additions & 9 deletions lux/compiler/Validator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# from ..luxDataFrame.LuxDataframe import LuxDataFrame

from lux.luxDataFrame.LuxDataframe import LuxDataFrame
from lux.context.Spec import Spec
from typing import List
class Validator:
'''
Contains methods for validating lux.Spec objects in the context.
Expand All @@ -11,8 +13,7 @@ def __repr__(self):
return f"<Validator>"

@staticmethod
# def validateSpec(ldf: LuxDataFrame):
def validateSpec(ldf):
def validateSpec(specs: List[Spec], ldf:LuxDataFrame) -> None:
"""
Validates input specifications from the user to find inconsistencies and errors.
Expand All @@ -30,6 +31,9 @@ def validateSpec(ldf):
ValueError
Ensures no input specs are consistent with DataFrame.
"""
uniqueVals = ldf.uniqueValues
printWarning = False

def existsInDF(value,uniqueValues):
return any(value in uniqueValues[vals] for vals in uniqueValues)

Expand Down Expand Up @@ -58,19 +62,14 @@ def validateAttr(spec):

# 1. Parse all string specification into Spec objects (nice-to-have)
# 2. Validate that the parsed specification is corresponds to the content in the LuxDataframe.
context = ldf.getContext()
uniqueVals = ldf.uniqueValues
printWarning = False
for spec in context:
for spec in specs:
if type(spec) is list:
for s in spec:
validateAttr(s)
else:
validateAttr(spec)


if printWarning:
#print warning
raise ValueError("Input spec is inconsistent with DataFrame.")

# lux.setContext(lux.Spec(attr = "Horsepower"))
Expand Down
15 changes: 0 additions & 15 deletions lux/context/Context.py

This file was deleted.

3 changes: 1 addition & 2 deletions lux/context/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .Spec import Spec
from .Context import Context
from .Spec import Spec
6 changes: 3 additions & 3 deletions lux/luxDataFrame/LuxDataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ def _refreshContext(self):
if self.SQLconnection == "":
self.computeStats()
self.computeDatasetMetadata()
Parser.parse(self)
Validator.validateSpec(self)
self.context = Parser.parse(self.getContext())
Validator.validateSpec(self.context,self)
viewCollection = Compiler.compile(self,self.viewCollection)
self.setViewCollection(viewCollection)

Expand Down Expand Up @@ -215,7 +215,7 @@ def computeSQLStats(self):
for attribute in self.columns:
if self.dataTypeLookup[attribute] == 'quantitative':
self.xMinMax[attribute] = (min(self.uniqueValues[attribute]), max(self.uniqueValues[attribute]))
self.yMinMax[attribute] = (min(self.uniqueValues[attribute]), max(self.uniqueValues[dimension]))
self.yMinMax[attribute] = (min(self.uniqueValues[attribute]), max(self.uniqueValues[attribute]))

def getSQLAttributes(self):
if "." in self.table_name:
Expand Down
42 changes: 35 additions & 7 deletions lux/view/View.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
from __future__ import annotations
from lux.context.Spec import Spec
from lux.utils.utils import checkImportLuxWidget
class View:
'''
View Object represents a collection of fully fleshed out specifications required for data fetching and visualization.
'''

def __init__(self, specifiedSpecLst,mark="", title=""):
self.specLst = specifiedSpecLst
def __init__(self, specLst, mark="", title=""):
# self.specLst = Parser.parse(specLst) #TODO: make sure this is not duplicated for programmatically generated views
self.specLst = specLst
self.title = title
self.mark = mark
self.data = None
self.score = 0.0
self.vis = None
self.xMinMax = {}
self.yMinMax = {}

def __repr__(self):
x_channel = ""
y_channel = ""
filter_spec = None
channels, additional_channels = [], []
for spec in self.specLst:
Expand Down Expand Up @@ -120,9 +119,38 @@ def toVegaLite(self) -> dict:
"""
from lux.vizLib.altair.AltairRenderer import AltairRenderer
renderer = AltairRenderer(outputType="VegaLite")
self.vis= renderer.createVis(self)
self.vis = renderer.createVis(self)
return self.vis

def renderVSpec(self, renderer="altair"):
if (renderer == "altair"):
return self.toVegaLite()
return self.toVegaLite()

def load(self, ldf) -> View:
"""
Loading the data into the view 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 view
Returns
-------
View
Complete View with fully-specified fields
See Also
--------
lux.view.ViewCollection.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)
#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)
PandasExecutor.execute(vc,ldf)
return vc[0]

0 comments on commit 3b5aee3

Please sign in to comment.